VirtualBox

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

Last change on this file since 1008 was 1008, checked in by bird, 18 years ago

precompile pipe.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 45.4 KB
Line 
1/* $Id: kObjCache.c 1008 2007-06-02 16:22:27Z bird $ */
2/** @file
3 *
4 * kObjCache - Object Cache.
5 *
6 * Copyright (c) 2007 knut st. osmundsen <[email protected]>
7 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <string.h>
32#include <stdlib.h>
33#include <stdarg.h>
34#include <stdio.h>
35#include <errno.h>
36#include <assert.h>
37#include <sys/stat.h>
38#include <fcntl.h>
39#include <limits.h>
40#ifndef PATH_MAX
41# define PATH_MAX _MAX_PATH /* windows */
42#endif
43#if defined(__OS2__) || defined(__WIN__)
44# include <process.h>
45# include <io.h>
46# ifdef __OS2__
47# include <unistd.h>
48# endif
49#else
50# include <unistd.h>
51# include <sys/wait.h>
52# ifndef O_BINARY
53# define O_BINARY 0
54# endif
55#endif
56#include "crc32.h"
57#include "md5.h"
58
59/*******************************************************************************
60* Defined Constants And Macros *
61*******************************************************************************/
62/** The max line length in a cache file. */
63#define KOBJCACHE_MAX_LINE_LEN 16384
64#if defined(__WIN__)
65# define PATH_SLASH '\\'
66#else
67# define PATH_SLASH '/'
68#endif
69#if defined(__OS2__) || defined(__WIN__)
70# define IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
71# define IS_SLASH_DRV(ch) ((ch) == '/' || (ch) == '\\' || (ch) == ':')
72#else
73# define IS_SLASH(ch) ((ch) == '/')
74# define IS_SLASH_DRV(ch) ((ch) == '/')
75#endif
76/** Use pipe instead of temp files when possible (speed). */
77#define USE_PIPE 1
78
79
80
81/*******************************************************************************
82* Structures and Typedefs *
83*******************************************************************************/
84/** A checksum list entry.
85 * We keep a list checksums (of precompiler output) that matches, The planned
86 * matching algorithm doesn't require the precompiler output to be indentical,
87 * only to produce the same object files.
88 */
89typedef struct KOCSUM
90{
91 /** The next checksum. */
92 struct KOCSUM *pNext;
93 /** The crc32 checksum. */
94 uint32_t crc32;
95 /** The MD5 digest. */
96 unsigned char md5[16];
97} KOCSUM, *PKOCSUM;
98/** Pointer to a const KOCSUM. */
99typedef const KOCSUM *PCKOCSUM;
100
101/**
102 * The object cache data.
103 */
104typedef struct KOBJCACHE
105{
106 /** The cache dir that all other names are relative to. */
107 char *pszDir;
108 /** The name of the cache file. */
109 const char *pszName;
110 /** Set if the object needs to be (re)compiled. */
111 unsigned fNeedCompiling;
112 /** Whether the precompiler runs in piped mode. If clear it's file
113 * mode (it could be redirected stdout, but that's essentially the
114 * same from our point of view). */
115 unsigned fPiped;
116
117 /** The name of new precompiled output. */
118 const char *pszNewCppName;
119 /** Pointer to the 'mapping' of the new precompiled output. */
120 char *pszNewCppMapping;
121 /** The size of the new precompiled output 'mapping'. */
122 size_t cbNewCpp;
123 /** The new checksum. */
124 KOCSUM NewSum;
125 /** The new object filename (relative to the cache file). */
126 char *pszNewObjName;
127
128 /** The name of the precompiled output. (relative to the cache file) */
129 char *pszOldCppName;
130 /** Pointer to the 'mapping' of the old precompiled output. */
131 char *pszOldCppMapping;
132 /** The size of the old precompiled output. */
133 size_t cbOldCpp;
134
135 /** The head of the checksum list. */
136 KOCSUM SumHead;
137 /** The object filename (relative to the cache file). */
138 char *pszObjName;
139 /** The compile argument vector used to build the object. */
140 char **papszArgvCompile;
141 /** The size of the compile */
142 unsigned cArgvCompile;
143} KOBJCACHE, *PKOBJCACHE;
144/** Pointer to a const KOBJCACHE. */
145typedef const KOBJCACHE *PCKOBJCACHE;
146
147
148/*******************************************************************************
149* Global Variables *
150*******************************************************************************/
151/** Whether verbose output is enabled. */
152static int g_fVerbose = 0;
153
154
155/*******************************************************************************
156* Internal Functions *
157*******************************************************************************/
158static const char *FindFilenameInPath(const char *pszPath);
159static char *AbsPath(const char *pszPath);
160static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);
161static char *CalcRelativeName(const char *pszPath, const char *pszDir);
162static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode);
163static int UnlinkFileInDir(const char *pszName, const char *pszDir);
164static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir);
165static int DoesFileInDirExist(const char *pszName, const char *pszDir);
166static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);
167static void *xmalloc(size_t);
168static void *xrealloc(void *, size_t);
169static char *xstrdup(const char *);
170
171
172/**
173 * Compares two check sum entries.
174 *
175 * @returns 1 if equal, 0 if not equal.
176 *
177 * @param pSum1 The first checksum.
178 * @param pSum2 The second checksum.
179 */
180static int kObjCacheSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)
181{
182 if (pSum1 == pSum2)
183 return 1;
184 if (!pSum1 || !pSum2)
185 return 0;
186 if (pSum1->crc32 != pSum2->crc32)
187 return 0;
188 if (memcmp(&pSum1->md5[0], &pSum2->md5[0], sizeof(pSum1->md5)))
189 return 0;
190 return 1;
191}
192
193
194/**
195 * Print a fatal error message and exit with rc=1.
196 *
197 * @param pEntry The cache entry.
198 * @param pszFormat The message to print.
199 * @param ... Format arguments.
200 */
201static void kObjCacheFatal(PCKOBJCACHE pEntry, const char *pszFormat, ...)
202{
203 va_list va;
204
205 fprintf(stderr, "kObjCache %s - fatal error: ", pEntry->pszName);
206 va_start(va, pszFormat);
207 vfprintf(stderr, pszFormat, va);
208 va_end(va);
209
210 exit(1);
211}
212
213
214/**
215 * Print a verbose message if verbosisty is enabled.
216 *
217 * @param pEntry The cache entry.
218 * @param pszFormat The message to print.
219 * @param ... Format arguments.
220 */
221static void kObjCacheVerbose(PCKOBJCACHE pEntry, const char *pszFormat, ...)
222{
223 if (g_fVerbose)
224 {
225 va_list va;
226
227 fprintf(stdout, "kObjCache %s - info: ", pEntry->pszName);
228 va_start(va, pszFormat);
229 vfprintf(stdout, pszFormat, va);
230 va_end(va);
231 }
232}
233
234
235/**
236 * Creates a cache entry for the given cache file name.
237 *
238 * @returns Pointer to a cache entry.
239 * @param pszFilename The cache file name.
240 */
241static PKOBJCACHE kObjCacheCreate(const char *pszFilename)
242{
243 PKOBJCACHE pEntry;
244
245 /*
246 * Allocate an empty entry.
247 */
248 pEntry = xmalloc(sizeof(*pEntry));
249 memset(pEntry, 0, sizeof(*pEntry));
250
251 /*
252 * Setup the directory and cache file name.
253 */
254 pEntry->pszDir = AbsPath(pszFilename);
255 pEntry->pszName = FindFilenameInPath(pEntry->pszDir);
256 if (pEntry->pszDir == pEntry->pszName)
257 kObjCacheFatal(pEntry, "Failed to find abs path for '%s'!\n", pszFilename);
258 ((char *)pEntry->pszName)[-1] = '\0';
259
260 return pEntry;
261}
262
263
264#if 0 /* don't bother. */
265/**
266 * Destroys the cache entry freeing up all it's resources.
267 *
268 * @param pEntry The entry to free.
269 */
270static void kObjCacheDestroy(PKOBJCACHE pEntry)
271{
272 free(pEntry->pszDir);
273 free(pEntry->pszName);
274 while (pEntry->SumHead.pNext)
275 {
276 void *pv = pEntry->SumHead.pNext;
277 pEntry->SumHead.pNext = pEntry->SumHead.pNext->pNext;
278 if (pv != &pEntry->NewSum)
279 free(pv);
280 }
281 free(pEntry);
282}
283#endif
284
285
286/**
287 * Reads and parses the cache file.
288 *
289 * @param pEntry The entry to read it into.
290 */
291static void kObjCacheRead(PKOBJCACHE pEntry)
292{
293 static char s_szLine[KOBJCACHE_MAX_LINE_LEN + 16];
294 FILE *pFile;
295 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb");
296 if (pFile)
297 {
298 kObjCacheVerbose(pEntry, "reading cache file...\n");
299
300 /*
301 * Check the magic.
302 */
303 if ( !fgets(s_szLine, sizeof(s_szLine), pFile)
304 || strcmp(s_szLine, "magic=kObjCache-1\n"))
305 {
306 kObjCacheVerbose(pEntry, "bad cache file (magic)\n");
307 pEntry->fNeedCompiling = 1;
308 }
309 else
310 {
311 /*
312 * Parse the rest of the file (relaxed order).
313 */
314 unsigned i;
315 int fBad = 0;
316 int fBadBeforeMissing;
317 int fFirstSum = 1;
318 while (fgets(s_szLine, sizeof(s_szLine), pFile))
319 {
320 /* Split the line and drop the trailing newline. */
321 char *pszNl = strchr(s_szLine, '\n');
322 char *pszVal = strchr(s_szLine, '=');
323 if ((fBad = pszVal == NULL))
324 break;
325 if (pszNl)
326 *pszNl = '\0';
327 *pszVal++ = '\0';
328
329 /* string case on variable name */
330 if (!strcmp(s_szLine, "obj"))
331 {
332 if ((fBad = pEntry->pszObjName != NULL))
333 break;
334 pEntry->pszObjName = xstrdup(pszVal);
335 }
336 else if (!strcmp(s_szLine, "cpp"))
337 {
338 if ((fBad = pEntry->pszOldCppName != NULL))
339 break;
340 pEntry->pszOldCppName = xstrdup(pszVal);
341 }
342 else if (!strcmp(s_szLine, "cpp-size"))
343 {
344 char *pszNext;
345 if ((fBad = pEntry->cbOldCpp != 0))
346 break;
347 pEntry->cbOldCpp = strtoul(pszVal, &pszNext, 0);
348 if ((fBad = pszNext && *pszNext))
349 break;
350 }
351 else if (!strcmp(s_szLine, "cc-argc"))
352 {
353 if ((fBad = pEntry->papszArgvCompile != NULL))
354 break;
355 pEntry->cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */
356 pEntry->papszArgvCompile = xmalloc((pEntry->cArgvCompile + 1) * sizeof(pEntry->papszArgvCompile[0]));
357 memset(pEntry->papszArgvCompile, 0, (pEntry->cArgvCompile + 1) * sizeof(pEntry->papszArgvCompile[0]));
358 }
359 else if (!strncmp(s_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1))
360 {
361 char *pszNext;
362 unsigned i = strtoul(&s_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0);
363 if ((fBad = i >= pEntry->cArgvCompile || pEntry->papszArgvCompile[i] || (pszNext && *pszNext)))
364 break;
365 pEntry->papszArgvCompile[i] = xstrdup(pszVal);
366 }
367 else if (!strcmp(s_szLine, "sum"))
368 {
369 KOCSUM Sum;
370 unsigned i;
371 char *pszNext;
372 char *pszMD5 = strchr(pszVal, ':');
373 if ((fBad = pszMD5 == NULL))
374 break;
375 *pszMD5++ = '\0';
376
377 /* crc32 */
378 Sum.crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16);
379 if ((fBad = (pszNext && *pszNext)))
380 break;
381
382 /* md5 */
383 for (i = 0; i < sizeof(Sum.md5) * 2; i++)
384 {
385 unsigned char ch = pszMD5[i];
386 int x;
387 if (ch - '0' <= 9)
388 x = ch - '0';
389 else if (ch - 'a' <= 5)
390 x = ch - 'a' + 10;
391 else if (ch - 'A' <= 5)
392 x = ch - 'A' + 10;
393 else
394 {
395 fBad = 1;
396 break;
397 }
398 if (!(i & 1))
399 Sum.md5[i >> 1] = x << 4;
400 else
401 Sum.md5[i >> 1] |= x;
402 }
403 if (fBad)
404 break;
405
406 if (fFirstSum)
407 {
408 pEntry->SumHead = Sum;
409 pEntry->SumHead.pNext = NULL;
410 fFirstSum = 0;
411 }
412 else
413 {
414 Sum.pNext = pEntry->SumHead.pNext;
415 pEntry->SumHead.pNext = xmalloc(sizeof(Sum));
416 *pEntry->SumHead.pNext = Sum;
417 }
418 }
419 else
420 {
421 fBad = 1;
422 break;
423 }
424 } /* parse loop */
425
426 /*
427 * Did we find everything?
428 */
429 fBadBeforeMissing = fBad;
430 if ( !fBad
431 && ( !pEntry->papszArgvCompile
432 || !pEntry->pszObjName
433 || !pEntry->pszOldCppName
434 || fFirstSum))
435 fBad = 1;
436 if (!fBad)
437 for (i = 0; i < pEntry->cArgvCompile; i++)
438 if ((fBad = !pEntry->papszArgvCompile[i]))
439 break;
440 if (fBad)
441 kObjCacheVerbose(pEntry, "bad cache file (%s)\n", fBadBeforeMissing ? s_szLine : "missing stuff");
442 else if (ferror(pFile))
443 kObjCacheVerbose(pEntry, "cache file read error\n");
444 pEntry->fNeedCompiling = fBad;
445 }
446 fclose(pFile);
447 }
448 else
449 {
450 kObjCacheVerbose(pEntry, "no cache file\n");
451 pEntry->fNeedCompiling = 1;
452 }
453}
454
455
456/**
457 * Writes the cache file.
458 *
459 * @param pEntry The entry to write.
460 */
461static void kObjCacheWrite(PKOBJCACHE pEntry)
462{
463 FILE *pFile;
464 PCKOCSUM pSum;
465 unsigned i;
466
467 kObjCacheVerbose(pEntry, "writing cache file...\n");
468 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb");
469 if (!pFile)
470 kObjCacheFatal(pEntry, "Failed to open '%s' in '%s': %s\n",
471 pEntry->pszName, pEntry->pszDir, strerror(errno));
472
473#define CHECK_LEN(expr) \
474 do { int cch = expr; if (cch >= KOBJCACHE_MAX_LINE_LEN) kObjCacheFatal(pEntry, "Line too long: %d (max %d)\nexpr: %s\n", cch, KOBJCACHE_MAX_LINE_LEN, #expr); } while (0)
475
476 fprintf(pFile, "magic=kObjCache-1\n");
477 CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->pszNewObjName ? pEntry->pszNewObjName : pEntry->pszObjName));
478 CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->pszNewCppName ? pEntry->pszNewCppName : pEntry->pszOldCppName));
479 CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->pszNewCppName ? pEntry->cbNewCpp : pEntry->cbOldCpp));
480 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->cArgvCompile));
481 for (i = 0; i < pEntry->cArgvCompile; i++)
482 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->papszArgvCompile[i]));
483 for (pSum = pEntry->fNeedCompiling ? &pEntry->NewSum : &pEntry->SumHead;
484 pSum;
485 pSum = pSum->pNext)
486 fprintf(pFile, "sum=%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
487 pSum->crc32,
488 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
489 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
490 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
491 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
492
493 if ( fflush(pFile) < 0
494 || ferror(pFile))
495 {
496 int iErr = errno;
497 fclose(pFile);
498 UnlinkFileInDir(pEntry->pszName, pEntry->pszDir);
499 kObjCacheFatal(pEntry, "Stream error occured while writing '%s' in '%s': %d (?)\n",
500 pEntry->pszName, pEntry->pszDir, strerror(iErr));
501 }
502 fclose(pFile);
503}
504
505
506/**
507 * Spawns a child in a synchronous fashion.
508 * Terminating on failure.
509 *
510 * @param papszArgv Argument vector. The cArgv element is NULL.
511 * @param cArgv The number of arguments in the vector.
512 */
513static void kObjCacheSpawn(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, const char *pszStdOut)
514{
515#if defined(__OS2__) || defined(__WIN__)
516 intptr_t rc;
517 int fdStdOut = -1;
518 if (pszStdOut)
519 {
520 int fdReDir;
521 fdStdOut = dup(1); /* dup2(1,-1) doesn't work right on windows */
522 close(1);
523 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0777);
524 if (fdReDir < 0)
525 kObjCacheFatal(pEntry, "%s - failed to create stdout redirection file '%s': %s\n",
526 pszMsg, pszStdOut, strerror(errno));
527
528 if (fdReDir != 1)
529 {
530 if (dup2(fdReDir, 1) < 0)
531 kObjCacheFatal(pEntry, "%s - dup2 failed: %s\n", pszMsg, strerror(errno));
532 close(fdReDir);
533 }
534 }
535
536 errno = 0;
537 rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
538 if (rc < 0)
539 kObjCacheFatal(pEntry, "%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
540 if (rc > 0)
541 kObjCacheFatal(pEntry, "%s - failed rc=%d\n", pszMsg, (int)rc);
542 if (fdStdOut)
543 {
544 close(1);
545 fdStdOut = dup2(fdStdOut, 1);
546 close(fdStdOut);
547 }
548
549#else
550 int iStatus;
551 pid_t pidWait;
552 pid_t pid = fork();
553 if (!pid)
554 {
555 if (pszStdOut)
556 {
557 close(1);
558 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0777);
559 if (fdReDir < 0)
560 kObjCacheFatal(pEntry, "%s - failed to create stdout redirection file '%s': %s\n",
561 pszMsg, pszStdOut, strerror(errno));
562 if (fdReDir != 1)
563 {
564 if (dup2(fdReDir, 1) < 0)
565 kObjCacheFatal(pEntry, "%s - dup2 failed: %s\n", pszMsg, strerror(errno));
566 close(fdReDir);
567 }
568 }
569
570 execvp(papszArgv[0], papszArgv);
571 kObjCacheFatal(pEntry, "%s - execvp failed rc=%d errno=%d %s\n",
572 pszMsg, rc, errno, strerror(errno));
573 }
574 if (pid == -1)
575 kObjCacheFatal(pEntry, "%s - fork() failed: %s\n", pszMsg, strerror(errno));
576
577 pidWait = waitpid(pid, &iStatus);
578 while (pidWait < 0 && errno == EINTR)
579 pidWait = waitpid(pid, &iStatus);
580 if (pidWait != pid)
581 kObjCacheFatal(pEntry, "%s - waitpid failed rc=%d: %s\n",
582 pszMsg, pidWait, strerror(errno));
583 if (!WIFEXITED(iStatus))
584 kObjCacheFatal(pEntry, "%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
585 if (WEXITSTATUS(iStatus))
586 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
587#endif
588 (void)cArgv;
589}
590
591
592#ifdef USE_PIPE
593/**
594 * Spawns a child in a synchronous fashion.
595 * Terminating on failure.
596 *
597 * @param papszArgv Argument vector. The cArgv element is NULL.
598 * @param cArgv The number of arguments in the vector.
599 */
600static void kObjCacheSpawnPipe(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, char **ppszOutput, size_t *pcchOutput)
601{
602 int fds[2];
603 int iStatus;
604#if defined(__WIN__)
605 intptr_t pid, pidWait;
606#else
607 pid_t pid, pidWait;
608#endif
609 int fdStdOut;
610 size_t cbAlloc;
611 size_t cbLeft;
612 char *psz;
613
614 /*
615 * Setup the pipe.
616 */
617#if defined(__WIN__)
618 if (_pipe(fds, 0, _O_NOINHERIT | _O_BINARY) < 0)
619#else
620 if (pipe(fds) < 0)
621#endif
622 kObjCacheFatal(pEntry, "pipe failed: %s\n", strerror(errno));
623 fdStdOut = dup(1);
624 if (dup2(fds[1 /* write */], 1) < 0)
625 kObjCacheFatal(pEntry, "dup2(,1) failed: %s\n", strerror(errno));
626 close(fds[1]);
627 fds[1] = -1;
628#ifndef __WIN__
629 fcntl(fds[0], F_SETFD, FD_CLOEXEC);
630 fcntl(fdStdOut, F_SETFD, FD_CLOEXEC);
631#endif
632
633 /*
634 * Create the child process.
635 */
636#if defined(__OS2__) || defined(__WIN__)
637 errno = 0;
638 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
639 if (pid == -1)
640 kObjCacheFatal(pEntry, "%s - _spawnvp failed: %s\n", pszMsg, strerror(errno));
641
642#else
643 pid = fork();
644 if (!pid)
645 {
646 execvp(papszArgv[0], papszArgv);
647 kObjCacheFatal(pEntry, "%s - execvp failed rc=%d errno=%d %s\n",
648 pszMsg, rc, errno, strerror(errno));
649 }
650 if (pid == -1)
651 kObjCacheFatal(pEntry, "%s - fork() failed: %s\n", pszMsg, strerror(errno));
652#endif
653
654 /*
655 * Restore stdout.
656 */
657 close(1);
658 fdStdOut = dup2(fdStdOut, 1);
659
660 /*
661 * Read data from the child.
662 */
663 cbAlloc = pEntry->cbOldCpp ? (pEntry->cbOldCpp + 4*1024*1024) & ~(4*1024*1024 - 1) : 4*1024*1024;
664 cbLeft = cbAlloc;
665 *ppszOutput = psz = xmalloc(cbAlloc);
666 for (;;)
667 {
668 long cbRead = _read(fds[0], psz, cbLeft);
669 if (!cbRead)
670 break;
671 if (cbRead < 0 && errno != EINTR)
672 kObjCacheFatal(pEntry, "%s - read(%d,,%ld) failed: %s\n", pszMsg, fds[0], (long)cbLeft, strerror(errno));
673 psz += cbRead;
674 cbLeft -= cbRead;
675
676 /* expand the buffer? */
677 if (!cbLeft)
678 {
679 size_t off = psz - *ppszOutput;
680 assert(off == cbAlloc);
681 cbLeft = 4*1024*1024;
682 cbAlloc += cbLeft;
683 *ppszOutput = xrealloc(*ppszOutput, cbAlloc);
684 psz = *ppszOutput + off;
685 }
686 }
687 close(fds[0]);
688 *pcchOutput = cbAlloc - cbLeft;
689
690 /*
691 * Reap the child.
692 */
693#ifdef __WIN__
694 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
695 if (pidWait == -1)
696 kObjCacheFatal(pEntry, "%s - waitpid failed: %s\n",
697 pszMsg, strerror(errno));
698 if (iStatus)
699 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, iStatus);
700#else
701 pidWait = waitpid(pid, &iStatus);
702 while (pidWait < 0 && errno == EINTR)
703 pidWait = waitpid(pid, &iStatus);
704 if (pidWait != pid)
705 kObjCacheFatal(pEntry, "%s - waitpid failed rc=%d: %s\n",
706 pszMsg, pidWait, strerror(errno));
707 if (!WIFEXITED(iStatus))
708 kObjCacheFatal(pEntry, "%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
709 if (WEXITSTATUS(iStatus))
710 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
711#endif
712 (void)cArgv;
713}
714#endif /* USE_PIPE */
715
716
717/**
718 * Reads the (new) output of the precompiler.
719 *
720 * Not used when using pipes.
721 *
722 * @param pEntry The cache entry. cbNewCpp and pszNewCppMapping will be updated.
723 */
724static void kObjCacheReadPrecompileOutput(PKOBJCACHE pEntry)
725{
726 pEntry->pszNewCppMapping = ReadFileInDir(pEntry->pszNewCppName, pEntry->pszDir, &pEntry->cbNewCpp);
727 if (!pEntry->pszNewCppMapping)
728 kObjCacheFatal(pEntry, "failed to open/read '%s' in '%s': %s\n",
729 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno));
730 kObjCacheVerbose(pEntry, "precompiled file is %lu bytes long\n", (unsigned long)pEntry->cbNewCpp);
731}
732
733
734/**
735 * Worker for kObjCachePreCompile and calculates the checksum of
736 * the precompiler output.
737 *
738 * @param pEntry The cache entry. NewSum will be updated.
739 */
740static void kObjCacheCalcChecksum(PKOBJCACHE pEntry)
741{
742 struct MD5Context MD5Ctx;
743
744 memset(&pEntry->NewSum, 0, sizeof(pEntry->NewSum));
745 pEntry->NewSum.crc32 = crc32(0, pEntry->pszNewCppMapping, pEntry->cbNewCpp);
746 MD5Init(&MD5Ctx);
747 MD5Update(&MD5Ctx, pEntry->pszNewCppMapping, pEntry->cbNewCpp);
748 MD5Final(&pEntry->NewSum.md5[0], &MD5Ctx);
749 kObjCacheVerbose(pEntry, "crc32=%#lx md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
750 pEntry->NewSum.crc32,
751 pEntry->NewSum.md5[0], pEntry->NewSum.md5[1], pEntry->NewSum.md5[2], pEntry->NewSum.md5[3],
752 pEntry->NewSum.md5[4], pEntry->NewSum.md5[5], pEntry->NewSum.md5[6], pEntry->NewSum.md5[7],
753 pEntry->NewSum.md5[8], pEntry->NewSum.md5[9], pEntry->NewSum.md5[10], pEntry->NewSum.md5[11],
754 pEntry->NewSum.md5[12], pEntry->NewSum.md5[13], pEntry->NewSum.md5[14], pEntry->NewSum.md5[15]);
755
756}
757
758
759/**
760 * Run the precompiler and calculate the checksum of the output.
761 *
762 * @param pEntry The cache entry.
763 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL.
764 * @param cArgvPreComp The number of arguments.
765 * @param pszPreCompName Precompile output name. (must kick around)
766 * @param fRedirStdOut Whether stdout needs to be redirected or not.
767 */
768static void kObjCachePreCompile(PKOBJCACHE pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp, const char *pszPreCompName, int fRedirStdOut)
769{
770#ifdef USE_PIPE
771 /*
772 * Flag it as piped or non-piped.
773 */
774 if (fRedirStdOut)
775 pEntry->fPiped = 1;
776 else
777#endif
778 pEntry->fPiped = 0;
779
780 /*
781 * Rename the old precompiled output to '-old'.
782 * We'll discard the old output and keep the new output, but because
783 * we might with to do a quick matchup later we can't remove it just now.
784 * If we're using the pipe strategy, we will not do any renaming.
785 */
786 if ( pEntry->pszOldCppName
787 && !pEntry->fPiped
788 && DoesFileInDirExist(pEntry->pszOldCppName, pEntry->pszDir))
789 {
790 size_t cch = strlen(pEntry->pszOldCppName);
791 char *psz = xmalloc(cch + sizeof("-old"));
792 memcpy(psz, pEntry->pszOldCppName, cch);
793 memcpy(psz + cch, "-old", sizeof("-old"));
794
795 kObjCacheVerbose(pEntry, "renaming '%s' to '%s' in '%s'\n", pEntry->pszOldCppName, psz, pEntry->pszDir);
796 UnlinkFileInDir(psz, pEntry->pszDir);
797 if (RenameFileInDir(pEntry->pszOldCppName, psz, pEntry->pszDir))
798 kObjCacheFatal(pEntry, "failed to rename '%s' -> '%s' in '%s': %s\n",
799 pEntry->pszOldCppName, psz, pEntry->pszDir, strerror(errno));
800 free(pEntry->pszOldCppName);
801 pEntry->pszOldCppName = psz;
802 }
803 pEntry->pszNewCppName = CalcRelativeName(pszPreCompName, pEntry->pszDir);
804
805 /*
806 * Precompile it and calculate the checksum on the output.
807 */
808 kObjCacheVerbose(pEntry, "precompiling -> '%s'...\n", pEntry->pszNewCppName);
809#ifdef USE_PIPE
810 if (pEntry->fPiped)
811 kObjCacheSpawnPipe(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", &pEntry->pszNewCppMapping, &pEntry->cbNewCpp);
812 else
813#endif
814 {
815 if (fRedirStdOut)
816 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", pszPreCompName);
817 else
818 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL);
819 kObjCacheReadPrecompileOutput(pEntry);
820 }
821 kObjCacheCalcChecksum(pEntry);
822}
823
824
825/**
826 * Worker for kObjCacheCompileIfNeeded that compares the
827 * precompiled output.
828 *
829 * @returns 1 if matching, 0 if not matching.
830 * @param pEntry The entry containing the names of the files to compare.
831 * The entry is not updated in any way.
832 */
833static int kObjCacheCompareOldAndNewOutput(PCKOBJCACHE pEntry)
834{
835 /** @todo do some quick but fancy comparing that determins whether code
836 * has actually changed or moved. We can ignore declarations and typedefs that
837 * has just been moved up/down a bit. The typical case is adding a new error
838 * #define that doesn't influence the current compile job. */
839 return 0;
840}
841
842
843/**
844 * Worker for kObjCacheCompileIfNeeded that does the actual (re)compilation.
845 *
846 * @returns 1 if matching, 0 if not matching.
847 * @param pEntry The cache entry.
848 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL.
849 * @param cArgvCompile The number of arguments in the vector.
850 * @param pszObjName The name of the object file.
851 */
852static void kObjCacheCompileIt(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName)
853{
854 /*
855 * Delete the old object file and precompiler output.
856 */
857 if (pEntry->pszObjName)
858 {
859 UnlinkFileInDir(pEntry->pszObjName, pEntry->pszDir);
860 pEntry->pszObjName = NULL;
861 }
862 pEntry->pszNewObjName = CalcRelativeName(pszObjName, pEntry->pszDir);
863
864 /*
865 * If we executed the precompiled in piped mode we'll have to write the
866 * precompiler output to disk so the compile has some thing to chew on.
867 */
868 if (pEntry->fPiped)
869 {
870 FILE *pFile = FOpenFileInDir(pEntry->pszNewCppName, pEntry->pszDir, "wb");
871 if (!pFile)
872 kObjCacheFatal(pEntry, "failed to create file '%s' in '%s': %s\n",
873 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno));
874 if (fwrite(pEntry->pszNewCppMapping, pEntry->cbNewCpp, 1, pFile) != 1)
875 kObjCacheFatal(pEntry, "fwrite failed: %s\n", strerror(errno));
876 if (fclose(pFile))
877 kObjCacheFatal(pEntry, "fclose failed: %s\n", strerror(errno));
878 }
879
880 /*
881 * Release buffers we no longer need before starting the compile.
882 */
883 free(pEntry->pszNewCppMapping);
884 pEntry->pszNewCppMapping = NULL;
885 free(pEntry->pszOldCppMapping);
886 pEntry->pszOldCppMapping = NULL;
887
888 /*
889 * Do the recompilation.
890 */
891 kObjCacheVerbose(pEntry, "compiling -> '%s'...\n", pEntry->pszNewObjName);
892 pEntry->papszArgvCompile = (char **)papszArgvCompile; /* LEAK */
893 pEntry->cArgvCompile = cArgvCompile;
894 kObjCacheSpawn(pEntry, papszArgvCompile, cArgvCompile, "compile", NULL);
895}
896
897
898/**
899 * Check if (re-)compilation is required and do it.
900 *
901 * @returns 1 if matching, 0 if not matching.
902 * @param pEntry The cache entry.
903 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL.
904 * @param cArgvCompile The number of arguments in the vector.
905 * @param pszObjName The name of the object file.
906 */
907static void kObjCacheCompileIfNeeded(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName)
908{
909 /*
910 * Does the object name differ?
911 */
912 if (!pEntry->fNeedCompiling)
913 {
914 char *pszTmp = CalcRelativeName(pszObjName, pEntry->pszDir);
915 if (strcmp(pEntry->pszObjName, pszTmp))
916 {
917 pEntry->fNeedCompiling = 1;
918 kObjCacheVerbose(pEntry, "object name changed '%s' -> '%s'\n", pEntry->pszObjName, pszTmp);
919 }
920 free(pszTmp);
921 }
922
923 /*
924 * Does the compile command differ?
925 * TODO: Ignore irrelevant options here (like warning level).
926 */
927 if ( !pEntry->fNeedCompiling
928 && pEntry->cArgvCompile != cArgvCompile)
929 {
930 pEntry->fNeedCompiling = 1;
931 kObjCacheVerbose(pEntry, "compile argument count changed\n");
932 }
933 if (!pEntry->fNeedCompiling)
934 {
935 unsigned i;
936 for (i = 0; i < cArgvCompile; i++)
937 if (strcmp(papszArgvCompile[i], pEntry->papszArgvCompile[i]))
938 {
939 pEntry->fNeedCompiling = 1;
940 kObjCacheVerbose(pEntry, "compile argument differs (%#d)\n", i);
941 break;
942 }
943 }
944
945 /*
946 * Does the object file exist?
947 */
948 if ( !pEntry->fNeedCompiling
949 && !DoesFileInDirExist(pEntry->pszObjName, pEntry->pszDir))
950 {
951 pEntry->fNeedCompiling = 1;
952 kObjCacheVerbose(pEntry, "object file doesn't exist\n");
953 }
954
955 /*
956 * Does the precompiled output differ in any significant way?
957 */
958 if (!pEntry->fNeedCompiling)
959 {
960 int fFound = 0;
961 PCKOCSUM pSum;
962 for (pSum = &pEntry->SumHead; pSum; pSum = pSum->pNext)
963 if (kObjCacheSumIsEqual(pSum, &pEntry->NewSum))
964 {
965 fFound = 1;
966 break;
967 }
968 if (!fFound)
969 {
970 kObjCacheVerbose(pEntry, "no checksum match - comparing output\n");
971 if (!kObjCacheCompareOldAndNewOutput(pEntry))
972 pEntry->fNeedCompiling = 1;
973 else
974 {
975 /* insert the sum into the list. */
976 pEntry->NewSum.pNext = pEntry->SumHead.pNext;
977 pEntry->SumHead.pNext = &pEntry->NewSum;
978 }
979 }
980 }
981
982 /*
983 * Discard the old precompiled output it's no longer needed.s
984 */
985 if (pEntry->pszOldCppName)
986 {
987 UnlinkFileInDir(pEntry->pszOldCppName, pEntry->pszDir);
988 free(pEntry->pszOldCppName);
989 pEntry->pszOldCppName = NULL;
990 }
991
992 /*
993 * Do the compliation if found necessary.
994 */
995 if (pEntry->fNeedCompiling)
996 kObjCacheCompileIt(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
997}
998
999
1000/**
1001 * Gets the absolute path
1002 *
1003 * @returns A new heap buffer containing the absolute path.
1004 * @param pszPath The path to make absolute. (Readonly)
1005 */
1006static char *AbsPath(const char *pszPath)
1007{
1008 char szTmp[PATH_MAX];
1009#if defined(__OS2__) || defined(__WIN__)
1010 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))
1011 return xstrdup(pszPath);
1012#else
1013 if (!realpath(pszPath, szTmp))
1014 return xstrdup(pszPath);
1015#endif
1016 return xstrdup(szTmp);
1017}
1018
1019
1020/**
1021 * Utility function that finds the filename part in a path.
1022 *
1023 * @returns Pointer to the file name part (this may be "").
1024 * @param pszPath The path to parse.
1025 */
1026static const char *FindFilenameInPath(const char *pszPath)
1027{
1028 const char *pszFilename = strchr(pszPath, '\0') - 1;
1029 while ( pszFilename > pszPath
1030 && !IS_SLASH_DRV(pszFilename[-1]))
1031 pszFilename--;
1032 return pszFilename;
1033}
1034
1035
1036/**
1037 * Utility function that combines a filename and a directory into a path.
1038 *
1039 * @returns malloced buffer containing the result.
1040 * @param pszName The file name.
1041 * @param pszDir The directory path.
1042 */
1043static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)
1044{
1045 size_t cchName = strlen(pszName);
1046 size_t cchDir = strlen(pszDir);
1047 char *pszBuf = xmalloc(cchName + cchDir + 2);
1048 memcpy(pszBuf, pszDir, cchDir);
1049 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1]))
1050 pszBuf[cchDir++] = PATH_SLASH;
1051 memcpy(pszBuf + cchDir, pszName, cchName + 1);
1052 return pszBuf;
1053}
1054
1055
1056/**
1057 * Compares two path strings to see if they are identical.
1058 *
1059 * This doesn't do anything fancy, just the case ignoring and
1060 * slash unification.
1061 *
1062 * @returns 1 if equal, 0 otherwise.
1063 * @param pszPath1 The first path.
1064 * @param pszPath2 The second path.
1065 * @param cch The number of characters to compare.
1066 */
1067static int ArePathsIdentical(const char *pszPath1, const char *pszPath2, size_t cch)
1068{
1069#if defined(__OS2__) || defined(__WIN__)
1070 if (strnicmp(pszPath1, pszPath2, cch))
1071 {
1072 /* Slashes may differ, compare char by char. */
1073 const char *psz1 = pszPath1;
1074 const char *psz2 = pszPath2;
1075 for (;cch; psz1++, psz2++, cch--)
1076 {
1077 if (*psz1 != *psz2)
1078 {
1079 if ( tolower(*psz1) != tolower(*psz2)
1080 && toupper(*psz1) != toupper(*psz2)
1081 && *psz1 != '/'
1082 && *psz1 != '\\'
1083 && *psz2 != '/'
1084 && *psz2 != '\\')
1085 return 0;
1086 }
1087 }
1088 }
1089 return 1;
1090#else
1091 return !strncmp(pszPath1, pszPath2, cch);
1092#endif
1093}
1094
1095
1096/**
1097 * Calculate how to get to pszPath from pszDir.
1098 *
1099 * @returns The relative path from pszDir to path pszPath.
1100 * @param pszPath The path to the object.
1101 * @param pszDir The directory it shall be relative to.
1102 */
1103static char *CalcRelativeName(const char *pszPath, const char *pszDir)
1104{
1105 char *pszRet = NULL;
1106 char *pszAbsPath = NULL;
1107 size_t cchDir = strlen(pszDir);
1108
1109 /*
1110 * This is indeed a bit tricky, so we'll try the easy way first...
1111 */
1112 if (ArePathsIdentical(pszPath, pszDir, cchDir))
1113 {
1114 if (pszPath[cchDir])
1115 pszRet = (char *)pszPath + cchDir;
1116 else
1117 pszRet = "./";
1118 }
1119 else
1120 {
1121 pszAbsPath = AbsPath(pszPath);
1122 if (ArePathsIdentical(pszAbsPath, pszDir, cchDir))
1123 {
1124 if (pszPath[cchDir])
1125 pszRet = pszAbsPath + cchDir;
1126 else
1127 pszRet = "./";
1128 }
1129 }
1130 if (pszRet)
1131 {
1132 while (IS_SLASH_DRV(*pszRet))
1133 pszRet++;
1134 pszRet = xstrdup(pszRet);
1135 free(pszAbsPath);
1136 return pszRet;
1137 }
1138
1139 /*
1140 * Damn, it's gonna be complicated. Deal with that later.
1141 */
1142 fprintf(stderr, "kObjCache: complicated relative path stuff isn't implemented yet. sorry.\n");
1143 exit(1);
1144 return NULL;
1145}
1146
1147
1148/**
1149 * Utility function that combines a filename and directory and passes it onto fopen.
1150 *
1151 * @returns fopen return value.
1152 * @param pszName The file name.
1153 * @param pszDir The directory path.
1154 * @param pszMode The fopen mode string.
1155 */
1156static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)
1157{
1158 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1159 FILE *pFile = fopen(pszPath, pszMode);
1160 free(pszPath);
1161 return pFile;
1162}
1163
1164
1165/**
1166 * Deletes a file in a directory.
1167 *
1168 * @returns whatever unlink returns.
1169 * @param pszName The file name.
1170 * @param pszDir The directory path.
1171 */
1172static int UnlinkFileInDir(const char *pszName, const char *pszDir)
1173{
1174 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1175 int rc = unlink(pszPath);
1176 free(pszPath);
1177 return rc;
1178}
1179
1180
1181/**
1182 * Renames a file in a directory.
1183 *
1184 * @returns whatever unlink returns.
1185 * @param pszOldName The new file name.
1186 * @param pszNewName The old file name.
1187 * @param pszDir The directory path.
1188 */
1189static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)
1190{
1191 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);
1192 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);
1193 int rc = rename(pszOldPath, pszNewPath);
1194 free(pszOldPath);
1195 free(pszNewPath);
1196 return rc;
1197}
1198
1199
1200/**
1201 * Check if a (regular) file exists in a directory.
1202 *
1203 * @returns 1 if it exists and is a regular file, 0 if not.
1204 * @param pszName The file name.
1205 * @param pszDir The directory path.
1206 */
1207static int DoesFileInDirExist(const char *pszName, const char *pszDir)
1208{
1209 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1210 struct stat st;
1211 int rc = stat(pszPath, &st);
1212 free(pszPath);
1213#ifdef S_ISREG
1214 return !rc && S_ISREG(st.st_mode);
1215#elif defined(_MSC_VER)
1216 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;
1217#else
1218#error "Port me"
1219#endif
1220}
1221
1222
1223/**
1224 * Reads into memory an entire file.
1225 *
1226 * @returns Pointer to the heap allocation containing the file.
1227 * On failure NULL and errno is returned.
1228 * @param pszName The file.
1229 * @param pszDir The directory the file resides in.
1230 * @param pcbFile Where to store the file size.
1231 */
1232static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)
1233{
1234 int SavedErrno;
1235 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1236 int fd = open(pszPath, O_RDONLY | O_BINARY);
1237 if (fd >= 0)
1238 {
1239 off_t cbFile = lseek(fd, 0, SEEK_END);
1240 if ( cbFile >= 0
1241 && lseek(fd, 0, SEEK_SET) == 0)
1242 {
1243 char *pb = malloc(cbFile + 1);
1244 if (pb)
1245 {
1246 if (read(fd, pb, cbFile) == cbFile)
1247 {
1248 close(fd);
1249 pb[cbFile] = '\0';
1250 *pcbFile = (size_t)cbFile;
1251 return pb;
1252 }
1253 SavedErrno = errno;
1254 free(pb);
1255 }
1256 else
1257 SavedErrno = ENOMEM;
1258 }
1259 else
1260 SavedErrno = errno;
1261 close(fd);
1262 }
1263 else
1264 SavedErrno = errno;
1265 free(pszPath);
1266 errno = SavedErrno;
1267 return NULL;
1268}
1269
1270
1271static void *xmalloc(size_t cb)
1272{
1273 void *pv = malloc(cb);
1274 if (!pv)
1275 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb);
1276 return pv;
1277}
1278
1279
1280static void *xrealloc(void *pvOld, size_t cb)
1281{
1282 void *pv = realloc(pvOld, cb);
1283 if (!pv)
1284 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb);
1285 return pv;
1286}
1287
1288
1289static char *xstrdup(const char *pszIn)
1290{
1291 char *psz = strdup(pszIn);
1292 if (!psz)
1293 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)strlen(pszIn));
1294 return psz;
1295}
1296
1297
1298/**
1299 * Prints a syntax error and returns the appropriate exit code
1300 *
1301 * @returns approriate exit code.
1302 * @param pszFormat The syntax error message.
1303 * @param ... Message args.
1304 */
1305static int SyntaxError(const char *pszFormat, ...)
1306{
1307 va_list va;
1308 fprintf(stderr, "kObjCache: syntax error: ");
1309 va_start(va, pszFormat);
1310 vfprintf(stderr, pszFormat, va);
1311 va_end(va);
1312 return 1;
1313}
1314
1315
1316/**
1317 * Prints the usage.
1318 * @returns 0.
1319 */
1320static int usage(void)
1321{
1322 printf("syntax: kObjCache [-v|--verbose] [-f|--file] <cache-file> [-V|--version] [-r|--redir-stdout]\n"
1323 " --kObjCache-cpp <filename> <precompiler + args> \n"
1324 " --kObjCache-cc <object> <compiler + args>\n"
1325 " [--kObjCache-both [args]]\n"
1326 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n"
1327 "\n");
1328 return 0;
1329}
1330
1331
1332int main(int argc, char **argv)
1333{
1334 PKOBJCACHE pEntry;
1335
1336 const char *pszCacheFile;
1337
1338 const char **papszArgvPreComp = NULL;
1339 unsigned cArgvPreComp = 0;
1340 const char *pszPreCompName = NULL;
1341 int fRedirStdOut = 0;
1342
1343 const char **papszArgvCompile = NULL;
1344 unsigned cArgvCompile = 0;
1345 const char *pszObjName = NULL;
1346
1347 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
1348
1349 int i;
1350
1351 /*
1352 * Parse the arguments.
1353 */
1354 for (i = 1; i < argc; i++)
1355 {
1356 if (!strcmp(argv[i], "--kObjCache-cpp"))
1357 {
1358 enmMode = kOC_CppArgv;
1359 if (!pszPreCompName)
1360 {
1361 if (++i >= argc)
1362 return SyntaxError("--kObjCache-cpp requires an object filename!\n");
1363 pszPreCompName = argv[i];
1364 }
1365 }
1366 else if (!strcmp(argv[i], "--kObjCache-cc"))
1367 {
1368 enmMode = kOC_CcArgv;
1369 if (!pszObjName)
1370 {
1371 if (++i >= argc)
1372 return SyntaxError("--kObjCache-cc requires an precompiler output filename!\n");
1373 pszObjName = argv[i];
1374 }
1375 }
1376 else if (!strcmp(argv[i], "--kObjCache-both"))
1377 enmMode = kOC_BothArgv;
1378 else if (!strcmp(argv[i], "--help"))
1379 return usage();
1380 else if (enmMode != kOC_Options)
1381 {
1382 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
1383 {
1384 if (!(cArgvPreComp % 16))
1385 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
1386 papszArgvPreComp[cArgvPreComp++] = argv[i];
1387 papszArgvPreComp[cArgvPreComp] = NULL;
1388 }
1389 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
1390 {
1391 if (!(cArgvCompile % 16))
1392 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
1393 papszArgvCompile[cArgvCompile++] = argv[i];
1394 papszArgvCompile[cArgvCompile] = NULL;
1395 }
1396 }
1397 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file"))
1398 {
1399 if (i + 1 >= argc)
1400 return SyntaxError("%s requires a cache filename!\n", argv[i]);
1401 pszCacheFile = argv[++i];
1402 }
1403 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
1404 fRedirStdOut = 1;
1405 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
1406 g_fVerbose = 1;
1407 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
1408 g_fVerbose = 0;
1409 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?"))
1410 return usage();
1411 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
1412 {
1413 printf("kObjCache v0.0.0 ($Revision: 1008 $)\n");
1414 return 0;
1415 }
1416 else
1417 return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
1418 }
1419 if (!pszCacheFile)
1420 return SyntaxError("No cache file name (-f)\n");
1421 if (!cArgvCompile)
1422 return SyntaxError("No compiler arguments (--kObjCache-cc)\n");
1423 if (!cArgvPreComp)
1424 return SyntaxError("No precompiler arguments (--kObjCache-cc)\n");
1425
1426 /*
1427 * Create a cache entry from the cache file (if found).
1428 */
1429 pEntry = kObjCacheCreate(pszCacheFile);
1430 kObjCacheRead(pEntry);
1431
1432 /*
1433 * Do the compiling.
1434 */
1435 kObjCachePreCompile(pEntry, papszArgvPreComp, cArgvPreComp, pszPreCompName, fRedirStdOut);
1436 kObjCacheCompileIfNeeded(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
1437
1438 /*
1439 * Write the cache file.
1440 */
1441 kObjCacheWrite(pEntry);
1442 //kObjCacheCleanup(pEntry);
1443 /* kObjCacheDestroy(pEntry); - don't bother */
1444 return 0;
1445}
1446
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