source: trunk/src/kObjCache/kObjCache.c@ 1016

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

Unix/mac fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 45.5 KB
Line 
1/* $Id: kObjCache.c 1016 2007-06-02 18:58:41Z bird $ */
2/** @file
3 *
4 * kObjCache - Object Cache.
5 *
6 * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
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 int fdReDir;
558
559 close(1);
560 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0777);
561 if (fdReDir < 0)
562 kObjCacheFatal(pEntry, "%s - failed to create stdout redirection file '%s': %s\n",
563 pszMsg, pszStdOut, strerror(errno));
564 if (fdReDir != 1)
565 {
566 if (dup2(fdReDir, 1) < 0)
567 kObjCacheFatal(pEntry, "%s - dup2 failed: %s\n", pszMsg, strerror(errno));
568 close(fdReDir);
569 }
570 }
571
572 execvp(papszArgv[0], (char **)papszArgv);
573 kObjCacheFatal(pEntry, "%s - execvp failed: %s\n",
574 pszMsg, strerror(errno));
575 }
576 if (pid == -1)
577 kObjCacheFatal(pEntry, "%s - fork() failed: %s\n", pszMsg, strerror(errno));
578
579 pidWait = waitpid(pid, &iStatus, 0);
580 while (pidWait < 0 && errno == EINTR)
581 pidWait = waitpid(pid, &iStatus, 0);
582 if (pidWait != pid)
583 kObjCacheFatal(pEntry, "%s - waitpid failed rc=%d: %s\n",
584 pszMsg, pidWait, strerror(errno));
585 if (!WIFEXITED(iStatus))
586 kObjCacheFatal(pEntry, "%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
587 if (WEXITSTATUS(iStatus))
588 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
589#endif
590 (void)cArgv;
591}
592
593
594#ifdef USE_PIPE
595/**
596 * Spawns a child in a synchronous fashion.
597 * Terminating on failure.
598 *
599 * @param papszArgv Argument vector. The cArgv element is NULL.
600 * @param cArgv The number of arguments in the vector.
601 */
602static void kObjCacheSpawnPipe(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, char **ppszOutput, size_t *pcchOutput)
603{
604 int fds[2];
605 int iStatus;
606#if defined(__WIN__)
607 intptr_t pid, pidWait;
608#else
609 pid_t pid, pidWait;
610#endif
611 int fdStdOut;
612 size_t cbAlloc;
613 size_t cbLeft;
614 char *psz;
615
616 /*
617 * Setup the pipe.
618 */
619#if defined(__WIN__)
620 if (_pipe(fds, 0, _O_NOINHERIT | _O_BINARY) < 0)
621#else
622 if (pipe(fds) < 0)
623#endif
624 kObjCacheFatal(pEntry, "pipe failed: %s\n", strerror(errno));
625 fdStdOut = dup(1);
626 if (dup2(fds[1 /* write */], 1) < 0)
627 kObjCacheFatal(pEntry, "dup2(,1) failed: %s\n", strerror(errno));
628 close(fds[1]);
629 fds[1] = -1;
630#ifndef __WIN__
631 fcntl(fds[0], F_SETFD, FD_CLOEXEC);
632 fcntl(fdStdOut, F_SETFD, FD_CLOEXEC);
633#endif
634
635 /*
636 * Create the child process.
637 */
638#if defined(__OS2__) || defined(__WIN__)
639 errno = 0;
640 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
641 if (pid == -1)
642 kObjCacheFatal(pEntry, "%s - _spawnvp failed: %s\n", pszMsg, strerror(errno));
643
644#else
645 pid = fork();
646 if (!pid)
647 {
648 execvp(papszArgv[0], (char **)papszArgv);
649 kObjCacheFatal(pEntry, "%s - execvp failed: %s\n",
650 pszMsg, strerror(errno));
651 }
652 if (pid == -1)
653 kObjCacheFatal(pEntry, "%s - fork() failed: %s\n", pszMsg, strerror(errno));
654#endif
655
656 /*
657 * Restore stdout.
658 */
659 close(1);
660 fdStdOut = dup2(fdStdOut, 1);
661
662 /*
663 * Read data from the child.
664 */
665 cbAlloc = pEntry->cbOldCpp ? (pEntry->cbOldCpp + 4*1024*1024 + 4096) & ~(4*1024*1024 - 1) : 4*1024*1024;
666 cbLeft = cbAlloc;
667 *ppszOutput = psz = xmalloc(cbAlloc);
668 for (;;)
669 {
670 long cbRead = read(fds[0], psz, cbLeft);
671 if (!cbRead)
672 break;
673 if (cbRead < 0 && errno != EINTR)
674 kObjCacheFatal(pEntry, "%s - read(%d,,%ld) failed: %s\n", pszMsg, fds[0], (long)cbLeft, strerror(errno));
675 psz += cbRead;
676 cbLeft -= cbRead;
677
678 /* expand the buffer? */
679 if (!cbLeft)
680 {
681 size_t off = psz - *ppszOutput;
682 assert(off == cbAlloc);
683 cbLeft = 4*1024*1024;
684 cbAlloc += cbLeft;
685 *ppszOutput = xrealloc(*ppszOutput, cbAlloc);
686 psz = *ppszOutput + off;
687 }
688 }
689 close(fds[0]);
690 *pcchOutput = cbAlloc - cbLeft;
691
692 /*
693 * Reap the child.
694 */
695#ifdef __WIN__
696 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
697 if (pidWait == -1)
698 kObjCacheFatal(pEntry, "%s - waitpid failed: %s\n",
699 pszMsg, strerror(errno));
700 if (iStatus)
701 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, iStatus);
702#else
703 pidWait = waitpid(pid, &iStatus, 0);
704 while (pidWait < 0 && errno == EINTR)
705 pidWait = waitpid(pid, &iStatus, 0);
706 if (pidWait != pid)
707 kObjCacheFatal(pEntry, "%s - waitpid failed rc=%d: %s\n",
708 pszMsg, pidWait, strerror(errno));
709 if (!WIFEXITED(iStatus))
710 kObjCacheFatal(pEntry, "%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
711 if (WEXITSTATUS(iStatus))
712 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
713#endif
714 (void)cArgv;
715}
716#endif /* USE_PIPE */
717
718
719/**
720 * Reads the (new) output of the precompiler.
721 *
722 * Not used when using pipes.
723 *
724 * @param pEntry The cache entry. cbNewCpp and pszNewCppMapping will be updated.
725 */
726static void kObjCacheReadPrecompileOutput(PKOBJCACHE pEntry)
727{
728 pEntry->pszNewCppMapping = ReadFileInDir(pEntry->pszNewCppName, pEntry->pszDir, &pEntry->cbNewCpp);
729 if (!pEntry->pszNewCppMapping)
730 kObjCacheFatal(pEntry, "failed to open/read '%s' in '%s': %s\n",
731 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno));
732 kObjCacheVerbose(pEntry, "precompiled file is %lu bytes long\n", (unsigned long)pEntry->cbNewCpp);
733}
734
735
736/**
737 * Worker for kObjCachePreCompile and calculates the checksum of
738 * the precompiler output.
739 *
740 * @param pEntry The cache entry. NewSum will be updated.
741 */
742static void kObjCacheCalcChecksum(PKOBJCACHE pEntry)
743{
744 struct MD5Context MD5Ctx;
745
746 memset(&pEntry->NewSum, 0, sizeof(pEntry->NewSum));
747 pEntry->NewSum.crc32 = crc32(0, pEntry->pszNewCppMapping, pEntry->cbNewCpp);
748 MD5Init(&MD5Ctx);
749 MD5Update(&MD5Ctx, (unsigned char *)pEntry->pszNewCppMapping, pEntry->cbNewCpp);
750 MD5Final(&pEntry->NewSum.md5[0], &MD5Ctx);
751 kObjCacheVerbose(pEntry, "crc32=%#lx md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
752 pEntry->NewSum.crc32,
753 pEntry->NewSum.md5[0], pEntry->NewSum.md5[1], pEntry->NewSum.md5[2], pEntry->NewSum.md5[3],
754 pEntry->NewSum.md5[4], pEntry->NewSum.md5[5], pEntry->NewSum.md5[6], pEntry->NewSum.md5[7],
755 pEntry->NewSum.md5[8], pEntry->NewSum.md5[9], pEntry->NewSum.md5[10], pEntry->NewSum.md5[11],
756 pEntry->NewSum.md5[12], pEntry->NewSum.md5[13], pEntry->NewSum.md5[14], pEntry->NewSum.md5[15]);
757
758}
759
760
761/**
762 * Run the precompiler and calculate the checksum of the output.
763 *
764 * @param pEntry The cache entry.
765 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL.
766 * @param cArgvPreComp The number of arguments.
767 * @param pszPreCompName Precompile output name. (must kick around)
768 * @param fRedirStdOut Whether stdout needs to be redirected or not.
769 */
770static void kObjCachePreCompile(PKOBJCACHE pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp, const char *pszPreCompName, int fRedirStdOut)
771{
772#ifdef USE_PIPE
773 /*
774 * Flag it as piped or non-piped.
775 */
776 if (fRedirStdOut)
777 pEntry->fPiped = 1;
778 else
779#endif
780 pEntry->fPiped = 0;
781
782 /*
783 * Rename the old precompiled output to '-old'.
784 * We'll discard the old output and keep the new output, but because
785 * we might with to do a quick matchup later we can't remove it just now.
786 * If we're using the pipe strategy, we will not do any renaming.
787 */
788 if ( pEntry->pszOldCppName
789 && !pEntry->fPiped
790 && DoesFileInDirExist(pEntry->pszOldCppName, pEntry->pszDir))
791 {
792 size_t cch = strlen(pEntry->pszOldCppName);
793 char *psz = xmalloc(cch + sizeof("-old"));
794 memcpy(psz, pEntry->pszOldCppName, cch);
795 memcpy(psz + cch, "-old", sizeof("-old"));
796
797 kObjCacheVerbose(pEntry, "renaming '%s' to '%s' in '%s'\n", pEntry->pszOldCppName, psz, pEntry->pszDir);
798 UnlinkFileInDir(psz, pEntry->pszDir);
799 if (RenameFileInDir(pEntry->pszOldCppName, psz, pEntry->pszDir))
800 kObjCacheFatal(pEntry, "failed to rename '%s' -> '%s' in '%s': %s\n",
801 pEntry->pszOldCppName, psz, pEntry->pszDir, strerror(errno));
802 free(pEntry->pszOldCppName);
803 pEntry->pszOldCppName = psz;
804 }
805 pEntry->pszNewCppName = CalcRelativeName(pszPreCompName, pEntry->pszDir);
806
807 /*
808 * Precompile it and calculate the checksum on the output.
809 */
810 kObjCacheVerbose(pEntry, "precompiling -> '%s'...\n", pEntry->pszNewCppName);
811#ifdef USE_PIPE
812 if (pEntry->fPiped)
813 kObjCacheSpawnPipe(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", &pEntry->pszNewCppMapping, &pEntry->cbNewCpp);
814 else
815#endif
816 {
817 if (fRedirStdOut)
818 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", pszPreCompName);
819 else
820 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL);
821 kObjCacheReadPrecompileOutput(pEntry);
822 }
823 kObjCacheCalcChecksum(pEntry);
824}
825
826
827/**
828 * Worker for kObjCacheCompileIfNeeded that compares the
829 * precompiled output.
830 *
831 * @returns 1 if matching, 0 if not matching.
832 * @param pEntry The entry containing the names of the files to compare.
833 * The entry is not updated in any way.
834 */
835static int kObjCacheCompareOldAndNewOutput(PCKOBJCACHE pEntry)
836{
837 /** @todo do some quick but fancy comparing that determins whether code
838 * has actually changed or moved. We can ignore declarations and typedefs that
839 * has just been moved up/down a bit. The typical case is adding a new error
840 * #define that doesn't influence the current compile job. */
841 return 0;
842}
843
844
845/**
846 * Worker for kObjCacheCompileIfNeeded that does the actual (re)compilation.
847 *
848 * @returns 1 if matching, 0 if not matching.
849 * @param pEntry The cache entry.
850 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL.
851 * @param cArgvCompile The number of arguments in the vector.
852 * @param pszObjName The name of the object file.
853 */
854static void kObjCacheCompileIt(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName)
855{
856 /*
857 * Delete the old object file and precompiler output.
858 */
859 if (pEntry->pszObjName)
860 {
861 UnlinkFileInDir(pEntry->pszObjName, pEntry->pszDir);
862 pEntry->pszObjName = NULL;
863 }
864 pEntry->pszNewObjName = CalcRelativeName(pszObjName, pEntry->pszDir);
865
866 /*
867 * If we executed the precompiled in piped mode we'll have to write the
868 * precompiler output to disk so the compile has some thing to chew on.
869 */
870 if (pEntry->fPiped)
871 {
872 FILE *pFile = FOpenFileInDir(pEntry->pszNewCppName, pEntry->pszDir, "wb");
873 if (!pFile)
874 kObjCacheFatal(pEntry, "failed to create file '%s' in '%s': %s\n",
875 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno));
876 if (fwrite(pEntry->pszNewCppMapping, pEntry->cbNewCpp, 1, pFile) != 1)
877 kObjCacheFatal(pEntry, "fwrite failed: %s\n", strerror(errno));
878 if (fclose(pFile))
879 kObjCacheFatal(pEntry, "fclose failed: %s\n", strerror(errno));
880 }
881
882 /*
883 * Release buffers we no longer need before starting the compile.
884 */
885 free(pEntry->pszNewCppMapping);
886 pEntry->pszNewCppMapping = NULL;
887 free(pEntry->pszOldCppMapping);
888 pEntry->pszOldCppMapping = NULL;
889
890 /*
891 * Do the recompilation.
892 */
893 kObjCacheVerbose(pEntry, "compiling -> '%s'...\n", pEntry->pszNewObjName);
894 pEntry->papszArgvCompile = (char **)papszArgvCompile; /* LEAK */
895 pEntry->cArgvCompile = cArgvCompile;
896 kObjCacheSpawn(pEntry, papszArgvCompile, cArgvCompile, "compile", NULL);
897}
898
899
900/**
901 * Check if (re-)compilation is required and do it.
902 *
903 * @returns 1 if matching, 0 if not matching.
904 * @param pEntry The cache entry.
905 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL.
906 * @param cArgvCompile The number of arguments in the vector.
907 * @param pszObjName The name of the object file.
908 */
909static void kObjCacheCompileIfNeeded(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName)
910{
911 /*
912 * Does the object name differ?
913 */
914 if (!pEntry->fNeedCompiling)
915 {
916 char *pszTmp = CalcRelativeName(pszObjName, pEntry->pszDir);
917 if (strcmp(pEntry->pszObjName, pszTmp))
918 {
919 pEntry->fNeedCompiling = 1;
920 kObjCacheVerbose(pEntry, "object name changed '%s' -> '%s'\n", pEntry->pszObjName, pszTmp);
921 }
922 free(pszTmp);
923 }
924
925 /*
926 * Does the compile command differ?
927 * TODO: Ignore irrelevant options here (like warning level).
928 */
929 if ( !pEntry->fNeedCompiling
930 && pEntry->cArgvCompile != cArgvCompile)
931 {
932 pEntry->fNeedCompiling = 1;
933 kObjCacheVerbose(pEntry, "compile argument count changed\n");
934 }
935 if (!pEntry->fNeedCompiling)
936 {
937 unsigned i;
938 for (i = 0; i < cArgvCompile; i++)
939 if (strcmp(papszArgvCompile[i], pEntry->papszArgvCompile[i]))
940 {
941 pEntry->fNeedCompiling = 1;
942 kObjCacheVerbose(pEntry, "compile argument differs (%#d)\n", i);
943 break;
944 }
945 }
946
947 /*
948 * Does the object file exist?
949 */
950 if ( !pEntry->fNeedCompiling
951 && !DoesFileInDirExist(pEntry->pszObjName, pEntry->pszDir))
952 {
953 pEntry->fNeedCompiling = 1;
954 kObjCacheVerbose(pEntry, "object file doesn't exist\n");
955 }
956
957 /*
958 * Does the precompiled output differ in any significant way?
959 */
960 if (!pEntry->fNeedCompiling)
961 {
962 int fFound = 0;
963 PCKOCSUM pSum;
964 for (pSum = &pEntry->SumHead; pSum; pSum = pSum->pNext)
965 if (kObjCacheSumIsEqual(pSum, &pEntry->NewSum))
966 {
967 fFound = 1;
968 break;
969 }
970 if (!fFound)
971 {
972 kObjCacheVerbose(pEntry, "no checksum match - comparing output\n");
973 if (!kObjCacheCompareOldAndNewOutput(pEntry))
974 pEntry->fNeedCompiling = 1;
975 else
976 {
977 /* insert the sum into the list. */
978 pEntry->NewSum.pNext = pEntry->SumHead.pNext;
979 pEntry->SumHead.pNext = &pEntry->NewSum;
980 }
981 }
982 }
983
984 /*
985 * Discard the old precompiled output it's no longer needed.s
986 */
987 if (pEntry->pszOldCppName)
988 {
989 UnlinkFileInDir(pEntry->pszOldCppName, pEntry->pszDir);
990 free(pEntry->pszOldCppName);
991 pEntry->pszOldCppName = NULL;
992 }
993
994 /*
995 * Do the compliation if found necessary.
996 */
997 if (pEntry->fNeedCompiling)
998 kObjCacheCompileIt(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
999}
1000
1001
1002/**
1003 * Gets the absolute path
1004 *
1005 * @returns A new heap buffer containing the absolute path.
1006 * @param pszPath The path to make absolute. (Readonly)
1007 */
1008static char *AbsPath(const char *pszPath)
1009{
1010 char szTmp[PATH_MAX];
1011#if defined(__OS2__) || defined(__WIN__)
1012 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))
1013 return xstrdup(pszPath);
1014#else
1015 if (!realpath(pszPath, szTmp))
1016 return xstrdup(pszPath);
1017#endif
1018 return xstrdup(szTmp);
1019}
1020
1021
1022/**
1023 * Utility function that finds the filename part in a path.
1024 *
1025 * @returns Pointer to the file name part (this may be "").
1026 * @param pszPath The path to parse.
1027 */
1028static const char *FindFilenameInPath(const char *pszPath)
1029{
1030 const char *pszFilename = strchr(pszPath, '\0') - 1;
1031 while ( pszFilename > pszPath
1032 && !IS_SLASH_DRV(pszFilename[-1]))
1033 pszFilename--;
1034 return pszFilename;
1035}
1036
1037
1038/**
1039 * Utility function that combines a filename and a directory into a path.
1040 *
1041 * @returns malloced buffer containing the result.
1042 * @param pszName The file name.
1043 * @param pszDir The directory path.
1044 */
1045static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)
1046{
1047 size_t cchName = strlen(pszName);
1048 size_t cchDir = strlen(pszDir);
1049 char *pszBuf = xmalloc(cchName + cchDir + 2);
1050 memcpy(pszBuf, pszDir, cchDir);
1051 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1]))
1052 pszBuf[cchDir++] = PATH_SLASH;
1053 memcpy(pszBuf + cchDir, pszName, cchName + 1);
1054 return pszBuf;
1055}
1056
1057
1058/**
1059 * Compares two path strings to see if they are identical.
1060 *
1061 * This doesn't do anything fancy, just the case ignoring and
1062 * slash unification.
1063 *
1064 * @returns 1 if equal, 0 otherwise.
1065 * @param pszPath1 The first path.
1066 * @param pszPath2 The second path.
1067 * @param cch The number of characters to compare.
1068 */
1069static int ArePathsIdentical(const char *pszPath1, const char *pszPath2, size_t cch)
1070{
1071#if defined(__OS2__) || defined(__WIN__)
1072 if (strnicmp(pszPath1, pszPath2, cch))
1073 {
1074 /* Slashes may differ, compare char by char. */
1075 const char *psz1 = pszPath1;
1076 const char *psz2 = pszPath2;
1077 for (;cch; psz1++, psz2++, cch--)
1078 {
1079 if (*psz1 != *psz2)
1080 {
1081 if ( tolower(*psz1) != tolower(*psz2)
1082 && toupper(*psz1) != toupper(*psz2)
1083 && *psz1 != '/'
1084 && *psz1 != '\\'
1085 && *psz2 != '/'
1086 && *psz2 != '\\')
1087 return 0;
1088 }
1089 }
1090 }
1091 return 1;
1092#else
1093 return !strncmp(pszPath1, pszPath2, cch);
1094#endif
1095}
1096
1097
1098/**
1099 * Calculate how to get to pszPath from pszDir.
1100 *
1101 * @returns The relative path from pszDir to path pszPath.
1102 * @param pszPath The path to the object.
1103 * @param pszDir The directory it shall be relative to.
1104 */
1105static char *CalcRelativeName(const char *pszPath, const char *pszDir)
1106{
1107 char *pszRet = NULL;
1108 char *pszAbsPath = NULL;
1109 size_t cchDir = strlen(pszDir);
1110
1111 /*
1112 * This is indeed a bit tricky, so we'll try the easy way first...
1113 */
1114 if (ArePathsIdentical(pszPath, pszDir, cchDir))
1115 {
1116 if (pszPath[cchDir])
1117 pszRet = (char *)pszPath + cchDir;
1118 else
1119 pszRet = "./";
1120 }
1121 else
1122 {
1123 pszAbsPath = AbsPath(pszPath);
1124 if (ArePathsIdentical(pszAbsPath, pszDir, cchDir))
1125 {
1126 if (pszPath[cchDir])
1127 pszRet = pszAbsPath + cchDir;
1128 else
1129 pszRet = "./";
1130 }
1131 }
1132 if (pszRet)
1133 {
1134 while (IS_SLASH_DRV(*pszRet))
1135 pszRet++;
1136 pszRet = xstrdup(pszRet);
1137 free(pszAbsPath);
1138 return pszRet;
1139 }
1140
1141 /*
1142 * Damn, it's gonna be complicated. Deal with that later.
1143 */
1144 fprintf(stderr, "kObjCache: complicated relative path stuff isn't implemented yet. sorry.\n");
1145 exit(1);
1146 return NULL;
1147}
1148
1149
1150/**
1151 * Utility function that combines a filename and directory and passes it onto fopen.
1152 *
1153 * @returns fopen return value.
1154 * @param pszName The file name.
1155 * @param pszDir The directory path.
1156 * @param pszMode The fopen mode string.
1157 */
1158static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)
1159{
1160 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1161 FILE *pFile = fopen(pszPath, pszMode);
1162 free(pszPath);
1163 return pFile;
1164}
1165
1166
1167/**
1168 * Deletes a file in a directory.
1169 *
1170 * @returns whatever unlink returns.
1171 * @param pszName The file name.
1172 * @param pszDir The directory path.
1173 */
1174static int UnlinkFileInDir(const char *pszName, const char *pszDir)
1175{
1176 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1177 int rc = unlink(pszPath);
1178 free(pszPath);
1179 return rc;
1180}
1181
1182
1183/**
1184 * Renames a file in a directory.
1185 *
1186 * @returns whatever unlink returns.
1187 * @param pszOldName The new file name.
1188 * @param pszNewName The old file name.
1189 * @param pszDir The directory path.
1190 */
1191static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)
1192{
1193 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);
1194 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);
1195 int rc = rename(pszOldPath, pszNewPath);
1196 free(pszOldPath);
1197 free(pszNewPath);
1198 return rc;
1199}
1200
1201
1202/**
1203 * Check if a (regular) file exists in a directory.
1204 *
1205 * @returns 1 if it exists and is a regular file, 0 if not.
1206 * @param pszName The file name.
1207 * @param pszDir The directory path.
1208 */
1209static int DoesFileInDirExist(const char *pszName, const char *pszDir)
1210{
1211 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1212 struct stat st;
1213 int rc = stat(pszPath, &st);
1214 free(pszPath);
1215#ifdef S_ISREG
1216 return !rc && S_ISREG(st.st_mode);
1217#elif defined(_MSC_VER)
1218 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;
1219#else
1220#error "Port me"
1221#endif
1222}
1223
1224
1225/**
1226 * Reads into memory an entire file.
1227 *
1228 * @returns Pointer to the heap allocation containing the file.
1229 * On failure NULL and errno is returned.
1230 * @param pszName The file.
1231 * @param pszDir The directory the file resides in.
1232 * @param pcbFile Where to store the file size.
1233 */
1234static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)
1235{
1236 int SavedErrno;
1237 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
1238 int fd = open(pszPath, O_RDONLY | O_BINARY);
1239 if (fd >= 0)
1240 {
1241 off_t cbFile = lseek(fd, 0, SEEK_END);
1242 if ( cbFile >= 0
1243 && lseek(fd, 0, SEEK_SET) == 0)
1244 {
1245 char *pb = malloc(cbFile + 1);
1246 if (pb)
1247 {
1248 if (read(fd, pb, cbFile) == cbFile)
1249 {
1250 close(fd);
1251 pb[cbFile] = '\0';
1252 *pcbFile = (size_t)cbFile;
1253 return pb;
1254 }
1255 SavedErrno = errno;
1256 free(pb);
1257 }
1258 else
1259 SavedErrno = ENOMEM;
1260 }
1261 else
1262 SavedErrno = errno;
1263 close(fd);
1264 }
1265 else
1266 SavedErrno = errno;
1267 free(pszPath);
1268 errno = SavedErrno;
1269 return NULL;
1270}
1271
1272
1273static void *xmalloc(size_t cb)
1274{
1275 void *pv = malloc(cb);
1276 if (!pv)
1277 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb);
1278 return pv;
1279}
1280
1281
1282static void *xrealloc(void *pvOld, size_t cb)
1283{
1284 void *pv = realloc(pvOld, cb);
1285 if (!pv)
1286 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb);
1287 return pv;
1288}
1289
1290
1291static char *xstrdup(const char *pszIn)
1292{
1293 char *psz = strdup(pszIn);
1294 if (!psz)
1295 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)strlen(pszIn));
1296 return psz;
1297}
1298
1299
1300/**
1301 * Prints a syntax error and returns the appropriate exit code
1302 *
1303 * @returns approriate exit code.
1304 * @param pszFormat The syntax error message.
1305 * @param ... Message args.
1306 */
1307static int SyntaxError(const char *pszFormat, ...)
1308{
1309 va_list va;
1310 fprintf(stderr, "kObjCache: syntax error: ");
1311 va_start(va, pszFormat);
1312 vfprintf(stderr, pszFormat, va);
1313 va_end(va);
1314 return 1;
1315}
1316
1317
1318/**
1319 * Prints the usage.
1320 * @returns 0.
1321 */
1322static int usage(void)
1323{
1324 printf("syntax: kObjCache [-v|--verbose] [-f|--file] <cache-file> [-V|--version] [-r|--redir-stdout]\n"
1325 " --kObjCache-cpp <filename> <precompiler + args> \n"
1326 " --kObjCache-cc <object> <compiler + args>\n"
1327 " [--kObjCache-both [args]]\n"
1328 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n"
1329 "\n");
1330 return 0;
1331}
1332
1333
1334int main(int argc, char **argv)
1335{
1336 PKOBJCACHE pEntry;
1337
1338 const char *pszCacheFile;
1339
1340 const char **papszArgvPreComp = NULL;
1341 unsigned cArgvPreComp = 0;
1342 const char *pszPreCompName = NULL;
1343 int fRedirStdOut = 0;
1344
1345 const char **papszArgvCompile = NULL;
1346 unsigned cArgvCompile = 0;
1347 const char *pszObjName = NULL;
1348
1349 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
1350
1351 int i;
1352
1353 /*
1354 * Parse the arguments.
1355 */
1356 for (i = 1; i < argc; i++)
1357 {
1358 if (!strcmp(argv[i], "--kObjCache-cpp"))
1359 {
1360 enmMode = kOC_CppArgv;
1361 if (!pszPreCompName)
1362 {
1363 if (++i >= argc)
1364 return SyntaxError("--kObjCache-cpp requires an object filename!\n");
1365 pszPreCompName = argv[i];
1366 }
1367 }
1368 else if (!strcmp(argv[i], "--kObjCache-cc"))
1369 {
1370 enmMode = kOC_CcArgv;
1371 if (!pszObjName)
1372 {
1373 if (++i >= argc)
1374 return SyntaxError("--kObjCache-cc requires an precompiler output filename!\n");
1375 pszObjName = argv[i];
1376 }
1377 }
1378 else if (!strcmp(argv[i], "--kObjCache-both"))
1379 enmMode = kOC_BothArgv;
1380 else if (!strcmp(argv[i], "--help"))
1381 return usage();
1382 else if (enmMode != kOC_Options)
1383 {
1384 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
1385 {
1386 if (!(cArgvPreComp % 16))
1387 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
1388 papszArgvPreComp[cArgvPreComp++] = argv[i];
1389 papszArgvPreComp[cArgvPreComp] = NULL;
1390 }
1391 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
1392 {
1393 if (!(cArgvCompile % 16))
1394 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
1395 papszArgvCompile[cArgvCompile++] = argv[i];
1396 papszArgvCompile[cArgvCompile] = NULL;
1397 }
1398 }
1399 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file"))
1400 {
1401 if (i + 1 >= argc)
1402 return SyntaxError("%s requires a cache filename!\n", argv[i]);
1403 pszCacheFile = argv[++i];
1404 }
1405 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
1406 fRedirStdOut = 1;
1407 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
1408 g_fVerbose = 1;
1409 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
1410 g_fVerbose = 0;
1411 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?"))
1412 return usage();
1413 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
1414 {
1415 printf("kObjCache v0.0.0 ($Revision: 1016 $)\n");
1416 return 0;
1417 }
1418 else
1419 return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
1420 }
1421 if (!pszCacheFile)
1422 return SyntaxError("No cache file name (-f)\n");
1423 if (!cArgvCompile)
1424 return SyntaxError("No compiler arguments (--kObjCache-cc)\n");
1425 if (!cArgvPreComp)
1426 return SyntaxError("No precompiler arguments (--kObjCache-cc)\n");
1427
1428 /*
1429 * Create a cache entry from the cache file (if found).
1430 */
1431 pEntry = kObjCacheCreate(pszCacheFile);
1432 kObjCacheRead(pEntry);
1433
1434 /*
1435 * Do the compiling.
1436 */
1437 kObjCachePreCompile(pEntry, papszArgvPreComp, cArgvPreComp, pszPreCompName, fRedirStdOut);
1438 kObjCacheCompileIfNeeded(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
1439
1440 /*
1441 * Write the cache file.
1442 */
1443 kObjCacheWrite(pEntry);
1444 //kObjCacheCleanup(pEntry);
1445 /* kObjCacheDestroy(pEntry); - don't bother */
1446 return 0;
1447}
1448
Note: See TracBrowser for help on using the repository browser.