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

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

initial write up. (backup commit)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 27.9 KB
Line 
1/* $Id: kObjCache.c 1001 2007-06-02 08:21:52Z 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#if defined(__OS2__) || defined(__WIN__)
40# include <process.h>
41# include <io.h>
42# ifdef __OS2__
43# include <unistd.h>
44# endif
45#else
46# include <unistd.h>
47# include <sys/wait.h>
48# ifndef O_BINARY
49# define O_BINARY 0
50# endif
51#endif
52#include "crc32.h"
53#include "md5.h"
54
55
56/* for later: */
57#define xmalloc malloc
58#define xrealloc realloc
59#define xstrdup strdup
60
61
62/*******************************************************************************
63* Structures and Typedefs *
64*******************************************************************************/
65/** A checksum list entry.
66 * We keep a list checksums (of precompiler output) that matches, The planned
67 * matching algorithm doesn't require the precompiler output to be indentical,
68 * only to produce the same object files.
69 */
70typedef struct KOCSUM
71{
72 /** The next checksum. */
73 struct KOCSUM *pNext;
74 /** The crc32 checksum. */
75 uint32_t crc32;
76 /** The MD5 digest. */
77 unsigned char md5[16];
78} KOCSUM, *PKOCSUM;
79/** Pointer to a const KOCSUM. */
80typedef const KOCSUM *PCKOCSUM;
81
82/**
83 * The object cache data.
84 */
85typedef struct KOBJCACHE
86{
87 /** The cache dir that all other names are relative to. */
88 char *pszDir;
89 /** The name of the cache file. */
90 char *pszName;
91 /** Set if the object needs to be (re)compiled. */
92 unsigned fNeedCompiling;
93
94 /** The name of new precompiled output. */
95 const char *pszNewCppName;
96 /** Pointer to the 'mapping' of the new precompiled output. */
97 char *pszNewCppMapping;
98 /** The size of the new precompiled output 'mapping'. */
99 size_t cbNewCppMapping;
100 /** The new checksum. */
101 KOCSUM NewSum;
102 /** The new object filename (relative to the cache file). */
103 char *pszNewObjName;
104
105 /** The name of the precompiled output. (relative to the cache file) */
106 char *pszOldCppName;
107 /** Pointer to the 'mapping' of the old precompiled output. */
108 char *pszOldCppMapping;
109 /** The size of the old precompiled output 'mapping'. */
110 size_t cbOldCppMapping;
111
112 /** The raw cache file buffer.
113 * The const members below points into this. */
114 char *pszRawFile;
115 /** The head of the checksum list. */
116 KOCSUM SumHead;
117 /** The object filename (relative to the cache file). */
118 const char *pszObjName;
119 /** The compile argument vector used to build the object. */
120 const char **papszArgvCompile;
121 /** The size of the compile */
122 unsigned cArgvCompile;
123} KOBJCACHE, *PKOBJCACHE;
124/** Pointer to a const KOBJCACHE. */
125typedef const KOBJCACHE *PCKOBJCACHE;
126
127
128/*******************************************************************************
129* Global Variables *
130*******************************************************************************/
131/** Whether verbose output is enabled. */
132static int g_fVerbose = 0;
133
134
135/*******************************************************************************
136* Internal Functions *
137*******************************************************************************/
138static const char *FindFilenameInPath(const char *pszPath);
139static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);
140static char *CalcRelativeName(const char *pszPath, const char *pszDir);
141static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode);
142static int UnlinkFileInDir(const char *pszName, const char *pszDir);
143static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir);
144static int DoesFileInDirExist(const char *pszName, const char *pszDir);
145static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);
146
147/* crc.c */
148extern uint32_t crc32(uint32_t, const void *, size_t);
149
150
151/**
152 * Compares two check sum entries.
153 *
154 * @returns 1 if equal, 0 if not equal.
155 *
156 * @param pSum1 The first checksum.
157 * @param pSum2 The second checksum.
158 */
159static int kObjCacheSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)
160{
161 if (pSum1 == pSum2)
162 return 1;
163 if (pSum1 || !pSum2)
164 return 0;
165 if (pSum1->crc32 != pSum2->crc32)
166 return 0;
167 if (memcmp(&pSum1->md5[0], &pSum2->md5[0], sizeof(pSum1->md5)))
168 return 0;
169 return 1;
170}
171
172
173/**
174 * Creates a cache entry for the given cache file name.
175 *
176 * @returns Pointer to a cache entry.
177 * @param pszFilename The cache file name.
178 */
179static PKOBJCACHE kObjCacheCreate(const char *pszFilename)
180{
181 PKOBJCACHE pEntry;
182 const char *pszDir;
183 size_t cchDir;
184
185 /*
186 * Allocate an empty entry.
187 */
188 pEntry = xmalloc(sizeof(*pEntry));
189 memset(pEntry, 0, sizeof(*pEntry));
190
191 /*
192 * Setup the directory and cache file name.
193 */
194 pszDir = pszFilename;
195 assert(*pszFilename);
196 pszFilename = FindFilenameInPath(pszFilename);
197 if (pszFilename <= pszDir)
198 {
199 pszDir = "./";
200 cchDir = 2;
201 }
202 else
203 cchDir = pszFilename - pszDir; /* includes the separator */
204
205 pEntry->pszDir = xmalloc(cchDir + 1);
206 memcpy(pEntry->pszDir, pszDir, cchDir);
207 pEntry->pszDir[cchDir] = '\0';
208 pEntry->pszName = xstrdup(pszFilename);
209
210 return pEntry;
211}
212
213
214#if 0 /* don't bother. */
215/**
216 * Destroys the cache entry freeing up all it's resources.
217 *
218 * @param pEntry The entry to free.
219 */
220static void kObjCacheDestroy(PKOBJCACHE pEntry)
221{
222 free(pEntry->pszDir);
223 free(pEntry->pszName);
224 while (pEntry->SumHead.pNext)
225 {
226 void *pv = pEntry->SumHead.pNext;
227 pEntry->SumHead.pNext = pEntry->SumHead.pNext->pNext;
228 if (pv != &pEntry->NewSum)
229 free(pv);
230 }
231 free(pEntry);
232}
233#endif
234
235
236/**
237 * Print a fatal error message and exit with rc=1.
238 *
239 * @param pEntry The cache entry.
240 * @param pszFormat The message to print.
241 * @param ... Format arguments.
242 */
243static void kObjCacheFatal(PCKOBJCACHE pEntry, const char *pszFormat, ...)
244{
245 va_list va;
246
247 fprintf(stderr, "kObjCache %s - fatal error: ", pEntry->pszName);
248 va_start(va, pszFormat);
249 vfprintf(stderr, pszFormat, va);
250 va_end(va);
251
252 exit(1);
253}
254
255
256/**
257 * Print a verbose message if verbosisty is enabled.
258 *
259 * @param pEntry The cache entry.
260 * @param pszFormat The message to print.
261 * @param ... Format arguments.
262 */
263static void kObjCacheVerbose(PCKOBJCACHE pEntry, const char *pszFormat, ...)
264{
265 if (g_fVerbose)
266 {
267 va_list va;
268
269 fprintf(stdout, "kObjCache %s - info: ", pEntry->pszName);
270 va_start(va, pszFormat);
271 vfprintf(stdout, pszFormat, va);
272 va_end(va);
273 }
274}
275
276
277/**
278 * Reads and parses the cache file.
279 *
280 * @param pEntry The entry to read it into.
281 */
282static void kObjCacheRead(PKOBJCACHE pEntry)
283{
284
285}
286
287
288/**
289 * Writes the cache file.
290 *
291 * @param pEntry The entry to write.
292 */
293static void kObjCacheWrite(PKOBJCACHE pEntry)
294{
295
296}
297
298
299/**
300 * Spawns a child in a synchronous fashion.
301 * Terminating on failure.
302 *
303 * @param papszArgv Argument vector. The cArgv element is NULL.
304 * @param cArgv The number of arguments in the vector.
305 */
306static void kObjCacheSpawn(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg)
307{
308#if defined(__OS2__)
309 int rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
310 if (rc)
311 kObjCacheFatal(pEntry, "%s - _spawnvp / command failed, rc=%#x\n", pszMsg, rc);
312
313#elif defined(__WIN__)
314 intptr_t rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
315 if (rc)
316 kObjCacheFatal(pEntry, "%s - _spawnvp / command failed, rc=0x%p\n", pszMsg, rc);
317
318#else
319 int iStatus;
320 pid_t pidWait;
321 pid_t pid = fork();
322 if (!pid)
323 {
324 execvp(papszArgv[0], papszArgv);
325 kObjCacheFatal(pEntry, "%s - execvp failed rc=%d errno=%d %s\n",
326 pszMsg, rc, errno, strerror(errno));
327 }
328 pidWait = waitpid(pid, &iStatus);
329 while (pidWait < 0 && errno == EINTR)
330 pidWait = waitpid(pid, &iStatus);
331 if (pidWait != pid)
332 kObjCacheFatal(pEntry, "%s - waitpid failed rc=%d errno=%d %s\n",
333 pszMsg, rc, errno, strerror(errno));
334 if (!WIFEXITED(iStatus))
335 kObjCacheFatal(pEntry, "%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
336 if (WEXITSTATUS(iStatus))
337 kObjCacheFatal(pEntry, "%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
338#endif
339 (void)cArgv;
340}
341
342
343/**
344 * Worker for kObjCachePreCompile and calculates the checksum of
345 * the precompiler output.
346 *
347 * @param pEntry The cache entry. NewSum will be updated.
348 */
349static void kObjCacheCalcChecksum(PKOBJCACHE pEntry)
350{
351 struct MD5Context MD5Ctx;
352
353 /*
354 * Read/maps the entire file into a buffer and does the crc sums
355 * on the buffer. This assumes the precompiler output isn't
356 * gigantic, but that's a pretty safe assumption I hope...
357 */
358 pEntry->pszNewCppMapping = ReadFileInDir(pEntry->pszNewCppName, pEntry->pszDir, &pEntry->cbNewCppMapping);
359 if (pEntry->pszNewCppMapping)
360 kObjCacheFatal(pEntry, "failed to open/read '%s' in '%s': %s\n",
361 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno));
362
363 pEntry->NewSum.crc32 = crc32(0, pEntry->pszNewCppMapping, pEntry->cbNewCppMapping);
364 MD5Init(&MD5Ctx);
365 MD5Update(&MD5Ctx, pEntry->pszNewCppMapping, pEntry->cbNewCppMapping);
366 MD5Final(&pEntry->NewSum.md5[0], &MD5Ctx);
367}
368
369
370/**
371 * Run the precompiler and calculate the checksum of the output.
372 *
373 * @param pEntry The cache entry.
374 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL.
375 * @param cArgvPreComp The number of arguments.
376 * @param pszPreCompName Precompile output name. (must kick around)
377 */
378static void kObjCachePreCompile(PKOBJCACHE pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp, const char *pszPreCompName)
379{
380 /*
381 * Rename the old precompiled output to '-old'.
382 * We'll discard the old output and keep the new output, but because
383 * we might with to do a quick matchup later we can't remove it just now.
384 */
385 if (pEntry->pszOldCppName)
386 {
387 size_t cch = strlen(pEntry->pszOldCppName);
388 char *psz = xmalloc(cch + sizeof("-old"));
389 memcpy(psz, pEntry->pszOldCppName, cch);
390 memcpy(psz + cch, "-old", sizeof("-old"));
391
392 UnlinkFileInDir(psz, pEntry->pszDir);
393 if (RenameFileInDir(pEntry->pszOldCppName, psz, pEntry->pszDir))
394 kObjCacheFatal(pEntry, "failed to rename '%s' -> '%s' in '%s': %s\n",
395 pEntry->pszOldCppName, psz, pEntry->pszDir, strerror(errno));
396 free(pEntry->pszOldCppName);
397 pEntry->pszOldCppName = psz;
398 }
399 pEntry->pszNewCppName = pszPreCompName;
400
401 /*
402 * Precompile it and calculate the checksum on the output.
403 */
404 if (!pszPreCompName)
405 {
406 kObjCacheFatal(pEntry, "redirection feature is not implemented\n");
407 /** @todo piped output. */
408 }
409 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile");
410 kObjCacheCalcChecksum(pEntry);
411}
412
413
414/**
415 * Worker for kObjCacheCompileIfNeeded that compares the
416 * precompiled output.
417 *
418 * @returns 1 if matching, 0 if not matching.
419 * @param pEntry The entry containing the names of the files to compare.
420 * The entry is not updated in any way.
421 */
422static int kObjCacheCompareOldAndNewOutput(PCKOBJCACHE pEntry)
423{
424 /** @todo do some quick but fancy comparing that determins whether code
425 * has actually changed or moved. We can ignore declarations and typedefs that
426 * has just been moved up/down a bit. The typical case is adding a new error
427 * #define that doesn't influence the current compile job. */
428 return 0;
429}
430
431
432/**
433 * Worker for kObjCacheCompileIfNeeded that does the actual (re)compilation.
434 *
435 * @returns 1 if matching, 0 if not matching.
436 * @param pEntry The cache entry.
437 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL.
438 * @param cArgvCompile The number of arguments in the vector.
439 * @param pszObjName The name of the object file.
440 */
441static void kObjCacheCompileIt(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName)
442{
443 /*
444 * Delete the old object file and precompiler output.
445 */
446 if (pEntry->pszObjName)
447 {
448 UnlinkFileInDir(pEntry->pszObjName, pEntry->pszDir);
449 pEntry->pszObjName = NULL;
450 }
451 pEntry->pszNewObjName = CalcRelativeName(pEntry->pszDir, pszObjName);
452
453 /*
454 * Release buffers we no longer need before starting the compile.
455 */
456 free(pEntry->pszNewCppMapping);
457 pEntry->pszNewCppMapping = NULL;
458 free(pEntry->pszOldCppMapping);
459 pEntry->pszOldCppMapping = NULL;
460
461 /*
462 * Do the recompilation.
463 */
464 pEntry->papszArgvCompile = papszArgvCompile;
465 pEntry->cArgvCompile = cArgvCompile;
466 kObjCacheSpawn(pEntry, papszArgvCompile, cArgvCompile, "compile");
467}
468
469
470/**
471 * Check if (re-)compilation is required and do it.
472 *
473 * @returns 1 if matching, 0 if not matching.
474 * @param pEntry The cache entry.
475 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL.
476 * @param cArgvCompile The number of arguments in the vector.
477 * @param pszObjName The name of the object file.
478 */
479static void kObjCacheCompileIfNeeded(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName)
480{
481 /*
482 * Does the object name differ?
483 */
484 if ( !pEntry->fNeedCompiling
485 && strcmp(FindFilenameInPath(pszObjName), pEntry->pszObjName))
486 {
487 pEntry->fNeedCompiling = 1;
488 kObjCacheVerbose(pEntry, "object name changed\n");
489 }
490
491 /*
492 * Does the compile command differ?
493 * TODO: Ignore irrelevant options here (like warning level).
494 */
495 if ( !pEntry->fNeedCompiling
496 && pEntry->cArgvCompile != cArgvCompile)
497 {
498 pEntry->fNeedCompiling = 1;
499 kObjCacheVerbose(pEntry, "compile argument count changed\n");
500 }
501 if (!pEntry->fNeedCompiling)
502 {
503 unsigned i;
504 for (i = 0; i < cArgvCompile; i++)
505 if (strcmp(papszArgvCompile[i], pEntry->papszArgvCompile[i]))
506 {
507 pEntry->fNeedCompiling = 1;
508 kObjCacheVerbose(pEntry, "compile argument differs (%#d)\n", i);
509 break;
510 }
511 }
512
513 /*
514 * Does the object file exist?
515 */
516 if (!DoesFileInDirExist(pEntry->pszObjName, pEntry->pszDir))
517 {
518 pEntry->fNeedCompiling = 1;
519 kObjCacheVerbose(pEntry, "object file doesn't exist\n");
520 }
521
522 /*
523 * Does the precompiled output differ in any significant way?
524 */
525 if (!pEntry->fNeedCompiling)
526 {
527 int fFound = 0;
528 PCKOCSUM pSum;
529 for (pSum = &pEntry->SumHead; pSum; pSum = pSum->pNext)
530 if (kObjCacheSumIsEqual(pSum, &pEntry->NewSum))
531 {
532 fFound = 1;
533 break;
534 }
535 if (!fFound)
536 {
537 kObjCacheVerbose(pEntry, "no checksum match - comparing output\n");
538 if (!kObjCacheCompareOldAndNewOutput(pEntry))
539 pEntry->fNeedCompiling = 1;
540 else
541 {
542 /* insert the sum into the list. */
543 pEntry->NewSum.pNext = pEntry->SumHead.pNext;
544 pEntry->SumHead.pNext = &pEntry->NewSum;
545 }
546 }
547 }
548
549 /*
550 * Discard the old precompiled output it's no longer needed.s
551 */
552 if (pEntry->pszOldCppName)
553 {
554 UnlinkFileInDir(pEntry->pszOldCppName, pEntry->pszDir);
555 free(pEntry->pszOldCppName);
556 pEntry->pszOldCppName = NULL;
557 }
558
559 /*
560 * Do the compliation if found necessary.
561 */
562 if (pEntry->fNeedCompiling)
563 kObjCacheCompileIt(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
564}
565
566
567/**
568 * Utility function that finds the filename part in a path.
569 *
570 * @returns Pointer to the file name part (this may be "").
571 * @param pszPath The path to parse.
572 */
573static const char *FindFilenameInPath(const char *pszPath)
574{
575 const char *pszFilename = strchr(pszPath, '\0') - 1;
576 while ( pszFilename > pszPath
577#if defined(__OS2__) || defined(__WIN__)
578 && pszFilename[-1] != ':' && pszFilename[-1] != '/' && pszFilename[-1] != '\\')
579#else
580 && pszFilename[-1] != '/')
581#endif
582 pszFilename--;
583 return pszFilename;
584}
585
586
587/**
588 * Utility function that combines a filename and a directory into a path.
589 *
590 * @returns malloced buffer containing the result.
591 * @param pszName The file name.
592 * @param pszDir The directory path.
593 */
594static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)
595{
596 size_t cchName = strlen(pszName);
597 size_t cchDir = strlen(pszDir);
598 char *pszBuf = xmalloc(cchName + cchDir + 2);
599 memcpy(pszBuf, pszDir, cchDir);
600#if defined(__OS2__) || defined(__WIN__)
601 if (cchDir > 0 && pszDir[cchDir - 1] != '/' && pszDir[cchDir - 1] != '\\' && pszDir[cchDir - 1] != ':')
602#else
603 if (cchDir > 0 && pszDir[cchDir - 1] != '/')
604#endif
605 pszBuf[cchDir++] = '/';
606 memcpy(pszBuf + cchDir, pszName, cchName + 1);
607 return pszBuf;
608}
609
610
611/**
612 * Calculate how to get to pszPath from pszDir.
613 *
614 * @returns The relative path from pszDir to path pszPath.
615 * @param pszPath The path to the object.
616 * @param pszDir The directory it shall be relative to.
617 */
618static char *CalcRelativeName(const char *pszPath, const char *pszDir)
619{
620 size_t cchDir = strlen(pszDir);
621#if defined(__OS2__) || defined(__WIN__)
622 int fMatches;
623#endif
624
625 /*
626 * This is indeed a bit tricky, so we'll try the easy way first...
627 */
628#if defined(__OS2__) || defined(__WIN__)
629 fMatches = strnicmp(pszPath, pszDir, cchDir);
630 if (!fMatches)
631 {
632 /* Slashes may differ, compare char by char. */
633 const char *psz1 = pszDir;
634 const char *psz2 = pszPath;
635 fMatches = 1;
636 for (;; psz1++, psz2++)
637 {
638 if (*psz1 != *psz2)
639 {
640 if (!*psz1) /* dir */
641 break;
642 if ( tolower(*psz1) != tolower(*psz2)
643 && toupper(*psz1) != toupper(*psz2)
644 && *psz1 != '/'
645 && *psz1 != '\\'
646 && *psz2 != '/'
647 && *psz2 != '\\')
648 {
649 fMatches = 0;
650 break;
651 }
652 }
653 }
654 }
655 if (fMatches)
656#else
657 if (!strncmp(pszPath, pszDir, cchDir))
658#endif
659 {
660 if (pszPath[cchDir])
661 return xstrdup(pszPath + cchDir);
662 return xstrdup("./");
663 }
664
665 /*
666 * Damn, it's gonna be complicated. Deal with that later.
667 */
668 fprintf(stderr, "kObjCache: complicated relative path stuff isn't implemented yet. sorry.\n");
669 exit(1);
670 return NULL;
671}
672
673
674/**
675 * Utility function that combines a filename and directory and passes it onto fopen.
676 *
677 * @returns fopen return value.
678 * @param pszName The file name.
679 * @param pszDir The directory path.
680 * @param pszMode The fopen mode string.
681 */
682static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)
683{
684 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
685 FILE *pFile = fopen(pszPath, pszMode);
686 free(pszPath);
687 return pFile;
688}
689
690
691/**
692 * Deletes a file in a directory.
693 *
694 * @returns whatever unlink returns.
695 * @param pszName The file name.
696 * @param pszDir The directory path.
697 */
698static int UnlinkFileInDir(const char *pszName, const char *pszDir)
699{
700 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
701 int rc = unlink(pszPath);
702 free(pszPath);
703 return rc;
704}
705
706
707/**
708 * Renames a file in a directory.
709 *
710 * @returns whatever unlink returns.
711 * @param pszOldName The new file name.
712 * @param pszNewName The old file name.
713 * @param pszDir The directory path.
714 */
715static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)
716{
717 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);
718 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);
719 int rc = rename(pszOldName, pszNewName);
720 free(pszOldPath);
721 free(pszNewPath);
722 return rc;
723}
724
725
726/**
727 * Check if a (regular) file exists in a directory.
728 *
729 * @returns 1 if it exists and is a regular file, 0 if not.
730 * @param pszName The file name.
731 * @param pszDir The directory path.
732 */
733static int DoesFileInDirExist(const char *pszName, const char *pszDir)
734{
735 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
736 struct stat st;
737 int rc = stat(pszPath, &st);
738 free(pszPath);
739#ifdef S_ISREG
740 return !rc && S_ISREG(st.st_mode);
741#elif defined(_MSC_VER)
742 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;
743#else
744#error "Port me"
745#endif
746}
747
748
749/**
750 * Reads into memory an entire file.
751 *
752 * @returns Pointer to the heap allocation containing the file.
753 * On failure NULL and errno is returned.
754 * @param pszName The file.
755 * @param pszDir The directory the file resides in.
756 * @param pcbFile Where to store the file size.
757 */
758static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)
759{
760 int SavedErrno;
761 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
762 int fd = open(pszName, O_RDONLY | O_BINARY);
763 if (fd >= 0)
764 {
765 off_t cbFile = lseek(fd, 0, SEEK_END);
766 if ( cbFile >= 0
767 && lseek(fd, 0, SEEK_SET) == 0)
768 {
769 char *pb = malloc(cbFile + 1);
770 if (pb)
771 {
772 if (read(fd, pb, cbFile) == cbFile)
773 {
774 close(fd);
775 pb[cbFile] = '\0';
776 *pcbFile = (size_t)cbFile;
777 return pb;
778 }
779 SavedErrno = errno;
780 free(pb);
781 }
782 else
783 SavedErrno = ENOMEM;
784 }
785 else
786 SavedErrno = errno;
787 close(fd);
788 }
789 else
790 SavedErrno = errno;
791 free(pszPath);
792 errno = SavedErrno;
793 return NULL;
794}
795
796
797
798/**
799 * Prints a syntax error and returns the appropriate exit code
800 *
801 * @returns approriate exit code.
802 * @param pszFormat The syntax error message.
803 * @param ... Message args.
804 */
805static int SyntaxError(const char *pszFormat, ...)
806{
807 va_list va;
808 fprintf(stderr, "kObjCache: syntax error: ");
809 va_start(va, pszFormat);
810 vfprintf(stderr, pszFormat, va);
811 va_end(va);
812 return 1;
813}
814
815
816/**
817 * Prints the usage.
818 * @returns 0.
819 */
820static int usage(void)
821{
822 printf("syntax: kObjCache [-v|--verbose] [-f|--file] <cache-file> [-V|--version]\n"
823 " --kObjCache-cpp <filename> <precompiler + args> \n"
824 " --kObjCache-cc <object> <compiler + args>\n"
825 " [--kObjCache-both [args]]\n"
826 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n"
827 "\n");
828 return 0;
829}
830
831
832int main(int argc, char **argv)
833{
834 PKOBJCACHE pEntry;
835
836 const char *pszCacheFile;
837
838 const char **papszArgvPreComp = NULL;
839 unsigned cArgvPreComp = 0;
840 const char *pszPreCompName = NULL;
841
842 const char **papszArgvCompile = NULL;
843 unsigned cArgvCompile = 0;
844 const char *pszObjName = NULL;
845
846 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
847
848 int i;
849
850 /*
851 * Parse the arguments.
852 */
853 for (i = 1; i < argc; i++)
854 {
855 if (!strcmp(argv[i], "--kObjCache-cpp"))
856 {
857 enmMode = kOC_CppArgv;
858 if (!pszPreCompName)
859 {
860 if (++i >= argc)
861 return SyntaxError("--kObjCache-cpp requires an object filename!\n");
862 pszPreCompName = argv[i];
863 }
864 }
865 else if (!strcmp(argv[i], "--kObjCache-cc"))
866 {
867 enmMode = kOC_CcArgv;
868 if (pszObjName)
869 {
870 if (++i >= argc)
871 return SyntaxError("--kObjCache-cc requires an precompiler output filename!\n");
872 pszObjName = argv[i];
873 }
874 }
875 else if (!strcmp(argv[i], "--kObjCache-both"))
876 enmMode = kOC_BothArgv;
877 else if (!strcmp(argv[i], "--help"))
878 return usage();
879 else if (enmMode != kOC_Options)
880 {
881 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
882 {
883 if (!(cArgvPreComp % 16))
884 {
885 cArgvPreComp += 16;
886 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 2) * sizeof(papszArgvPreComp[0]));
887 }
888 papszArgvPreComp[cArgvPreComp++] = argv[i];
889 papszArgvPreComp[cArgvPreComp] = NULL;
890 }
891 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
892 {
893 if (!(cArgvCompile % 16))
894 {
895 cArgvCompile += 16;
896 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 2) * sizeof(papszArgvCompile[0]));
897 }
898 papszArgvCompile[cArgvCompile++] = argv[i];
899 papszArgvCompile[cArgvCompile] = NULL;
900 }
901 }
902 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file"))
903 {
904 if (i + 1 >= argc)
905 return SyntaxError("%s requires a cache filename!\n", argv[i]);
906 pszCacheFile = argv[++i];
907 }
908 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
909 g_fVerbose = 1;
910 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
911 g_fVerbose = 0;
912 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?"))
913 return usage();
914 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
915 {
916 printf("kObjCache v0.0.0 ($Revision: 1001 $)\n");
917 return 0;
918 }
919 else
920 return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
921 }
922 if (!pszCacheFile)
923 return SyntaxError("No cache file name (-f)\n");
924 if (!cArgvCompile)
925 return SyntaxError("No compiler arguments (--kObjCache-cc)\n");
926 if (!cArgvPreComp)
927 return SyntaxError("No precompiler arguments (--kObjCache-cc)\n");
928
929 /*
930 * Create a cache entry from the cache file (if found).
931 */
932 pEntry = kObjCacheCreate(pszCacheFile);
933 kObjCacheRead(pEntry);
934
935 /*
936 * Do the compiling.
937 */
938 kObjCachePreCompile(pEntry, papszArgvPreComp, cArgvPreComp, pszPreCompName);
939 kObjCacheCompileIfNeeded(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
940
941 /*
942 * Write the cache file.
943 */
944 kObjCacheWrite(pEntry);
945 //kObjCacheCleanup(pEntry);
946 /* kObjCacheDestroy(pEntry); - don't bother */
947 return 0;
948}
949
950
Note: See TracBrowser for help on using the repository browser.