Changeset 1040 in kBuild
- Timestamp:
- Jun 8, 2007 2:08:10 AM (18 years ago)
- Location:
- trunk/src/kObjCache
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kObjCache/Makefile.kmk
r1001 r1040 29 29 include $(PATH_KBUILD)/subheader.kmk 30 30 31 ifeq ($(USERNAME),bird) # disable it for now. 31 32 PROGRAMS += kObjCache 33 endif 32 34 kObjCache_TEMPLATE = BIN 33 35 kObjCache_SOURCES = kObjCache.c -
trunk/src/kObjCache/kObjCache.c
r1038 r1040 48 48 # include <unistd.h> 49 49 # endif 50 # if defined(_MSC_VER) 51 typedef intptr_t pid_t; 52 # endif 50 53 #else 51 54 # include <unistd.h> … … 55 58 # endif 56 59 #endif 60 #if defined(__WIN__) 61 # include <Windows.h> 62 #endif 63 57 64 #include "crc32.h" 58 65 #include "md5.h" 66 59 67 60 68 /******************************************************************************* … … 75 83 # define IS_SLASH_DRV(ch) ((ch) == '/') 76 84 #endif 77 /** Use pipe instead of temp files when possible (speed). */78 #define USE_PIPE 179 80 81 82 /*******************************************************************************83 * Structures and Typedefs *84 *******************************************************************************/85 /** A checksum list entry.86 * We keep a list checksums (of precompiler output) that matches, The planned87 * matching algorithm doesn't require the precompiler output to be indentical,88 * only to produce the same object files.89 */90 typedef struct KOCSUM91 {92 /** The next checksum. */93 struct KOCSUM *pNext;94 /** The crc32 checksum. */95 uint32_t crc32;96 /** The MD5 digest. */97 unsigned char md5[16];98 } KOCSUM, *PKOCSUM;99 /** Pointer to a const KOCSUM. */100 typedef const KOCSUM *PCKOCSUM;101 102 /**103 * The object cache data.104 */105 typedef struct KOBJCACHE106 {107 /** The cache dir that all other names are relative to. */108 char *pszDir;109 /** The name of the cache file. */110 const char *pszName;111 /** Set if the object needs to be (re)compiled. */112 unsigned fNeedCompiling;113 /** Whether the precompiler runs in piped mode. If clear it's file114 * mode (it could be redirected stdout, but that's essentially the115 * same from our point of view). */116 unsigned fPiped;117 118 /** The name of new precompiled output. */119 const char *pszNewCppName;120 /** Pointer to the 'mapping' of the new precompiled output. */121 char *pszNewCppMapping;122 /** The size of the new precompiled output 'mapping'. */123 size_t cbNewCpp;124 /** The new checksum. */125 KOCSUM NewSum;126 /** The new object filename (relative to the cache file). */127 char *pszNewObjName;128 129 /** The name of the precompiled output. (relative to the cache file) */130 char *pszOldCppName;131 /** Pointer to the 'mapping' of the old precompiled output. */132 char *pszOldCppMapping;133 /** The size of the old precompiled output. */134 size_t cbOldCpp;135 136 /** The head of the checksum list. */137 KOCSUM SumHead;138 /** The object filename (relative to the cache file). */139 char *pszObjName;140 /** The compile argument vector used to build the object. */141 char **papszArgvCompile;142 /** The size of the compile */143 unsigned cArgvCompile;144 } KOBJCACHE, *PKOBJCACHE;145 /** Pointer to a const KOBJCACHE. */146 typedef const KOBJCACHE *PCKOBJCACHE;147 85 148 86 … … 151 89 *******************************************************************************/ 152 90 /** Whether verbose output is enabled. */ 153 static int g_fVerbose = 0; 91 static unsigned g_cVerbosityLevel = 0; 92 /** What to prefix the errors with. */ 93 static char g_szErrorPrefix[128]; 94 95 /** Read buffer shared by the cache components. */ 96 static char g_szLine[KOBJCACHE_MAX_LINE_LEN + 16]; 154 97 155 98 … … 157 100 * Internal Functions * 158 101 *******************************************************************************/ 159 static const char *FindFilenameInPath(const char *pszPath);160 static char *AbsPath(const char *pszPath);161 102 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir); 162 103 static char *CalcRelativeName(const char *pszPath, const char *pszDir); … … 166 107 static int DoesFileInDirExist(const char *pszName, const char *pszDir); 167 108 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile); 168 static void *xmalloc(size_t); 169 static void *xrealloc(void *, size_t); 170 static char *xstrdup(const char *); 109 110 111 void FatalMsg(const char *pszFormat, ...) 112 { 113 va_list va; 114 115 if (g_szErrorPrefix[0]) 116 fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix); 117 else 118 fprintf(stderr, "fatal error: "); 119 120 va_start(va, pszFormat); 121 vfprintf(stderr, pszFormat, va); 122 va_end(va); 123 } 124 125 126 void FatalDie(const char *pszFormat, ...) 127 { 128 va_list va; 129 130 if (g_szErrorPrefix[0]) 131 fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix); 132 else 133 fprintf(stderr, "fatal error: "); 134 135 va_start(va, pszFormat); 136 vfprintf(stderr, pszFormat, va); 137 va_end(va); 138 139 exit(1); 140 } 141 142 143 static void ErrorMsg(const char *pszFormat, ...) 144 { 145 va_list va; 146 147 if (g_szErrorPrefix[0]) 148 fprintf(stderr, "%s - error: ", g_szErrorPrefix); 149 else 150 fprintf(stderr, "error: "); 151 152 va_start(va, pszFormat); 153 vfprintf(stderr, pszFormat, va); 154 va_end(va); 155 } 156 157 158 static void InfoMsg(unsigned uLevel, const char *pszFormat, ...) 159 { 160 if (uLevel <= g_cVerbosityLevel) 161 { 162 va_list va; 163 164 if (g_szErrorPrefix[0]) 165 fprintf(stderr, "%s - info: ", g_szErrorPrefix); 166 else 167 fprintf(stderr, "info: "); 168 169 va_start(va, pszFormat); 170 vfprintf(stderr, pszFormat, va); 171 va_end(va); 172 } 173 } 174 175 176 static void SetErrorPrefix(const char *pszPrefix, ...) 177 { 178 int cch; 179 va_list va; 180 181 va_start(va, pszPrefix); 182 #if defined(_MSC_VER) || defined(__sun__) 183 cch = vsprintf(g_szErrorPrefix, pszPrefix, va); 184 if (cch >= sizeof(g_szErrorPrefix)) 185 FatalDie("Buffer overflow setting error prefix!\n"); 186 #else 187 vsnprintf(g_szErrorPrefix, sizeof(g_szErrorPrefix), pszPrefix, va); 188 #endif 189 va_end(va); 190 (void)cch; 191 } 192 193 194 void *xmalloc(size_t cb) 195 { 196 void *pv = malloc(cb); 197 if (!pv) 198 FatalDie(NULL, "out of memory (%d)\n", (int)cb); 199 return pv; 200 } 201 202 203 void *xmallocz(size_t cb) 204 { 205 void *pv = xmalloc(cb); 206 memset(pv, 0, cb); 207 return pv; 208 } 209 210 211 void *xrealloc(void *pvOld, size_t cb) 212 { 213 void *pv = realloc(pvOld, cb); 214 if (!pv) 215 FatalDie(NULL, "out of memory (%d)\n", (int)cb); 216 return pv; 217 } 218 219 220 char *xstrdup(const char *pszIn) 221 { 222 char *psz = strdup(pszIn); 223 if (!psz) 224 FatalDie(NULL, "out of memory (%d)\n", (int)strlen(pszIn)); 225 return psz; 226 } 227 228 229 230 /** 231 * Gets the absolute path 232 * 233 * @returns A new heap buffer containing the absolute path. 234 * @param pszPath The path to make absolute. (Readonly) 235 */ 236 static char *AbsPath(const char *pszPath) 237 { 238 char szTmp[PATH_MAX]; 239 #if defined(__OS2__) || defined(__WIN__) 240 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))) 241 return xstrdup(pszPath); 242 #else 243 if (!realpath(pszPath, szTmp)) 244 return xstrdup(pszPath); 245 #endif 246 return xstrdup(szTmp); 247 } 248 249 250 /** 251 * Utility function that finds the filename part in a path. 252 * 253 * @returns Pointer to the file name part (this may be ""). 254 * @param pszPath The path to parse. 255 */ 256 static const char *FindFilenameInPath(const char *pszPath) 257 { 258 const char *pszFilename = strchr(pszPath, '\0') - 1; 259 while ( pszFilename > pszPath 260 && !IS_SLASH_DRV(pszFilename[-1])) 261 pszFilename--; 262 return pszFilename; 263 } 264 265 266 /** 267 * Utility function that combines a filename and a directory into a path. 268 * 269 * @returns malloced buffer containing the result. 270 * @param pszName The file name. 271 * @param pszDir The directory path. 272 */ 273 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir) 274 { 275 size_t cchName = strlen(pszName); 276 size_t cchDir = strlen(pszDir); 277 char *pszBuf = xmalloc(cchName + cchDir + 2); 278 memcpy(pszBuf, pszDir, cchDir); 279 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1])) 280 pszBuf[cchDir++] = PATH_SLASH; 281 memcpy(pszBuf + cchDir, pszName, cchName + 1); 282 return pszBuf; 283 } 284 285 286 /** 287 * Compares two path strings to see if they are identical. 288 * 289 * This doesn't do anything fancy, just the case ignoring and 290 * slash unification. 291 * 292 * @returns 1 if equal, 0 otherwise. 293 * @param pszPath1 The first path. 294 * @param pszPath2 The second path. 295 * @param cch The number of characters to compare. 296 */ 297 static int ArePathsIdentical(const char *pszPath1, const char *pszPath2, size_t cch) 298 { 299 #if defined(__OS2__) || defined(__WIN__) 300 if (strnicmp(pszPath1, pszPath2, cch)) 301 { 302 /* Slashes may differ, compare char by char. */ 303 const char *psz1 = pszPath1; 304 const char *psz2 = pszPath2; 305 for (;cch; psz1++, psz2++, cch--) 306 { 307 if (*psz1 != *psz2) 308 { 309 if ( tolower(*psz1) != tolower(*psz2) 310 && toupper(*psz1) != toupper(*psz2) 311 && *psz1 != '/' 312 && *psz1 != '\\' 313 && *psz2 != '/' 314 && *psz2 != '\\') 315 return 0; 316 } 317 } 318 } 319 return 1; 320 #else 321 return !strncmp(pszPath1, pszPath2, cch); 322 #endif 323 } 324 325 326 /** 327 * Calculate how to get to pszPath from pszDir. 328 * 329 * @returns The relative path from pszDir to path pszPath. 330 * @param pszPath The path to the object. 331 * @param pszDir The directory it shall be relative to. 332 */ 333 static char *CalcRelativeName(const char *pszPath, const char *pszDir) 334 { 335 char *pszRet = NULL; 336 char *pszAbsPath = NULL; 337 size_t cchDir = strlen(pszDir); 338 339 /* 340 * This is indeed a bit tricky, so we'll try the easy way first... 341 */ 342 if (ArePathsIdentical(pszPath, pszDir, cchDir)) 343 { 344 if (pszPath[cchDir]) 345 pszRet = (char *)pszPath + cchDir; 346 else 347 pszRet = "./"; 348 } 349 else 350 { 351 pszAbsPath = AbsPath(pszPath); 352 if (ArePathsIdentical(pszAbsPath, pszDir, cchDir)) 353 { 354 if (pszPath[cchDir]) 355 pszRet = pszAbsPath + cchDir; 356 else 357 pszRet = "./"; 358 } 359 } 360 if (pszRet) 361 { 362 while (IS_SLASH_DRV(*pszRet)) 363 pszRet++; 364 pszRet = xstrdup(pszRet); 365 free(pszAbsPath); 366 return pszRet; 367 } 368 369 /* 370 * Damn, it's gonna be complicated. Deal with that later. 371 */ 372 FatalDie("complicated relative path stuff isn't implemented yet. sorry.\n"); 373 return NULL; 374 } 375 376 377 /** 378 * Utility function that combines a filename and directory and passes it onto fopen. 379 * 380 * @returns fopen return value. 381 * @param pszName The file name. 382 * @param pszDir The directory path. 383 * @param pszMode The fopen mode string. 384 */ 385 static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode) 386 { 387 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 388 FILE *pFile = fopen(pszPath, pszMode); 389 free(pszPath); 390 return pFile; 391 } 392 393 394 /** 395 * Utility function that combines a filename and directory and passes it onto open. 396 * 397 * @returns open return value. 398 * @param pszName The file name. 399 * @param pszDir The directory path. 400 * @param fFlags The open flags. 401 * @param fCreateMode The file creation mode. 402 */ 403 static int OpenFileInDir(const char *pszName, const char *pszDir, int fFlags, int fCreateMode) 404 { 405 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 406 int fd = open(pszPath, fFlags, fCreateMode); 407 free(pszPath); 408 return fd; 409 } 410 411 412 413 /** 414 * Deletes a file in a directory. 415 * 416 * @returns whatever unlink returns. 417 * @param pszName The file name. 418 * @param pszDir The directory path. 419 */ 420 static int UnlinkFileInDir(const char *pszName, const char *pszDir) 421 { 422 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 423 int rc = unlink(pszPath); 424 free(pszPath); 425 return rc; 426 } 427 428 429 /** 430 * Renames a file in a directory. 431 * 432 * @returns whatever rename returns. 433 * @param pszOldName The new file name. 434 * @param pszNewName The old file name. 435 * @param pszDir The directory path. 436 */ 437 static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir) 438 { 439 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir); 440 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir); 441 int rc = rename(pszOldPath, pszNewPath); 442 free(pszOldPath); 443 free(pszNewPath); 444 return rc; 445 } 446 447 448 /** 449 * Check if a (regular) file exists in a directory. 450 * 451 * @returns 1 if it exists and is a regular file, 0 if not. 452 * @param pszName The file name. 453 * @param pszDir The directory path. 454 */ 455 static int DoesFileInDirExist(const char *pszName, const char *pszDir) 456 { 457 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 458 struct stat st; 459 int rc = stat(pszPath, &st); 460 free(pszPath); 461 #ifdef S_ISREG 462 return !rc && S_ISREG(st.st_mode); 463 #elif defined(_MSC_VER) 464 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG; 465 #else 466 #error "Port me" 467 #endif 468 } 469 470 471 /** 472 * Reads into memory an entire file. 473 * 474 * @returns Pointer to the heap allocation containing the file. 475 * On failure NULL and errno is returned. 476 * @param pszName The file. 477 * @param pszDir The directory the file resides in. 478 * @param pcbFile Where to store the file size. 479 */ 480 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile) 481 { 482 int SavedErrno; 483 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 484 int fd = open(pszPath, O_RDONLY | O_BINARY); 485 if (fd >= 0) 486 { 487 off_t cbFile = lseek(fd, 0, SEEK_END); 488 if ( cbFile >= 0 489 && lseek(fd, 0, SEEK_SET) == 0) 490 { 491 char *pb = malloc(cbFile + 1); 492 if (pb) 493 { 494 if (read(fd, pb, cbFile) == cbFile) 495 { 496 close(fd); 497 pb[cbFile] = '\0'; 498 *pcbFile = (size_t)cbFile; 499 return pb; 500 } 501 SavedErrno = errno; 502 free(pb); 503 } 504 else 505 SavedErrno = ENOMEM; 506 } 507 else 508 SavedErrno = errno; 509 close(fd); 510 } 511 else 512 SavedErrno = errno; 513 free(pszPath); 514 errno = SavedErrno; 515 return NULL; 516 } 517 518 519 /** 520 * Creates a directory including all necessary parent directories. 521 * 522 * @returns 0 on success, -1 + errno on failure. 523 * @param pszDir The directory. 524 */ 525 static int MakePath(const char *pszPath) 526 { 527 /** @todo implement me */ 528 return 0; 529 } 530 531 532 /** 533 * Adds the arguments found in the pszCmdLine string to argument vector. 534 * 535 * The parsing of the pszCmdLine string isn't very sophisticated, no 536 * escaping or quotes. 537 * 538 * @param pcArgs Pointer to the argument counter. 539 * @param ppapszArgs Pointer to the argument vector pointer. 540 * @param pszCmdLine The command line to parse and append. 541 * @param pszWedgeArg Argument to put infront of anything found in pszCmdLine. 542 */ 543 static void AppendArgs(int *pcArgs, char ***ppapszArgs, const char *pszCmdLine, const char *pszWedgeArg) 544 { 545 int i; 546 int cExtraArgs; 547 const char *psz; 548 char **papszArgs; 549 550 /* 551 * Count the new arguments. 552 */ 553 cExtraArgs = 0; 554 psz = pszCmdLine; 555 while (*psz) 556 { 557 while (isspace(*psz)) 558 psz++; 559 if (!psz) 560 break; 561 cExtraArgs++; 562 while (!isspace(*psz) && *psz) 563 psz++; 564 } 565 if (!cExtraArgs) 566 return; 567 568 /* 569 * Allocate a new vector that can hold the arguments. 570 * (Reallocating might not work since the argv might not be allocated 571 * from the heap but off the stack or somewhere... ) 572 */ 573 i = *pcArgs; 574 *pcArgs = i + cExtraArgs + 1 + !!pszWedgeArg; 575 papszArgs = xmalloc(*pcArgs * sizeof(char *)); 576 *ppapszArgs = memcpy(papszArgs, *ppapszArgs, i * sizeof(char *)); 577 578 if (pszWedgeArg) 579 papszArgs[i++] = xstrdup(pszWedgeArg); 580 581 psz = pszCmdLine; 582 while (*psz) 583 { 584 const char *pszEnd; 585 while (isspace(*psz)) 586 psz++; 587 if (!psz) 588 break; 589 pszEnd = psz; 590 while (!isspace(*pszEnd) && *pszEnd) 591 pszEnd++; 592 593 papszArgs[i] = xmalloc(psz - pszEnd + 1); 594 memcpy(papszArgs[i], psz, psz - pszEnd); 595 papszArgs[i][psz - pszEnd] = '\0'; 596 i++; 597 } 598 } 599 600 601 602 603 604 /** A checksum list entry. 605 * We keep a list checksums (of precompiler output) that matches, The planned 606 * matching algorithm doesn't require the precompiler output to be indentical, 607 * only to produce the same object files. 608 */ 609 typedef struct KOCSUM 610 { 611 /** The next checksum. */ 612 struct KOCSUM *pNext; 613 /** The crc32 checksum. */ 614 uint32_t crc32; 615 /** The MD5 digest. */ 616 unsigned char md5[16]; 617 /** Valid or not. */ 618 unsigned fUsed; 619 } KOCSUM; 620 /** Pointer to a KOCSUM. */ 621 typedef KOCSUM *PKOCSUM; 622 /** Pointer to a const KOCSUM. */ 623 typedef const KOCSUM *PCKOCSUM; 624 625 626 /** 627 * Temporary context record used when calculating 628 * the checksum of some data. 629 */ 630 typedef struct KOCSUMCTX 631 { 632 /** The MD5 context. */ 633 struct MD5Context MD5Ctx; 634 } KOCSUMCTX; 635 /** Pointer to a check context record. */ 636 typedef KOCSUMCTX *PKOCSUMCTX; 637 638 639 640 /** 641 * Initializes a checksum object with an associated context. 642 * 643 * @param pSum The checksum object. 644 * @param pCtx The checksum context. 645 */ 646 static void kOCSumInitWithCtx(PKOCSUM pSum, PKOCSUMCTX pCtx) 647 { 648 memcmp(pSum, 0, sizeof(*pSum)); 649 MD5Init(&pCtx->MD5Ctx); 650 } 651 652 653 /** 654 * Updates the checksum calculation. 655 * 656 * @param pSum The checksum. 657 * @param pCtx The checksum calcuation context. 658 * @param pvBuf The input data to checksum. 659 * @param cbBuf The size of the input data. 660 */ 661 static void kOCSumUpdate(PKOCSUM pSum, PKOCSUMCTX pCtx, const void *pvBuf, size_t cbBuf) 662 { 663 /* 664 * Take in relativly small chunks to try keep it in the cache. 665 */ 666 const unsigned char *pb = (const unsigned char *)pvBuf; 667 while (cbBuf > 0) 668 { 669 size_t cb = cbBuf >= 128*1024 ? 128*1024 : cbBuf; 670 pSum->crc32 = crc32(pSum->crc32, pb, cb); 671 MD5Update(&pCtx->MD5Ctx, pb, cb); 672 cbBuf -= cb; 673 } 674 } 675 676 677 /** 678 * Finalizes a checksum calculation. 679 * 680 * @param pSum The checksum. 681 * @param pCtx The checksum calcuation context. 682 */ 683 static void kOCSumFinalize(PKOCSUM pSum, PKOCSUMCTX pCtx) 684 { 685 MD5Final(&pSum->md5[0], &pCtx->MD5Ctx); 686 } 687 688 689 /** 690 * Init a check sum chain head. 691 * 692 * @param pSumHead The checksum head to init. 693 */ 694 static void kOCSumInit(PKOCSUM pSumHead) 695 { 696 memcmp(pSumHead, 0, sizeof(*pSumHead)); 697 } 698 699 700 /** 701 * Parses the given string into a checksum head object. 702 * 703 * @returns 0 on success, -1 on format error. 704 * @param pSumHead The checksum head to init. 705 * @param pszVal The string to initialized it from. 706 */ 707 static int kOCSumInitFromString(PKOCSUM pSumHead, const char *pszVal) 708 { 709 unsigned i; 710 char *pszNext; 711 char *pszMD5; 712 713 memset(pSumHead, 0, sizeof(*pSumHead)); 714 715 pszMD5 = strchr(pszVal, ':'); 716 if (pszMD5 == NULL) 717 return -1; 718 *pszMD5++ = '\0'; 719 720 /* crc32 */ 721 pSumHead->crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16); 722 if (pszNext && *pszNext) 723 return -1; 724 725 /* md5 */ 726 for (i = 0; i < sizeof(pSumHead->md5) * 2; i++) 727 { 728 unsigned char ch = pszMD5[i]; 729 int x; 730 if ((unsigned char)(ch - '0') <= 9) 731 x = ch - '0'; 732 else if ((unsigned char)(ch - 'a') <= 5) 733 x = ch - 'a' + 10; 734 else if ((unsigned char)(ch - 'A') <= 5) 735 x = ch - 'A' + 10; 736 else 737 return -1; 738 if (!(i & 1)) 739 pSumHead->md5[i >> 1] = x << 4; 740 else 741 pSumHead->md5[i >> 1] |= x; 742 } 743 744 pSumHead->fUsed = 1; 745 return 0; 746 } 747 748 749 /** 750 * Delete a check sum chain. 751 * 752 * @param pSumHead The head of the checksum chain. 753 */ 754 static void kOCSumDeleteChain(PKOCSUM pSumHead) 755 { 756 void *pv; 757 while ((pv = pSumHead->pNext)) 758 { 759 pSumHead = pSumHead->pNext; 760 free(pv); 761 } 762 memcmp(pSumHead, 0, sizeof(*pSumHead)); 763 } 764 765 766 /** 767 * Insert a check sum into the chain. 768 * 769 * @param pSumHead The head of the checksum list. 770 * @param pSumAdd The checksum to add (duplicate). 771 */ 772 static void kOCSumAdd(PKOCSUM pSumHead, PCKOCSUM pSumAdd) 773 { 774 if (pSumHead->fUsed) 775 { 776 PKOCSUM pNew = xmalloc(sizeof(*pNew)); 777 *pNew = *pSumAdd; 778 pNew->pNext = pSumHead->pNext; 779 pNew->fUsed = 1; 780 pSumHead->pNext = pNew; 781 } 782 else 783 { 784 *pSumHead = *pSumAdd; 785 pSumHead->pNext = NULL; 786 pSumHead->fUsed = 1; 787 } 788 } 789 790 791 /** 792 * Inserts an entrie chain into the given check sum chain. 793 * 794 * @param pSumHead The head of the checksum list. 795 * @param pSumHeadAdd The head of the checksum list to be added. 796 */ 797 static void kOCSumAddChain(PKOCSUM pSumHead, PCKOCSUM pSumHeadAdd) 798 { 799 while (pSumHeadAdd) 800 { 801 kOCSumAdd(pSumHead, pSumHeadAdd); 802 pSumHeadAdd = pSumHeadAdd->pNext; 803 } 804 } 805 806 807 808 /** 809 * Prints the checksum to the specified stream. 810 * 811 * @param pSum The checksum. 812 * @param pFile The output file stream 813 */ 814 static void kOCSumFPrintf(PCKOCSUM pSum, FILE *pFile) 815 { 816 fprintf(pFile, "%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 817 pSum->crc32, 818 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3], 819 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7], 820 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11], 821 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]); 822 } 823 824 825 /** 826 * Displays the checksum (not chain!) using the InfoMsg() method. 827 * 828 * @param pSum The checksum. 829 * @param uLevel The info message level. 830 * @param pszMsg Message to prefix the info message with. 831 */ 832 static void kOCSumInfo(PCKOCSUM pSum, unsigned uLevel, const char *pszMsg) 833 { 834 InfoMsg(uLevel, 835 "%s: crc32=%#010x md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 836 pszMsg, 837 pSum->crc32, 838 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3], 839 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7], 840 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11], 841 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]); 842 } 171 843 172 844 … … 179 851 * @param pSum2 The second checksum. 180 852 */ 181 static int kO bjCacheSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)853 static int kOCSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2) 182 854 { 183 855 if (pSum1 == pSum2) … … 194 866 195 867 /** 196 * Print a fatal error message and exit with rc=1. 197 * 198 * @param pEntry The cache entry. 199 * @param pszFormat The message to print. 200 * @param ... Format arguments. 201 */ 202 static void kObjCacheFatal(PCKOBJCACHE pEntry, const char *pszFormat, ...) 203 { 204 va_list va; 205 206 fprintf(stderr, "kObjCache %s - fatal error: ", pEntry->pszName); 207 va_start(va, pszFormat); 208 vfprintf(stderr, pszFormat, va); 209 va_end(va); 210 211 exit(1); 212 } 213 214 215 /** 216 * Print a verbose message if verbosisty is enabled. 217 * 218 * @param pEntry The cache entry. 219 * @param pszFormat The message to print. 220 * @param ... Format arguments. 221 */ 222 static void kObjCacheVerbose(PCKOBJCACHE pEntry, const char *pszFormat, ...) 223 { 224 if (g_fVerbose) 225 { 226 va_list va; 227 228 fprintf(stdout, "kObjCache %s - info: ", pEntry->pszName); 229 va_start(va, pszFormat); 230 vfprintf(stdout, pszFormat, va); 231 va_end(va); 232 } 233 } 868 * Checks if the specified checksum equals one of the 869 * checksums in the chain. 870 * 871 * @returns 1 if equals one of them, 0 if not. 872 * 873 * @param pSumHead The checksum chain too look in. 874 * @param pSum The checksum to look for. 875 * @todo ugly name. fix. 876 */ 877 static int kOCSumHasEqualInChain(PCKOCSUM pSumHead, PCKOCSUM pSum) 878 { 879 for (; pSumHead; pSumHead = pSumHead->pNext) 880 { 881 if (pSumHead == pSum) 882 return 1; 883 if (pSumHead->crc32 != pSum->crc32) 884 continue; 885 if (memcmp(&pSumHead->md5[0], &pSum->md5[0], sizeof(pSumHead->md5))) 886 continue; 887 return 1; 888 } 889 return 0; 890 } 891 892 893 /** 894 * Checks if the checksum (chain) empty. 895 * 896 * @returns 1 if empty, 0 if it there is one or more checksums. 897 * @param pSum The checksum to test. 898 */ 899 static int kOCSumIsEmpty(PCKOCSUM pSum) 900 { 901 return !pSum->fUsed; 902 } 903 904 905 906 907 908 909 /** 910 * The representation of a cache entry. 911 */ 912 typedef struct KOCENTRY 913 { 914 /** The name of the cache entry. */ 915 const char *pszName; 916 /** The dir that all other names are relative to. */ 917 char *pszDir; 918 /** The absolute path. */ 919 char *pszAbsPath; 920 /** Set if the object needs to be (re)compiled. */ 921 unsigned fNeedCompiling; 922 /** Whether the precompiler runs in piped mode. If clear it's file 923 * mode (it could be redirected stdout, but that's essentially the 924 * same from our point of view). */ 925 unsigned fPipedPreComp; 926 /** Whether the compiler runs in piped mode (precompiler output on stdin). */ 927 unsigned fPipedCompile; 928 /** Cache entry key that's used for some quick digest validation. */ 929 uint32_t uKey; 930 931 /** The file data. */ 932 struct KOCENTRYDATA 933 { 934 /** The name of file containing the precompiler output. */ 935 char *pszCppName; 936 /** Pointer to the precompiler output. */ 937 char *pszCppMapping; 938 /** The size of the precompiler output. 0 if not determined. */ 939 size_t cbCpp; 940 /** The precompiler output checksums that will produce the cached object. */ 941 KOCSUM SumHead; 942 /** The object filename (relative to the cache file). */ 943 char *pszObjName; 944 /** The compile argument vector used to build the object. */ 945 char **papszArgvCompile; 946 /** The size of the compile */ 947 unsigned cArgvCompile; 948 /** The checksum of the compiler argument vector. */ 949 KOCSUM SumCompArgv; 950 /** The target os/arch identifier. */ 951 char *pszTarget; 952 } 953 /** The old data.*/ 954 Old, 955 /** The new data. */ 956 New; 957 } KOCENTRY; 958 /** Pointer to a KOCENTRY. */ 959 typedef KOCENTRY *PKOCENTRY; 960 /** Pointer to a const KOCENTRY. */ 961 typedef const KOCENTRY *PCKOCENTRY; 234 962 235 963 … … 240 968 * @param pszFilename The cache file name. 241 969 */ 242 static PKOBJCACHE kObjCacheCreate(const char *pszFilename) 243 { 244 PKOBJCACHE pEntry; 970 static PKOCENTRY kOCEntryCreate(const char *pszFilename) 971 { 972 PKOCENTRY pEntry; 973 size_t off; 245 974 246 975 /* 247 976 * Allocate an empty entry. 248 977 */ 249 pEntry = xmalloc(sizeof(*pEntry)); 250 memset(pEntry, 0, sizeof(*pEntry)); 978 pEntry = xmallocz(sizeof(*pEntry)); 979 980 kOCSumInit(&pEntry->New.SumHead); 981 kOCSumInit(&pEntry->Old.SumHead); 982 983 kOCSumInit(&pEntry->New.SumCompArgv); 984 kOCSumInit(&pEntry->Old.SumCompArgv); 251 985 252 986 /* 253 987 * Setup the directory and cache file name. 254 988 */ 255 pEntry->pszDir = AbsPath(pszFilename); 256 pEntry->pszName = FindFilenameInPath(pEntry->pszDir); 257 if (pEntry->pszDir == pEntry->pszName) 258 kObjCacheFatal(pEntry, "Failed to find abs path for '%s'!\n", pszFilename); 259 ((char *)pEntry->pszName)[-1] = '\0'; 989 pEntry->pszAbsPath = AbsPath(pszFilename); 990 pEntry->pszName = FindFilenameInPath(pEntry->pszAbsPath); 991 off = pEntry->pszName - pEntry->pszAbsPath; 992 if (!off) 993 FatalDie("Failed to find abs path for '%s'!\n", pszFilename); 994 pEntry->pszDir = xmalloc(off - 1); 995 memcpy(pEntry->pszDir, pEntry->pszAbsPath, off); 996 pEntry->pszDir[off - 1] = '\0'; 260 997 261 998 return pEntry; … … 263 1000 264 1001 265 #if 0 /* don't bother. */266 1002 /** 267 1003 * Destroys the cache entry freeing up all it's resources. … … 269 1005 * @param pEntry The entry to free. 270 1006 */ 271 static void kO bjCacheDestroy(PKOBJCACHEpEntry)1007 static void kOCEntryDestroy(PKOCENTRY pEntry) 272 1008 { 273 1009 free(pEntry->pszDir); 274 free(pEntry->pszName); 275 while (pEntry->SumHead.pNext) 276 { 277 void *pv = pEntry->SumHead.pNext; 278 pEntry->SumHead.pNext = pEntry->SumHead.pNext->pNext; 279 if (pv != &pEntry->NewSum) 280 free(pv); 281 } 1010 free(pEntry->pszAbsPath); 1011 1012 kOCSumDeleteChain(&pEntry->New.SumHead); 1013 kOCSumDeleteChain(&pEntry->Old.SumHead); 1014 1015 kOCSumDeleteChain(&pEntry->New.SumCompArgv); 1016 kOCSumDeleteChain(&pEntry->Old.SumCompArgv); 1017 1018 free(pEntry->New.pszCppName); 1019 free(pEntry->Old.pszCppName); 1020 1021 free(pEntry->New.pszCppMapping); 1022 free(pEntry->Old.pszCppMapping); 1023 1024 free(pEntry->New.pszObjName); 1025 free(pEntry->Old.pszObjName); 1026 1027 while (pEntry->New.cArgvCompile > 0) 1028 free(pEntry->New.papszArgvCompile[--pEntry->New.cArgvCompile]); 1029 while (pEntry->Old.cArgvCompile > 0) 1030 free(pEntry->Old.papszArgvCompile[--pEntry->Old.cArgvCompile]); 1031 1032 free(pEntry->New.papszArgvCompile); 1033 free(pEntry->Old.papszArgvCompile); 1034 282 1035 free(pEntry); 283 1036 } 284 #endif285 1037 286 1038 … … 290 1042 * @param pEntry The entry to read it into. 291 1043 */ 292 static void kObjCacheRead(PKOBJCACHE pEntry) 293 { 294 static char s_szLine[KOBJCACHE_MAX_LINE_LEN + 16]; 1044 static void kOCEntryRead(PKOCENTRY pEntry) 1045 { 295 1046 FILE *pFile; 296 1047 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb"); 297 1048 if (pFile) 298 1049 { 299 kObjCacheVerbose(pEntry, "reading cache file...\n");1050 InfoMsg(1, "reading cache file...\n"); 300 1051 301 1052 /* 302 1053 * Check the magic. 303 1054 */ 304 if ( !fgets( s_szLine, sizeof(s_szLine), pFile)305 || strcmp( s_szLine, "magic=kObjCache-1\n"))306 { 307 kObjCacheVerbose(pEntry, "bad cache file (magic)\n");1055 if ( !fgets(g_szLine, sizeof(g_szLine), pFile) 1056 || strcmp(g_szLine, "magic=kObjCache-1\n")) 1057 { 1058 InfoMsg(1, "bad cache file (magic)\n"); 308 1059 pEntry->fNeedCompiling = 1; 309 1060 } … … 315 1066 unsigned i; 316 1067 int fBad = 0; 317 int fBadBeforeMissing ;1068 int fBadBeforeMissing = 1; 318 1069 int fFirstSum = 1; 319 while (fgets( s_szLine, sizeof(s_szLine), pFile))1070 while (fgets(g_szLine, sizeof(g_szLine), pFile)) 320 1071 { 1072 char *pszNl; 1073 char *pszVal; 1074 321 1075 /* Split the line and drop the trailing newline. */ 322 char *pszNl = strchr(s_szLine, '\n'); 323 char *pszVal = strchr(s_szLine, '='); 1076 pszVal = strchr(g_szLine, '='); 324 1077 if ((fBad = pszVal == NULL)) 325 1078 break; 1079 *pszVal++ = '\0'; 1080 1081 pszNl = strchr(pszVal, '\n'); 326 1082 if (pszNl) 327 1083 *pszNl = '\0'; 328 *pszVal++ = '\0';329 1084 330 1085 /* string case on variable name */ 331 if (!strcmp( s_szLine, "obj"))1086 if (!strcmp(g_szLine, "obj")) 332 1087 { 333 if ((fBad = pEntry-> pszObjName != NULL))1088 if ((fBad = pEntry->Old.pszObjName != NULL)) 334 1089 break; 335 pEntry-> pszObjName = xstrdup(pszVal);1090 pEntry->Old.pszObjName = xstrdup(pszVal); 336 1091 } 337 else if (!strcmp( s_szLine, "cpp"))1092 else if (!strcmp(g_szLine, "cpp")) 338 1093 { 339 if ((fBad = pEntry-> pszOldCppName != NULL))1094 if ((fBad = pEntry->Old.pszCppName != NULL)) 340 1095 break; 341 pEntry-> pszOldCppName = xstrdup(pszVal);1096 pEntry->Old.pszCppName = xstrdup(pszVal); 342 1097 } 343 else if (!strcmp( s_szLine, "cpp-size"))1098 else if (!strcmp(g_szLine, "cpp-size")) 344 1099 { 345 1100 char *pszNext; 346 if ((fBad = pEntry-> cbOldCpp != 0))1101 if ((fBad = pEntry->Old.cbCpp != 0)) 347 1102 break; 348 pEntry-> cbOldCpp = strtoul(pszVal, &pszNext, 0);1103 pEntry->Old.cbCpp = strtoul(pszVal, &pszNext, 0); 349 1104 if ((fBad = pszNext && *pszNext)) 350 1105 break; 351 1106 } 352 else if (!strcmp( s_szLine, "cc-argc"))1107 else if (!strcmp(g_szLine, "cc-argc")) 353 1108 { 354 if ((fBad = pEntry-> papszArgvCompile != NULL))1109 if ((fBad = pEntry->Old.papszArgvCompile != NULL)) 355 1110 break; 356 pEntry->cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */ 357 pEntry->papszArgvCompile = xmalloc((pEntry->cArgvCompile + 1) * sizeof(pEntry->papszArgvCompile[0])); 358 memset(pEntry->papszArgvCompile, 0, (pEntry->cArgvCompile + 1) * sizeof(pEntry->papszArgvCompile[0])); 1111 pEntry->Old.cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */ 1112 pEntry->Old.papszArgvCompile = xmallocz((pEntry->Old.cArgvCompile + 1) * sizeof(pEntry->Old.papszArgvCompile[0])); 359 1113 } 360 else if (!strncmp( s_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1))1114 else if (!strncmp(g_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1)) 361 1115 { 362 1116 char *pszNext; 363 unsigned i = strtoul(& s_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0);364 if ((fBad = i >= pEntry-> cArgvCompile || pEntry->papszArgvCompile[i] || (pszNext && *pszNext)))1117 unsigned i = strtoul(&g_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0); 1118 if ((fBad = i >= pEntry->Old.cArgvCompile || pEntry->Old.papszArgvCompile[i] || (pszNext && *pszNext))) 365 1119 break; 366 pEntry-> papszArgvCompile[i] = xstrdup(pszVal);1120 pEntry->Old.papszArgvCompile[i] = xstrdup(pszVal); 367 1121 } 368 else if (!strcmp( s_szLine, "sum"))1122 else if (!strcmp(g_szLine, "sum")) 369 1123 { 370 1124 KOCSUM Sum; 371 unsigned i; 372 char *pszNext; 373 char *pszMD5 = strchr(pszVal, ':'); 374 if ((fBad = pszMD5 == NULL)) 1125 if ((fBad = kOCSumInitFromString(&Sum, pszVal))) 375 1126 break; 376 *pszMD5++ = '\0';377 378 /* crc32 */379 Sum.crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16);380 if ((fBad = (pszNext && *pszNext)))1127 kOCSumAdd(&pEntry->Old.SumHead, &Sum); 1128 } 1129 else if (!strcmp(g_szLine, "target")) 1130 { 1131 if ((fBad = pEntry->Old.pszTarget != NULL)) 381 1132 break; 382 383 /* md5 */ 384 for (i = 0; i < sizeof(Sum.md5) * 2; i++) 385 { 386 unsigned char ch = pszMD5[i]; 387 int x; 388 if ((unsigned char)(ch - '0') <= 9) 389 x = ch - '0'; 390 else if ((unsigned char)(ch - 'a') <= 5) 391 x = ch - 'a' + 10; 392 else if ((unsigned char)(ch - 'A') <= 5) 393 x = ch - 'A' + 10; 394 else 395 { 396 fBad = 1; 397 break; 398 } 399 if (!(i & 1)) 400 Sum.md5[i >> 1] = x << 4; 401 else 402 Sum.md5[i >> 1] |= x; 403 } 404 if (fBad) 405 break; 406 407 if (fFirstSum) 408 { 409 pEntry->SumHead = Sum; 410 pEntry->SumHead.pNext = NULL; 411 fFirstSum = 0; 412 } 413 else 414 { 415 Sum.pNext = pEntry->SumHead.pNext; 416 pEntry->SumHead.pNext = xmalloc(sizeof(Sum)); 417 *pEntry->SumHead.pNext = Sum; 418 } 1133 pEntry->Old.pszTarget = xstrdup(pszVal); 1134 } 1135 else if (!strcmp(g_szLine, "the-end")) 1136 { 1137 fBadBeforeMissing = fBad = !strcmp(pszVal, "fine"); 1138 break; 419 1139 } 420 1140 else … … 426 1146 427 1147 /* 428 * Did we find everything ?1148 * Did we find everything and does it add up correctly? 429 1149 */ 430 fBadBeforeMissing = fBad; 431 if ( !fBad 432 && ( !pEntry->papszArgvCompile 433 || !pEntry->pszObjName 434 || !pEntry->pszOldCppName 435 || fFirstSum)) 1150 if (!fBad && fBadBeforeMissing) 1151 { 1152 InfoMsg(1, "bad cache file (no end)\n"); 436 1153 fBad = 1; 437 if (!fBad) 438 for (i = 0; i < pEntry->cArgvCompile; i++) 439 if ((fBad = !pEntry->papszArgvCompile[i])) 440 break; 441 if (fBad) 442 kObjCacheVerbose(pEntry, "bad cache file (%s)\n", fBadBeforeMissing ? s_szLine : "missing stuff"); 443 else if (ferror(pFile)) 444 kObjCacheVerbose(pEntry, "cache file read error\n"); 1154 } 1155 else 1156 { 1157 fBadBeforeMissing = fBad; 1158 if ( !fBad 1159 && ( !pEntry->Old.papszArgvCompile 1160 || !pEntry->Old.pszObjName 1161 || !pEntry->Old.pszCppName 1162 || fFirstSum)) 1163 fBad = 1; 1164 if (!fBad) 1165 { 1166 KOCSUMCTX Ctx; 1167 KOCSUM Sum; 1168 1169 kOCSumInitWithCtx(&Sum, &Ctx); 1170 for (i = 0; i < pEntry->Old.cArgvCompile; i++) 1171 { 1172 if ((fBad = !pEntry->Old.papszArgvCompile[i])) 1173 break; 1174 kOCSumUpdate(&Sum, &Ctx, pEntry->Old.papszArgvCompile[i], strlen(pEntry->Old.papszArgvCompile[i]) + 1); 1175 } 1176 kOCSumFinalize(&Sum, &Ctx); 1177 if (!fBad) 1178 fBad = !kOCSumIsEqual(&pEntry->Old.SumCompArgv, &Sum); 1179 } 1180 if (fBad) 1181 InfoMsg(1, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff"); 1182 else if (ferror(pFile)) 1183 { 1184 InfoMsg(1, "cache file read error\n"); 1185 fBad = 1; 1186 } 1187 1188 /* 1189 * Verify the existance of the object file. 1190 */ 1191 if (!fBad) 1192 { 1193 struct stat st; 1194 char *pszPath = MakePathFromDirAndFile(pEntry->Old.pszObjName, pEntry->pszDir); 1195 if (stat(pszPath, &st) != 0) 1196 { 1197 InfoMsg(1, "failed to stat object file: %s\n", strerror(errno)); 1198 fBad = 1; 1199 } 1200 else 1201 { 1202 /** @todo verify size and the timestamp. */ 1203 } 1204 } 1205 } 445 1206 pEntry->fNeedCompiling = fBad; 446 1207 } … … 449 1210 else 450 1211 { 451 kObjCacheVerbose(pEntry, "no cache file\n");1212 InfoMsg(1, "no cache file\n"); 452 1213 pEntry->fNeedCompiling = 1; 453 1214 } … … 460 1221 * @param pEntry The entry to write. 461 1222 */ 462 static void kO bjCacheWrite(PKOBJCACHEpEntry)1223 static void kOCEntryWrite(PKOCENTRY pEntry) 463 1224 { 464 1225 FILE *pFile; … … 466 1227 unsigned i; 467 1228 468 kObjCacheVerbose(pEntry, "writing cache file...\n");1229 InfoMsg(1, "writing cache file...\n"); 469 1230 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb"); 470 1231 if (!pFile) 471 kObjCacheFatal(pEntry,"Failed to open '%s' in '%s': %s\n",472 1232 FatalDie("Failed to open '%s' in '%s': %s\n", 1233 pEntry->pszName, pEntry->pszDir, strerror(errno)); 473 1234 474 1235 #define CHECK_LEN(expr) \ 475 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)1236 do { int cch = expr; if (cch >= KOBJCACHE_MAX_LINE_LEN) FatalDie("Line too long: %d (max %d)\nexpr: %s\n", cch, KOBJCACHE_MAX_LINE_LEN, #expr); } while (0) 476 1237 477 1238 fprintf(pFile, "magic=kObjCache-1\n"); 478 CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->pszNewObjName ? pEntry->pszNewObjName : pEntry->pszObjName)); 479 CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->pszNewCppName ? pEntry->pszNewCppName : pEntry->pszOldCppName)); 480 CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->pszNewCppName ? pEntry->cbNewCpp : pEntry->cbOldCpp)); 481 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->cArgvCompile)); 482 for (i = 0; i < pEntry->cArgvCompile; i++) 483 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->papszArgvCompile[i])); 484 for (pSum = pEntry->fNeedCompiling ? &pEntry->NewSum : &pEntry->SumHead; 1239 CHECK_LEN(fprintf(pFile, "target=%s\n", pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget)); 1240 CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName)); 1241 CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName)); 1242 CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->New.pszCppName ? pEntry->New.cbCpp : pEntry->Old.cbCpp)); 1243 1244 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv)) 1245 { 1246 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->New.cArgvCompile)); 1247 for (i = 0; i < pEntry->New.cArgvCompile; i++) 1248 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->New.papszArgvCompile[i])); 1249 fprintf(pFile, "cc-argv-sum="); 1250 kOCSumFPrintf(&pEntry->New.SumCompArgv, pFile); 1251 } 1252 else 1253 { 1254 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->Old.cArgvCompile)); 1255 for (i = 0; i < pEntry->Old.cArgvCompile; i++) 1256 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->Old.papszArgvCompile[i])); 1257 fprintf(pFile, "cc-argv-sum="); 1258 kOCSumFPrintf(&pEntry->Old.SumCompArgv, pFile); 1259 } 1260 1261 1262 for (pSum = kOCSumIsEmpty(&pEntry->New.SumHead) ? &pEntry->New.SumHead : &pEntry->Old.SumHead; 485 1263 pSum; 486 1264 pSum = pSum->pNext) 487 fprintf(pFile, "sum=%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 488 pSum->crc32, 489 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3], 490 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7], 491 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11], 492 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]); 493 1265 { 1266 fprintf(pFile, "sum="); 1267 kOCSumFPrintf(pSum, pFile); 1268 } 1269 1270 fprintf(pFile, "the-end=fine\n"); 1271 1272 #undef CHECK_LEN 1273 1274 /* 1275 * Flush the file and check for errors. 1276 * On failure delete the file so we won't be seeing any invalid 1277 * files the next time or upset make with new timestamps. 1278 */ 494 1279 if ( fflush(pFile) < 0 495 1280 || ferror(pFile)) … … 498 1283 fclose(pFile); 499 1284 UnlinkFileInDir(pEntry->pszName, pEntry->pszDir); 500 kObjCacheFatal(pEntry, "Stream error occured while writing '%s' in '%s': %d (?)\n",501 1285 FatalDie("Stream error occured while writing '%s' in '%s': %s\n", 1286 pEntry->pszName, pEntry->pszDir, strerror(iErr)); 502 1287 } 503 1288 fclose(pFile); 1289 } 1290 1291 1292 /** 1293 * Checks that the read cache entry is valid. 1294 * It sets fNeedCompiling if it isn't. 1295 * 1296 * @returns 1 valid, 0 invalid. 1297 * @param pEntry The cache entry. 1298 */ 1299 static int kOCEntryCheck(PKOCENTRY pEntry) 1300 { 1301 return pEntry->fNeedCompiling; 1302 } 1303 1304 1305 /** 1306 * Set the new compiler args, calc their checksum, and comparing them with any old ones. 1307 * 1308 * @param pEntry The cache entry. 1309 * @param papszArgvCompile The new argument vector for compilation. 1310 * @param cArgvCompile The number of arguments in the vector. 1311 */ 1312 static void kOCEntrySetCompileArgv(PKOCENTRY pEntry, const char * const *papszArgvCompile, unsigned cArgvCompile) 1313 { 1314 KOCSUMCTX Ctx; 1315 unsigned i; 1316 1317 /* call me only once! */ 1318 assert(!pEntry->New.cArgvCompile); 1319 1320 /* 1321 * Copy the argument vector and calculate the checksum while doing so. 1322 */ 1323 pEntry->New.cArgvCompile = cArgvCompile; 1324 pEntry->New.papszArgvCompile = xmalloc(cArgvCompile + 1); 1325 kOCSumInitWithCtx(&pEntry->New.SumCompArgv, &Ctx); 1326 for (i = 0; i < cArgvCompile; i++) 1327 { 1328 pEntry->New.papszArgvCompile[i] = xstrdup(papszArgvCompile[i]); 1329 kOCSumUpdate(&pEntry->New.SumCompArgv, &Ctx, papszArgvCompile[i], strlen(papszArgvCompile[i])); 1330 } 1331 kOCSumFinalize(&pEntry->New.SumCompArgv, &Ctx); 1332 pEntry->New.papszArgvCompile[i] = NULL; /* for exev/spawnv */ 1333 1334 /* 1335 * Compare with the old argument vector. 1336 */ 1337 if ( !pEntry->fNeedCompiling 1338 && !kOCSumIsEqual(&pEntry->New.SumCompArgv, &pEntry->Old.SumCompArgv)) 1339 { 1340 InfoMsg(1, "compiler args differs\n"); 1341 pEntry->fNeedCompiling = 1; 1342 } 1343 } 1344 1345 1346 /** 1347 * Sets the object name and compares it with the old name if present. 1348 * 1349 * @param pEntry The cache entry. 1350 * @param pszObjName The new object name. 1351 */ 1352 static void kOCEntrySetCompileObjName(PKOCENTRY pEntry, const char *pszObjName) 1353 { 1354 assert(!pEntry->New.pszObjName); 1355 pEntry->New.pszObjName = CalcRelativeName(pszObjName, pEntry->pszDir); 1356 1357 if ( !pEntry->fNeedCompiling 1358 && ( !pEntry->Old.pszObjName 1359 || strcmp(pEntry->New.pszObjName, pEntry->Old.pszObjName))) 1360 { 1361 InfoMsg(1, "object file name differs\n"); 1362 pEntry->fNeedCompiling = 1; 1363 } 1364 1365 if ( !pEntry->fNeedCompiling 1366 && !DoesFileInDirExist(pEntry->New.pszObjName, pEntry->pszDir)) 1367 { 1368 InfoMsg(1, "object file doesn't exist\n"); 1369 pEntry->fNeedCompiling = 1; 1370 } 1371 } 1372 1373 1374 /** 1375 * Sets the arch/os target and compares it with the old name if present. 1376 * 1377 * @param pEntry The cache entry. 1378 * @param pszObjName The new object name. 1379 */ 1380 static void kOCEntrySetTarget(PKOCENTRY pEntry, const char *pszTarget) 1381 { 1382 assert(!pEntry->New.pszTarget); 1383 pEntry->New.pszTarget = xstrdup(pszTarget); 1384 1385 if ( !pEntry->fNeedCompiling 1386 && ( !pEntry->Old.pszTarget 1387 || strcmp(pEntry->New.pszTarget, pEntry->Old.pszTarget))) 1388 { 1389 InfoMsg(1, "target differs\n"); 1390 pEntry->fNeedCompiling = 1; 1391 } 1392 } 1393 1394 1395 /** 1396 * Sets the precompiler output filename. 1397 * We don't generally care if this matches the old name or not. 1398 * 1399 * @param pEntry The cache entry. 1400 * @param pszCppName The precompiler output filename. 1401 */ 1402 static void kOCEntrySetCppName(PKOCENTRY pEntry, const char *pszCppName) 1403 { 1404 assert(!pEntry->New.pszCppName); 1405 pEntry->New.pszCppName = CalcRelativeName(pszCppName, pEntry->pszDir); 1406 } 1407 1408 1409 /** 1410 * Sets the piped mode of the precompiler and compiler. 1411 * 1412 * @param pEntry The cache entry. 1413 * @param fRedirPreCompStdOut Whether the precompiler is in piped mode. 1414 * @param fRedirCompileStdIn Whether the compiler is in piped mode. 1415 */ 1416 static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn) 1417 { 1418 pEntry->fPipedPreComp = fRedirPreCompStdOut; 1419 pEntry->fPipedCompile = fRedirCompileStdIn; 504 1420 } 505 1421 … … 512 1428 * @param cArgv The number of arguments in the vector. 513 1429 */ 514 static void kO bjCacheSpawn(PCKOBJCACHEpEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, const char *pszStdOut)1430 static void kOCEntrySpawn(PCKOCENTRY pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, const char *pszStdOut) 515 1431 { 516 1432 #if defined(__OS2__) || defined(__WIN__) … … 522 1438 fdStdOut = dup(1); /* dup2(1,-1) doesn't work right on windows */ 523 1439 close(1); 524 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0 777);1440 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666); 525 1441 if (fdReDir < 0) 526 kObjCacheFatal(pEntry,"%s - failed to create stdout redirection file '%s': %s\n",527 1442 FatalDie("%s - failed to create stdout redirection file '%s': %s\n", 1443 pszMsg, pszStdOut, strerror(errno)); 528 1444 529 1445 if (fdReDir != 1) 530 1446 { 531 1447 if (dup2(fdReDir, 1) < 0) 532 kObjCacheFatal(pEntry,"%s - dup2 failed: %s\n", pszMsg, strerror(errno));1448 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno)); 533 1449 close(fdReDir); 534 1450 } … … 538 1454 rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv); 539 1455 if (rc < 0) 540 kObjCacheFatal(pEntry,"%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));1456 FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno)); 541 1457 if (rc > 0) 542 kObjCacheFatal(pEntry,"%s - failed rc=%d\n", pszMsg, (int)rc);1458 FatalDie("%s - failed rc=%d\n", pszMsg, (int)rc); 543 1459 if (fdStdOut) 544 1460 { … … 559 1475 560 1476 close(1); 561 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0 777);1477 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666); 562 1478 if (fdReDir < 0) 563 kObjCacheFatal(pEntry,"%s - failed to create stdout redirection file '%s': %s\n",564 1479 FatalDie("%s - failed to create stdout redirection file '%s': %s\n", 1480 pszMsg, pszStdOut, strerror(errno)); 565 1481 if (fdReDir != 1) 566 1482 { 567 1483 if (dup2(fdReDir, 1) < 0) 568 kObjCacheFatal(pEntry,"%s - dup2 failed: %s\n", pszMsg, strerror(errno));1484 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno)); 569 1485 close(fdReDir); 570 1486 } … … 572 1488 573 1489 execvp(papszArgv[0], (char **)papszArgv); 574 kObjCacheFatal(pEntry,"%s - execvp failed: %s\n",575 1490 FatalDie("%s - execvp failed: %s\n", 1491 pszMsg, strerror(errno)); 576 1492 } 577 1493 if (pid == -1) 578 kObjCacheFatal(pEntry,"%s - fork() failed: %s\n", pszMsg, strerror(errno));1494 FatalDie("%s - fork() failed: %s\n", pszMsg, strerror(errno)); 579 1495 580 1496 pidWait = waitpid(pid, &iStatus, 0); … … 582 1498 pidWait = waitpid(pid, &iStatus, 0); 583 1499 if (pidWait != pid) 584 kObjCacheFatal(pEntry,"%s - waitpid failed rc=%d: %s\n",585 1500 FatalDie("%s - waitpid failed rc=%d: %s\n", 1501 pszMsg, pidWait, strerror(errno)); 586 1502 if (!WIFEXITED(iStatus)) 587 kObjCacheFatal(pEntry,"%s - abended (iStatus=%#x)\n", pszMsg, iStatus);1503 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus); 588 1504 if (WEXITSTATUS(iStatus)) 589 kObjCacheFatal(pEntry,"%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));1505 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus)); 590 1506 #endif 591 1507 (void)cArgv; … … 593 1509 594 1510 595 #ifdef USE_PIPE 596 /** 597 * Spawns a child in a synchronous fashion. 598 * Terminating on failure. 599 * 1511 /** 1512 * Spawns child with optional redirection of stdin and stdout. 1513 * 1514 * @param pEntry The cache entry. 600 1515 * @param papszArgv Argument vector. The cArgv element is NULL. 601 1516 * @param cArgv The number of arguments in the vector. 602 */ 603 static void kObjCacheSpawnPipe(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, char **ppszOutput, size_t *pcchOutput) 604 { 605 int fds[2]; 606 int iStatus; 607 #if defined(__WIN__) 608 intptr_t pid, pidWait; 609 #else 610 pid_t pid, pidWait; 1517 * @param fdStdIn Child stdin, -1 if it should inherit our stdin. Will be closed. 1518 * @param fdStdOut Child stdout, -1 if it should inherit our stdout. Will be closed. 1519 * @param pszMsg Message to start the info/error messages with. 1520 */ 1521 static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, const char **papszArgv, unsigned cArgv, int fdStdIn, int fdStdOut, const char *pszMsg) 1522 { 1523 pid_t pid; 1524 int fdSavedStdOut = -1; 1525 int fdSavedStdIn = -1; 1526 1527 /* 1528 * Setup redirection. 1529 */ 1530 if (fdStdOut != -1) 1531 { 1532 fdSavedStdOut = dup(1 /* stdout */); 1533 if (dup2(fdStdOut, 1 /* stdout */) < 0) 1534 FatalDie("%s - dup2(,1) failed: %s\n", pszMsg, strerror(errno)); 1535 close(fdStdOut); 1536 #ifndef __WIN__ 1537 fcntl(fdSavedStdOut, F_SETFD, FD_CLOEXEC); 611 1538 #endif 612 int fdStdOut; 613 size_t cbAlloc; 614 size_t cbLeft; 615 char *psz; 616 617 /* 618 * Setup the pipe. 619 */ 620 #if defined(__WIN__) 621 if (_pipe(fds, 0, _O_NOINHERIT | _O_BINARY) < 0) 622 #else 623 if (pipe(fds) < 0) 1539 } 1540 if (fdStdIn != -1) 1541 { 1542 fdSavedStdIn = dup(0 /* stdin */); 1543 if (dup2(fdStdOut, 0 /* stdin */) < 0) 1544 FatalDie("%s - dup2(,0) failed: %s\n", pszMsg, strerror(errno)); 1545 close(fdStdIn); 1546 #ifndef __WIN__ 1547 fcntl(fdSavedStdIn, F_SETFD, FD_CLOEXEC); 624 1548 #endif 625 kObjCacheFatal(pEntry, "pipe failed: %s\n", strerror(errno)); 626 fdStdOut = dup(1); 627 if (dup2(fds[1 /* write */], 1) < 0) 628 kObjCacheFatal(pEntry, "dup2(,1) failed: %s\n", strerror(errno)); 629 close(fds[1]); 630 fds[1] = -1; 631 #ifndef __WIN__ 632 fcntl(fds[0], F_SETFD, FD_CLOEXEC); 633 fcntl(fdStdOut, F_SETFD, FD_CLOEXEC); 634 #endif 1549 } 635 1550 636 1551 /* … … 641 1556 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv); 642 1557 if (pid == -1) 643 kObjCacheFatal(pEntry, "%s - _spawnvp failed: %s\n", pszMsg, strerror(errno));1558 FatalDie("precompile - _spawnvp failed: %s\n", strerror(errno)); 644 1559 645 1560 #else … … 648 1563 { 649 1564 execvp(papszArgv[0], (char **)papszArgv); 650 kObjCacheFatal(pEntry, "%s - execvp failed: %s\n", 651 pszMsg, strerror(errno)); 1565 FatalDie("precompile - execvp failed: %s\n", strerror(errno)); 652 1566 } 653 1567 if (pid == -1) 654 kObjCacheFatal(pEntry, "%s - fork() failed: %s\n", pszMsg, strerror(errno));1568 FatalDie("precompile - fork() failed: %s\n", strerror(errno)); 655 1569 #endif 656 1570 657 1571 /* 658 * Restore stdout. 659 */ 660 close(1); 661 fdStdOut = dup2(fdStdOut, 1); 662 663 /* 664 * Read data from the child. 665 */ 666 cbAlloc = pEntry->cbOldCpp ? (pEntry->cbOldCpp + 4*1024*1024 + 4096) & ~(4*1024*1024 - 1) : 4*1024*1024; 667 cbLeft = cbAlloc; 668 *ppszOutput = psz = xmalloc(cbAlloc); 669 for (;;) 670 { 671 long cbRead = read(fds[0], psz, cbLeft - 1); 672 if (!cbRead) 673 break; 674 if (cbRead < 0 && errno != EINTR) 675 kObjCacheFatal(pEntry, "%s - read(%d,,%ld) failed: %s\n", pszMsg, fds[0], (long)cbLeft, strerror(errno)); 676 psz += cbRead; 677 *psz = '\0'; 678 cbLeft -= cbRead; 679 680 /* expand the buffer? */ 681 if (cbLeft <= 1) 682 { 683 size_t off = psz - *ppszOutput; 684 cbLeft = 4*1024*1024; 685 cbAlloc += cbLeft; 686 *ppszOutput = xrealloc(*ppszOutput, cbAlloc); 687 psz = *ppszOutput + off; 688 } 689 } 690 close(fds[0]); 691 *pcchOutput = cbAlloc - cbLeft; 692 693 /* 694 * Reap the child. 695 */ 1572 * Restore stdout & stdin. 1573 */ 1574 if (fdSavedStdIn) 1575 { 1576 close(0 /* stdin */); 1577 dup2(fdStdOut, 0 /* stdin */); 1578 close(fdSavedStdIn); 1579 } 1580 if (fdSavedStdOut) 1581 { 1582 close(1 /* stdout */); 1583 dup2(fdSavedStdOut, 1 /* stdout */); 1584 close(fdSavedStdOut); 1585 } 1586 1587 (void)cArgv; 1588 (void)pEntry; 1589 return pid; 1590 } 1591 1592 1593 /** 1594 * Waits for a child and exits fatally if the child failed in any way. 1595 * 1596 * @param pEntry The cache entry. 1597 * @param pid The child to wait for. 1598 * @param pszMsg Message to start the info/error messages with. 1599 */ 1600 static void kOCEntryWaitChild(PCKOCENTRY pEntry, pid_t pid, const char *pszMsg) 1601 { 1602 int iStatus = -1; 1603 pid_t pidWait; 696 1604 #ifdef __WIN__ 697 1605 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD); 698 1606 if (pidWait == -1) 699 kObjCacheFatal(pEntry, "%s - waitpid failed: %s\n", 700 pszMsg, strerror(errno)); 1607 FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno)); 701 1608 if (iStatus) 702 kObjCacheFatal(pEntry,"%s - failed with rc %d\n", pszMsg, iStatus);1609 FatalDie("%s - failed with rc %d\n", pszMsg, iStatus); 703 1610 #else 704 1611 pidWait = waitpid(pid, &iStatus, 0); … … 706 1613 pidWait = waitpid(pid, &iStatus, 0); 707 1614 if (pidWait != pid) 708 kObjCacheFatal(pEntry, "%s - waitpid failed rc=%d: %s\n", 709 pszMsg, pidWait, strerror(errno)); 1615 FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno)); 710 1616 if (!WIFEXITED(iStatus)) 711 kObjCacheFatal(pEntry,"%s - abended (iStatus=%#x)\n", pszMsg, iStatus);1617 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus); 712 1618 if (WEXITSTATUS(iStatus)) 713 kObjCacheFatal(pEntry,"%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));1619 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus)); 714 1620 #endif 715 (void)cArgv; 716 } 717 #endif /* USE_PIPE */ 718 719 720 /** 721 * Reads the (new) output of the precompiler. 722 * 723 * Not used when using pipes. 724 * 725 * @param pEntry The cache entry. cbNewCpp and pszNewCppMapping will be updated. 726 */ 727 static void kObjCacheReadPrecompileOutput(PKOBJCACHE pEntry) 728 { 729 pEntry->pszNewCppMapping = ReadFileInDir(pEntry->pszNewCppName, pEntry->pszDir, &pEntry->cbNewCpp); 730 if (!pEntry->pszNewCppMapping) 731 kObjCacheFatal(pEntry, "failed to open/read '%s' in '%s': %s\n", 732 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno)); 733 kObjCacheVerbose(pEntry, "precompiled file is %lu bytes long\n", (unsigned long)pEntry->cbNewCpp); 734 } 735 736 737 /** 738 * Worker for kObjCachePreCompile and calculates the checksum of 1621 (void)pEntry; 1622 } 1623 1624 1625 /** 1626 * Creates a pipe for setting up redirected stdin/stdout. 1627 * 1628 * @param pEntry The cache entry. 1629 * @param pFDs Where to store the two file descriptors. 1630 * @param pszMsg The operation message for info/error messages. 1631 */ 1632 static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *pFDs, const char *pszMsg) 1633 { 1634 #if defined(__WIN__) 1635 if (_pipe(pFDs, 0, _O_NOINHERIT | _O_BINARY) < 0) 1636 #else 1637 if (pipe(pFDs) < 0) 1638 #endif 1639 FatalDie("%s - pipe failed: %s\n", pszMsg, strerror(errno)); 1640 #if !defined(__WIN__) 1641 fcntl(pFDs[0], F_SETFD, FD_CLOEXEC); 1642 fcntl(pFDs[1], F_SETFD, FD_CLOEXEC); 1643 #endif 1644 } 1645 1646 1647 /** 1648 * Spawns a child that produces output to stdout. 1649 * 1650 * @param papszArgv Argument vector. The cArgv element is NULL. 1651 * @param cArgv The number of arguments in the vector. 1652 * @param pszMsg The operation message for info/error messages. 1653 * @param pfnConsumer Pointer to a consumer callback function that is responsible 1654 * for servicing the child output and closing the pipe. 1655 */ 1656 static void kOCEntrySpawnProducer(PKOCENTRY pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, 1657 void (*pfnConsumer)(PKOCENTRY, int)) 1658 { 1659 int fds[2]; 1660 pid_t pid; 1661 1662 kOCEntryCreatePipe(pEntry, fds, pszMsg); 1663 pid = kOCEntrySpawnChild(pEntry, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg); 1664 1665 pfnConsumer(pEntry, fds[0 /* read */]); 1666 1667 kOCEntryWaitChild(pEntry, pid, pszMsg); 1668 } 1669 1670 1671 /** 1672 * Spawns a child that consumes input on stdin. 1673 * 1674 * @param papszArgv Argument vector. The cArgv element is NULL. 1675 * @param cArgv The number of arguments in the vector. 1676 * @param pszMsg The operation message for info/error messages. 1677 * @param pfnProducer Pointer to a producer callback function that is responsible 1678 * for serving the child input and closing the pipe. 1679 */ 1680 static void kOCEntrySpawnConsumer(PKOCENTRY pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, 1681 void (*pfnProducer)(PKOCENTRY, int)) 1682 { 1683 int fds[2]; 1684 pid_t pid; 1685 1686 kOCEntryCreatePipe(pEntry, fds, pszMsg); 1687 pid = kOCEntrySpawnChild(pEntry, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg); 1688 1689 pfnProducer(pEntry, fds[1 /* write */]); 1690 1691 kOCEntryWaitChild(pEntry, pid, pszMsg); 1692 } 1693 1694 1695 /** 1696 * Spawns two child processes, one producing output and one consuming. 1697 * Terminating on failure. 1698 * 1699 * @param papszArgv Argument vector. The cArgv element is NULL. 1700 * @param cArgv The number of arguments in the vector. 1701 * @param pszMsg The operation message for info/error messages. 1702 * @param pfnConsumer Pointer to a consumer callback function that is responsible 1703 * for servicing the child output and closing the pipe. 1704 */ 1705 static void kOCEntrySpawnTee(PKOCENTRY pEntry, const char **papszProdArgv, unsigned cProdArgv, 1706 const char **papszConsArgv, unsigned cConsArgv, 1707 const char *pszMsg, void (*pfnTeeConsumer)(PKOCENTRY, int, int)) 1708 { 1709 int fds[2]; 1710 int fdIn, fdOut; 1711 pid_t pidProducer, pidConsumer; 1712 1713 /* 1714 * The producer. 1715 */ 1716 kOCEntryCreatePipe(pEntry, fds, pszMsg); 1717 pidConsumer = kOCEntrySpawnChild(pEntry, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg); 1718 fdIn = fds[0 /* read */]; 1719 1720 /* 1721 * The consumer. 1722 */ 1723 kOCEntryCreatePipe(pEntry, fds, pszMsg); 1724 pidProducer = kOCEntrySpawnChild(pEntry, papszProdArgv, cProdArgv, fds[0 /* read */], -1, pszMsg); 1725 fdOut = fds[1 /* write */]; 1726 1727 /* 1728 * Hand it on to the tee consumer. 1729 */ 1730 pfnTeeConsumer(pEntry, fdIn, fdOut); 1731 1732 /* 1733 * Reap the children. 1734 */ 1735 kOCEntryWaitChild(pEntry, pidProducer, pszMsg); 1736 kOCEntryWaitChild(pEntry, pidConsumer, pszMsg); 1737 } 1738 1739 1740 /** 1741 * Reads the output from the precompiler. 1742 * 1743 * @param pEntry The cache entry. New.cbCpp and New.pszCppMapping will be updated. 1744 * @param pWhich Specifies what to read (old/new). 1745 * @param fNonFatal Whether failure is fatal or not. 1746 */ 1747 static int kOCEntryReadCppOutput(PKOCENTRY pEntry, struct KOCENTRYDATA *pWhich, int fNonFatal) 1748 { 1749 pWhich->pszCppMapping = ReadFileInDir(pWhich->pszCppName, pEntry->pszDir, &pWhich->cbCpp); 1750 if (!pWhich->pszCppMapping) 1751 { 1752 if (!fNonFatal) 1753 FatalDie("failed to open/read '%s' in '%s': %s\n", 1754 pWhich->pszCppName, pEntry->pszDir, strerror(errno)); 1755 InfoMsg(1, "failed to open/read '%s' in '%s': %s\n", 1756 pWhich->pszCppName, pEntry->pszDir, strerror(errno)); 1757 return -1; 1758 } 1759 1760 InfoMsg(1, "precompiled file is %lu bytes long\n", (unsigned long)pWhich->cbCpp); 1761 return 0; 1762 } 1763 1764 1765 /** 1766 * Worker for kOCEntryPreCompile and calculates the checksum of 739 1767 * the precompiler output. 740 1768 * 741 1769 * @param pEntry The cache entry. NewSum will be updated. 742 1770 */ 743 static void kObjCacheCalcChecksum(PKOBJCACHE pEntry) 744 { 745 struct MD5Context MD5Ctx; 746 747 memset(&pEntry->NewSum, 0, sizeof(pEntry->NewSum)); 748 pEntry->NewSum.crc32 = crc32(0, pEntry->pszNewCppMapping, pEntry->cbNewCpp); 749 MD5Init(&MD5Ctx); 750 MD5Update(&MD5Ctx, (unsigned char *)pEntry->pszNewCppMapping, pEntry->cbNewCpp); 751 MD5Final(&pEntry->NewSum.md5[0], &MD5Ctx); 752 kObjCacheVerbose(pEntry, "crc32=%#lx md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 753 pEntry->NewSum.crc32, 754 pEntry->NewSum.md5[0], pEntry->NewSum.md5[1], pEntry->NewSum.md5[2], pEntry->NewSum.md5[3], 755 pEntry->NewSum.md5[4], pEntry->NewSum.md5[5], pEntry->NewSum.md5[6], pEntry->NewSum.md5[7], 756 pEntry->NewSum.md5[8], pEntry->NewSum.md5[9], pEntry->NewSum.md5[10], pEntry->NewSum.md5[11], 757 pEntry->NewSum.md5[12], pEntry->NewSum.md5[13], pEntry->NewSum.md5[14], pEntry->NewSum.md5[15]); 758 759 } 1771 static void kOCEntryCalcChecksum(PKOCENTRY pEntry) 1772 { 1773 KOCSUMCTX Ctx; 1774 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx); 1775 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, pEntry->New.pszCppMapping, pEntry->New.cbCpp); 1776 kOCSumFinalize(&pEntry->New.SumHead, &Ctx); 1777 kOCSumInfo(&pEntry->New.SumHead, 1, ""); 1778 } 1779 1780 1781 /** 1782 * This consumes the precompiler output and checksums it. 1783 * 1784 * @param pEntry The cache entry. 1785 * @param fdIn The precompiler output pipe. 1786 * @param fdOut The compiler input pipe, -1 if no compiler. 1787 */ 1788 static void kOCEntryPreCompileConsumer(PKOCENTRY pEntry, int fdIn) 1789 { 1790 KOCSUMCTX Ctx; 1791 long cbLeft; 1792 long cbAlloc; 1793 char *psz; 1794 1795 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx); 1796 cbAlloc = pEntry->Old.cbCpp ? (pEntry->Old.cbCpp + 4*1024*1024 + 4096) & ~(4*1024*1024 - 1) : 4*1024*1024; 1797 cbLeft = cbAlloc; 1798 pEntry->New.pszCppMapping = psz = xmalloc(cbAlloc); 1799 for (;;) 1800 { 1801 /* 1802 * Read data from the pipe. 1803 */ 1804 long cbRead = read(fdIn, psz, cbLeft - 1); 1805 if (!cbRead) 1806 break; 1807 if (cbRead < 0) 1808 { 1809 if (errno == EINTR) 1810 continue; 1811 FatalDie("precompile - read(%d,,%ld) failed: %s\n", 1812 fdIn, (long)cbLeft, strerror(errno)); 1813 } 1814 1815 /* 1816 * Process the data. 1817 */ 1818 psz[cbRead] = '\0'; 1819 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead); 1820 1821 /* 1822 * Advance. 1823 */ 1824 psz += cbRead; 1825 cbLeft -= cbRead; 1826 if (cbLeft <= 1) 1827 { 1828 size_t off = psz - pEntry->New.pszCppMapping; 1829 assert(off == cbAlloc); 1830 cbLeft = 4*1024*1024; 1831 cbAlloc += cbLeft; 1832 pEntry->New.pszCppMapping = xrealloc(pEntry->New.pszCppMapping, cbAlloc); 1833 psz = pEntry->New.pszCppMapping + off; 1834 } 1835 } 1836 1837 close(fdIn); 1838 pEntry->New.cbCpp = cbAlloc - cbLeft; 1839 kOCSumFinalize(&pEntry->New.SumHead, &Ctx); 1840 } 1841 1842 760 1843 761 1844 … … 766 1849 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL. 767 1850 * @param cArgvPreComp The number of arguments. 768 * @param pszPreCompName Precompile output name. (must kick around) 769 * @param fRedirStdOut Whether stdout needs to be redirected or not. 770 */ 771 static void kObjCachePreCompile(PKOBJCACHE pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp, const char *pszPreCompName, int fRedirStdOut) 772 { 773 #ifdef USE_PIPE 774 /* 775 * Flag it as piped or non-piped. 776 */ 777 if (fRedirStdOut) 778 pEntry->fPiped = 1; 1851 */ 1852 static void kOCEntryPreCompile(PKOCENTRY pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp) 1853 { 1854 /* 1855 * If we're executing the precompiler in piped mode, it's relatively simple. 1856 */ 1857 if (pEntry->fPipedPreComp) 1858 kOCEntrySpawnProducer(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", 1859 kOCEntryPreCompileConsumer); 779 1860 else 780 #endif 781 pEntry->fPiped = 0; 782 783 /* 784 * Rename the old precompiled output to '-old'. 785 * We'll discard the old output and keep the new output, but because 786 * we might with to do a quick matchup later we can't remove it just now. 787 * If we're using the pipe strategy, we will not do any renaming. 788 */ 789 if ( pEntry->pszOldCppName 790 && !pEntry->fPiped 791 && DoesFileInDirExist(pEntry->pszOldCppName, pEntry->pszDir)) 792 { 793 size_t cch = strlen(pEntry->pszOldCppName); 794 char *psz = xmalloc(cch + sizeof("-old")); 795 memcpy(psz, pEntry->pszOldCppName, cch); 796 memcpy(psz + cch, "-old", sizeof("-old")); 797 798 kObjCacheVerbose(pEntry, "renaming '%s' to '%s' in '%s'\n", pEntry->pszOldCppName, psz, pEntry->pszDir); 799 UnlinkFileInDir(psz, pEntry->pszDir); 800 if (RenameFileInDir(pEntry->pszOldCppName, psz, pEntry->pszDir)) 801 kObjCacheFatal(pEntry, "failed to rename '%s' -> '%s' in '%s': %s\n", 802 pEntry->pszOldCppName, psz, pEntry->pszDir, strerror(errno)); 803 free(pEntry->pszOldCppName); 804 pEntry->pszOldCppName = psz; 805 } 806 pEntry->pszNewCppName = CalcRelativeName(pszPreCompName, pEntry->pszDir); 807 808 /* 809 * Precompile it and calculate the checksum on the output. 810 */ 811 kObjCacheVerbose(pEntry, "precompiling -> '%s'...\n", pEntry->pszNewCppName); 812 #ifdef USE_PIPE 813 if (pEntry->fPiped) 814 kObjCacheSpawnPipe(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", &pEntry->pszNewCppMapping, &pEntry->cbNewCpp); 1861 { 1862 /* 1863 * Rename the old precompiled output to '-old' so the precompiler won't 1864 * overwrite it when we execute it. 1865 */ 1866 if ( pEntry->Old.pszCppName 1867 && DoesFileInDirExist(pEntry->Old.pszCppName, pEntry->pszDir)) 1868 { 1869 size_t cch = strlen(pEntry->Old.pszCppName); 1870 char *psz = xmalloc(cch + sizeof("-old")); 1871 memcpy(psz, pEntry->Old.pszCppName, cch); 1872 memcpy(psz + cch, "-old", sizeof("-old")); 1873 1874 InfoMsg(1, "renaming '%s' to '%s' in '%s'\n", pEntry->Old.pszCppName, psz, pEntry->pszDir); 1875 UnlinkFileInDir(psz, pEntry->pszDir); 1876 if (RenameFileInDir(pEntry->Old.pszCppName, psz, pEntry->pszDir)) 1877 FatalDie("failed to rename '%s' -> '%s' in '%s': %s\n", 1878 pEntry->Old.pszCppName, psz, pEntry->pszDir, strerror(errno)); 1879 free(pEntry->Old.pszCppName); 1880 pEntry->Old.pszCppName = psz; 1881 } 1882 1883 /* 1884 * Precompile it and calculate the checksum on the output. 1885 */ 1886 InfoMsg(1, "precompiling -> '%s'...\n", pEntry->New.pszCppName); 1887 kOCEntrySpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL); 1888 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */); 1889 kOCEntryCalcChecksum(pEntry); 1890 } 1891 } 1892 1893 1894 /** 1895 * Worker function for kOCEntryTeeConsumer and kOCEntryCompileIt that 1896 * writes the precompiler output to disk. 1897 * 1898 * @param pEntry The cache entry. 1899 * @param fFreeIt Whether we can free it after writing it or not. 1900 */ 1901 static void kOCEntryWriteCppOutput(PKOCENTRY pEntry, int fFreeIt) 1902 { 1903 /* 1904 * Remove old files. 1905 */ 1906 if (pEntry->Old.pszCppName) 1907 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir); 1908 if (pEntry->New.pszCppName) 1909 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir); 1910 1911 /* 1912 * Write it to disk if we've got a file name. 1913 */ 1914 if (pEntry->New.pszCppName) 1915 { 1916 long cbLeft; 1917 char *psz; 1918 int fd = OpenFileInDir(pEntry->New.pszCppName, pEntry->pszDir, O_WRONLY | O_TRUNC | O_BINARY, 0666); 1919 if (fd == -1) 1920 FatalDie("Failed to create '%s' in '%s': %s\n", 1921 pEntry->New.pszCppName, pEntry->pszDir, strerror(errno)); 1922 psz = pEntry->New.pszCppMapping; 1923 cbLeft = pEntry->New.cbCpp; 1924 while (cbLeft > 0) 1925 { 1926 long cbWritten = write(fd, psz, cbLeft); 1927 if (cbWritten < 0) 1928 { 1929 int iErr = errno; 1930 if (iErr == EINTR) 1931 continue; 1932 close(fd); 1933 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir); 1934 FatalDie("error writing '%s' in '%s': %s\n", 1935 pEntry->New.pszCppName, pEntry->pszDir, strerror(iErr)); 1936 } 1937 } 1938 close(fd); 1939 } 1940 1941 /* 1942 * Free it. 1943 */ 1944 if (fFreeIt) 1945 { 1946 free(pEntry->New.pszCppMapping); 1947 pEntry->New.pszCppMapping = NULL; 1948 } 1949 } 1950 1951 1952 /** 1953 * kOCEntrySpawnConsumer callback that passes the precompiler 1954 * output to the compiler and writes it to the disk (latter only when necesary). 1955 * 1956 * @param pEntry The cache entry. 1957 * @param fdOut The pipe handle connected to the childs stdin. 1958 */ 1959 static void kOCEntryCompileProducer(PKOCENTRY pEntry, int fdOut) 1960 { 1961 const char *psz = pEntry->New.pszCppMapping; 1962 long cbLeft = pEntry->New.cbCpp; 1963 while (cbLeft > 0) 1964 { 1965 long cbWritten = write(fdOut, psz, cbLeft); 1966 if (cbWritten < 0) 1967 { 1968 if (errno == EINTR) 1969 continue; 1970 FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno)); 1971 } 1972 psz += cbWritten; 1973 cbLeft -= cbWritten; 1974 } 1975 1976 if (pEntry->fPipedPreComp) 1977 kOCEntryWriteCppOutput(pEntry, 1 /* free it */); 1978 } 1979 1980 1981 /** 1982 * Does the actual compiling. 1983 * 1984 * @param pEntry The cache entry. 1985 */ 1986 static void kOCEntryCompileIt(PKOCENTRY pEntry) 1987 { 1988 /* 1989 * Delete the object files and free old cpp output that's no longer needed. 1990 */ 1991 if (pEntry->Old.pszObjName) 1992 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir); 1993 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir); 1994 1995 free(pEntry->Old.pszCppMapping); 1996 pEntry->Old.pszCppMapping = NULL; 1997 if (!pEntry->fPipedPreComp && !pEntry->fPipedCompile) 1998 { 1999 free(pEntry->New.pszCppMapping); 2000 pEntry->New.pszCppMapping = NULL; 2001 } 2002 2003 /* 2004 * Do the (re-)compile job. 2005 */ 2006 if (pEntry->fPipedCompile) 2007 { 2008 if ( !pEntry->fPipedPreComp 2009 && !pEntry->New.pszCppMapping) 2010 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */); 2011 InfoMsg(1, "compiling -> '%s'...\n", pEntry->New.pszObjName); 2012 kOCEntrySpawnConsumer(pEntry, pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile, 2013 "compile", kOCEntryCompileProducer); 2014 } 815 2015 else 816 #endif 817 { 818 if (fRedirStdOut) 819 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", pszPreCompName); 820 else 821 kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL); 822 kObjCacheReadPrecompileOutput(pEntry); 823 } 824 kObjCacheCalcChecksum(pEntry); 2016 { 2017 if (pEntry->fPipedPreComp) 2018 kOCEntryWriteCppOutput(pEntry, 1 /* free it */); 2019 InfoMsg(1, "compiling -> '%s'...\n", pEntry->New.pszObjName); 2020 kOCEntrySpawn(pEntry, pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile, "compile", NULL); 2021 } 2022 } 2023 2024 2025 /** 2026 * kOCEntrySpawnTee callback that works sort of like 'tee'. 2027 * 2028 * It will calculate the precompiled output checksum and 2029 * write it to disk while the compiler is busy compiling it. 2030 * 2031 * @param pEntry The cache entry. 2032 * @param fdIn The input handle (connected to the precompiler). 2033 * @param fdOut The output handle (connected to the compiler). 2034 */ 2035 static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut) 2036 { 2037 KOCSUMCTX Ctx; 2038 long cbLeft; 2039 long cbAlloc; 2040 char *psz; 2041 2042 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx); 2043 cbAlloc = pEntry->Old.cbCpp ? (pEntry->Old.cbCpp + 4*1024*1024 + 4096) & ~(4*1024*1024 - 1) : 4*1024*1024; 2044 cbLeft = cbAlloc; 2045 pEntry->New.pszCppMapping = psz = xmalloc(cbAlloc); 2046 for (;;) 2047 { 2048 /* 2049 * Read data from the pipe. 2050 */ 2051 long cbRead = read(fdIn, psz, cbLeft - 1); 2052 if (!cbRead) 2053 break; 2054 if (cbRead < 0) 2055 { 2056 if (errno == EINTR) 2057 continue; 2058 FatalDie("precompile|compile - read(%d,,%ld) failed: %s\n", 2059 fdIn, (long)cbLeft, strerror(errno)); 2060 } 2061 2062 /* 2063 * Process the data. 2064 */ 2065 psz[cbRead] = '\0'; 2066 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead); 2067 do 2068 { 2069 long cbWritten = write(fdOut, psz, cbRead); 2070 if (cbWritten < 0) 2071 { 2072 if (errno == EINTR) 2073 continue; 2074 FatalDie("precompile|compile - write(%d,,%ld) failed: %s\n", fdOut, cbRead, strerror(errno)); 2075 } 2076 psz += cbWritten; 2077 cbRead -= cbWritten; 2078 cbLeft -= cbWritten; 2079 } while (cbRead > 0); 2080 2081 /* 2082 * Expand the buffer? 2083 */ 2084 if (cbLeft <= 1) 2085 { 2086 size_t off = psz - pEntry->New.pszCppMapping; 2087 assert(off == cbAlloc); 2088 cbLeft = 4*1024*1024; 2089 cbAlloc += cbLeft; 2090 pEntry->New.pszCppMapping = xrealloc(pEntry->New.pszCppMapping, cbAlloc); 2091 psz = pEntry->New.pszCppMapping + off; 2092 } 2093 } 2094 2095 close(fdIn); 2096 close(fdOut); 2097 pEntry->New.cbCpp = cbAlloc - cbLeft; 2098 kOCSumFinalize(&pEntry->New.SumHead, &Ctx); 2099 2100 /* 2101 * Write the precompiler output to disk and free the memory it 2102 * occupies while the compiler is busy compiling. 2103 */ 2104 kOCEntryWriteCppOutput(pEntry, 1 /* free it */); 2105 } 2106 2107 2108 /** 2109 * Performs pre-compile and compile in one go (typical clean build scenario). 2110 * 2111 * @param pEntry The cache entry. 2112 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL. 2113 * @param cArgvPreComp The number of arguments. 2114 */ 2115 static void kOCEntryPreCompileAndCompile(PKOCENTRY pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp) 2116 { 2117 if ( pEntry->fPipedCompile 2118 && pEntry->fPipedPreComp) 2119 { 2120 /* 2121 * Clean up old stuff first. 2122 */ 2123 if (pEntry->Old.pszObjName) 2124 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir); 2125 if (pEntry->New.pszObjName) 2126 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir); 2127 if (pEntry->Old.pszCppName) 2128 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir); 2129 if (pEntry->New.pszCppName) 2130 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir); 2131 2132 /* 2133 * Do the actual compile and write the precompiler output to disk. 2134 */ 2135 kOCEntrySpawnTee(pEntry, papszArgvPreComp, cArgvPreComp, 2136 pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile, 2137 "precompile|compile", kOCEntryTeeConsumer); 2138 } 2139 else 2140 { 2141 kOCEntryPreCompile(pEntry, papszArgvPreComp, cArgvPreComp); 2142 kOCEntryCompileIt(pEntry); 2143 } 825 2144 } 826 2145 … … 834 2153 * @parma ppszFile Where to store the start of the filename. 835 2154 */ 836 static int IsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile)2155 static int kOCEntryIsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile) 837 2156 { 838 2157 unsigned iLine; … … 883 2202 * @param piLine The line number count to adjust. 884 2203 */ 885 static const char * FindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine)2204 static const char *kOCEntryFindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine) 886 2205 { 887 2206 unsigned iLine = *piLine; … … 899 2218 psz--; 900 2219 if ( (psz < pszStop || *psz == '\n') 901 && IsLineStatement(pszStart, &iLineTmp, &pszFile))2220 && kOCEntryIsLineStatement(pszStart, &iLineTmp, &pszFile)) 902 2221 { 903 2222 *piLine = iLine + iLineTmp - 1; … … 912 2231 913 2232 /** 914 * Worker for kO bjCacheCompareOldAndNewOutput() that compares the2233 * Worker for kOCEntryCompareOldAndNewOutput() that compares the 915 2234 * precompiled output using a fast but not very good method. 916 2235 * … … 919 2238 * The entry is not updated in any way. 920 2239 */ 921 static int kO bjCacheCompareFast(PCKOBJCACHEpEntry)922 { 923 const char * psz1 = pEntry-> pszNewCppMapping;924 const char * const pszEnd1 = psz1 + pEntry-> cbNewCpp;925 const char * psz2 = pEntry-> pszOldCppMapping;926 const char * const pszEnd2 = psz2 + pEntry-> cbOldCpp;2240 static int kOCEntryCompareFast(PCKOCENTRY pEntry) 2241 { 2242 const char * psz1 = pEntry->New.pszCppMapping; 2243 const char * const pszEnd1 = psz1 + pEntry->New.cbCpp; 2244 const char * psz2 = pEntry->Old.pszCppMapping; 2245 const char * const pszEnd2 = psz2 + pEntry->Old.cbCpp; 927 2246 928 2247 assert(*pszEnd1 == '\0'); … … 986 2305 /* locate the start of that line. */ 987 2306 psz = psz1; 988 while ( psz > pEntry-> pszNewCppMapping2307 while ( psz > pEntry->New.pszCppMapping 989 2308 && psz[-1] != '\n') 990 2309 psz--; … … 1011 2330 iLine1++; 1012 2331 } 1013 else if (*psz == '#' && IsLineStatement(psz, &iLine1, &pszFile1))2332 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine1, &pszFile1)) 1014 2333 { 1015 2334 psz1 = memchr(psz, '\n', pszEnd1 - psz); … … 1053 2372 iLine2++; 1054 2373 } 1055 else if (*psz == '#' && IsLineStatement(psz, &iLine2, &pszFile2))2374 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine2, &pszFile2)) 1056 2375 { 1057 2376 psz2 = memchr(psz, '\n', pszEnd2 - psz); … … 1098 2417 /* Check that we're at the same location now. */ 1099 2418 if (!pszFile1) 1100 pszFile1 = FindFileStatement(pszMismatch1, pEntry->pszNewCppMapping, &iLine1);2419 pszFile1 = kOCEntryFindFileStatement(pszMismatch1, pEntry->New.pszCppMapping, &iLine1); 1101 2420 if (!pszFile2) 1102 pszFile2 = FindFileStatement(pszMismatch2, pEntry->pszOldCppMapping, &iLine2);2421 pszFile2 = kOCEntryFindFileStatement(pszMismatch2, pEntry->Old.pszCppMapping, &iLine2); 1103 2422 if (pszFile1 && pszFile2) 1104 2423 { … … 1138 2457 1139 2458 /** 1140 * Worker for kO bjCacheCompileIfNeeded that compares the2459 * Worker for kOCEntryCompileIfNeeded that compares the 1141 2460 * precompiled output. 1142 2461 * 1143 2462 * @returns 1 if matching, 0 if not matching. 1144 2463 * @param pEntry The entry containing the names of the files to compare. 1145 * This will load the old cpp output (changing pszOldCppName and cbOldCpp). 1146 */ 1147 static int kObjCacheCompareOldAndNewOutput(PKOBJCACHE pEntry) 1148 { 1149 /** @todo do some quick but fancy comparing that determins whether code 1150 * has actually changed or moved. We can ignore declarations and typedefs that 1151 * has just been moved up/down a bit. The typical case is adding a new error 1152 * #define that doesn't influence the current compile job. */ 1153 1154 /* 1155 * Load the old output. 1156 */ 1157 pEntry->pszOldCppMapping = ReadFileInDir(pEntry->pszOldCppName, pEntry->pszDir, &pEntry->cbOldCpp); 1158 if (!pEntry->pszOldCppMapping) 1159 { 1160 kObjCacheVerbose(pEntry, "failed to read old cpp file ('%s' in '%s'): %s\n", 1161 pEntry->pszOldCppName, pEntry->pszDir, strerror(errno)); 2464 * This will load the old cpp output (changing pszOldCppName and Old.cbCpp). 2465 */ 2466 static int kOCEntryCompareOldAndNewOutput(PKOCENTRY pEntry) 2467 { 2468 /* 2469 * I may implement a more sophisticated alternative method later... maybe. 2470 */ 2471 if (kOCEntryReadCppOutput(pEntry, &pEntry->Old, 1 /* nonfatal */) == -1) 1162 2472 return 0; 1163 }1164 1165 /*1166 * I may implement a more sophisticated alternative method later... maybe.1167 */1168 2473 //if () 1169 // return kO bjCacheCompareBest(pEntry);1170 return kO bjCacheCompareFast(pEntry);1171 } 1172 1173 1174 /** 1175 * Worker for kObjCacheCompileIfNeeded that does the actual (re)compilation.1176 * 1177 * @returns 1 if matching, 0 if not matching.2474 // return kOCEntryCompareBest(pEntry); 2475 return kOCEntryCompareFast(pEntry); 2476 } 2477 2478 2479 /** 2480 * Check if re-compilation is required. 2481 * This sets the fNeedCompile flag. 2482 * 1178 2483 * @param pEntry The cache entry. 1179 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL. 1180 * @param cArgvCompile The number of arguments in the vector. 1181 * @param pszObjName The name of the object file. 1182 */ 1183 static void kObjCacheCompileIt(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName) 1184 { 1185 /* 1186 * Delete the old object file and precompiler output. 1187 */ 1188 if (pEntry->pszObjName) 1189 { 1190 UnlinkFileInDir(pEntry->pszObjName, pEntry->pszDir); 1191 pEntry->pszObjName = NULL; 1192 } 1193 pEntry->pszNewObjName = CalcRelativeName(pszObjName, pEntry->pszDir); 1194 1195 /* 1196 * If we executed the precompiled in piped mode we'll have to write the 1197 * precompiler output to disk so the compile has some thing to chew on. 1198 */ 1199 if (pEntry->fPiped) 1200 { 1201 FILE *pFile = FOpenFileInDir(pEntry->pszNewCppName, pEntry->pszDir, "wb"); 1202 if (!pFile) 1203 kObjCacheFatal(pEntry, "failed to create file '%s' in '%s': %s\n", 1204 pEntry->pszNewCppName, pEntry->pszDir, strerror(errno)); 1205 if (fwrite(pEntry->pszNewCppMapping, pEntry->cbNewCpp, 1, pFile) != 1) 1206 kObjCacheFatal(pEntry, "fwrite failed: %s\n", strerror(errno)); 1207 if (fclose(pFile)) 1208 kObjCacheFatal(pEntry, "fclose failed: %s\n", strerror(errno)); 1209 } 1210 1211 /* 1212 * Release buffers we no longer need before starting the compile. 1213 */ 1214 free(pEntry->pszNewCppMapping); 1215 pEntry->pszNewCppMapping = NULL; 1216 free(pEntry->pszOldCppMapping); 1217 pEntry->pszOldCppMapping = NULL; 1218 1219 /* 1220 * Do the recompilation. 1221 */ 1222 kObjCacheVerbose(pEntry, "compiling -> '%s'...\n", pEntry->pszNewObjName); 1223 pEntry->papszArgvCompile = (char **)papszArgvCompile; /* LEAK */ 1224 pEntry->cArgvCompile = cArgvCompile; 1225 kObjCacheSpawn(pEntry, papszArgvCompile, cArgvCompile, "compile", NULL); 1226 } 1227 1228 1229 /** 1230 * Check if (re-)compilation is required and do it. 1231 * 1232 * @returns 1 if matching, 0 if not matching. 1233 * @param pEntry The cache entry. 1234 * @param papszArgvCompile The argument vector for invoking the compiler. The cArgvCompile'th entry must be NULL. 1235 * @param cArgvCompile The number of arguments in the vector. 1236 * @param pszObjName The name of the object file. 1237 */ 1238 static void kObjCacheCompileIfNeeded(PKOBJCACHE pEntry, const char **papszArgvCompile, unsigned cArgvCompile, const char *pszObjName) 1239 { 1240 /* 1241 * Does the object name differ? 1242 */ 1243 if (!pEntry->fNeedCompiling) 1244 { 1245 char *pszTmp = CalcRelativeName(pszObjName, pEntry->pszDir); 1246 if (strcmp(pEntry->pszObjName, pszTmp)) 1247 { 2484 */ 2485 static void kOCEntryCalcRecompile(PKOCENTRY pEntry) 2486 { 2487 if (pEntry->fNeedCompiling) 2488 return; 2489 2490 /* 2491 * Check if the precompiler output differ in any significant way? 2492 */ 2493 if (!kOCSumHasEqualInChain(&pEntry->Old.SumHead, &pEntry->New.SumHead)) 2494 { 2495 InfoMsg(1, "no checksum match - comparing output\n"); 2496 if (!kOCEntryCompareOldAndNewOutput(pEntry)) 1248 2497 pEntry->fNeedCompiling = 1; 1249 kObjCacheVerbose(pEntry, "object name changed '%s' -> '%s'\n", pEntry->pszObjName, pszTmp); 2498 else 2499 kOCSumAddChain(&pEntry->New.SumHead, &pEntry->Old.SumHead); 2500 } 2501 } 2502 2503 2504 /** 2505 * Does this cache entry need compiling or what? 2506 * 2507 * @returns 1 if it does, 0 if it doesn't. 2508 * @param pEntry The cache entry in question. 2509 */ 2510 static int kOCEntryNeedsCompiling(PCKOCENTRY pEntry) 2511 { 2512 return pEntry->fNeedCompiling; 2513 } 2514 2515 2516 /** 2517 * Worker function for kOCEntryCopy. 2518 * 2519 * @param pEntry The entry we're coping to, which pszTo is relative to. 2520 * @param pszTo The destination. 2521 * @param pszFrom The source. This path will be freed. 2522 */ 2523 static void kOCEntryCopyFile(PCKOCENTRY pEntry, const char *pszTo, char *pszSrc) 2524 { 2525 char *pszDst = MakePathFromDirAndFile(pszTo, pEntry->pszDir); 2526 char *pszBuf = xmalloc(256 * 1024); 2527 char *psz; 2528 int fdSrc; 2529 int fdDst; 2530 2531 /* 2532 * Open the files. 2533 */ 2534 fdSrc = open(pszSrc, O_RDONLY | O_BINARY); 2535 if (fdSrc == -1) 2536 FatalDie("failed to open '%s': %s\n", pszSrc, strerror(errno)); 2537 2538 unlink(pszDst); 2539 fdDst = open(pszDst, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); 2540 if (fdDst == -1) 2541 FatalDie("failed to create '%s': %s\n", pszDst, strerror(errno)); 2542 2543 /* 2544 * Copy them. 2545 */ 2546 for (;;) 2547 { 2548 /* read a chunk. */ 2549 long cbRead = read(fdSrc, pszBuf, 256*1024); 2550 if (cbRead < 0) 2551 { 2552 if (errno == EINTR) 2553 continue; 2554 if (eof(fdSrc)) 2555 break; 2556 FatalDie("read '%s' failed: %s\n", pszSrc, strerror(errno)); 1250 2557 } 1251 free(pszTmp); 1252 } 1253 1254 /* 1255 * Does the compile command differ? 1256 * TODO: Ignore irrelevant options here (like warning level). 1257 */ 1258 if ( !pEntry->fNeedCompiling 1259 && pEntry->cArgvCompile != cArgvCompile) 1260 { 1261 pEntry->fNeedCompiling = 1; 1262 kObjCacheVerbose(pEntry, "compile argument count changed\n"); 1263 } 1264 if (!pEntry->fNeedCompiling) 1265 { 1266 unsigned i; 1267 for (i = 0; i < cArgvCompile; i++) 1268 if (strcmp(papszArgvCompile[i], pEntry->papszArgvCompile[i])) 2558 2559 /* write the chunk. */ 2560 psz = pszBuf; 2561 do 2562 { 2563 long cbWritten = write(fdDst, psz, cbRead); 2564 if (cbWritten < 0) 1269 2565 { 1270 pEntry->fNeedCompiling = 1; 1271 kObjCacheVerbose(pEntry, "compile argument differs (%#d)\n", i); 2566 if (errno == EINTR) 2567 continue; 2568 FatalDie("write '%s' failed: %s\n", pszSrc, strerror(errno)); 2569 } 2570 psz += cbWritten; 2571 cbRead -= cbWritten; 2572 } while (cbRead > 0); 2573 } 2574 2575 /* cleanup */ 2576 if (close(fdDst) != 0) 2577 FatalDie("closing '%s' failed: %s\n", pszDst, strerror(errno)); 2578 close(fdSrc); 2579 free(pszBuf); 2580 free(pszDst); 2581 free(pszSrc); 2582 } 2583 2584 2585 /** 2586 * Copies the object (and whatever else) from one cache entry to another. 2587 * 2588 * This is called when a matching cache entry has been found and we don't 2589 * need to recompile anything. 2590 * 2591 * @param pEntry The entry to copy to. 2592 * @param pFrom The entry to copy from. 2593 */ 2594 static void kOCEntryCopy(PKOCENTRY pEntry, PCKOCENTRY pFrom) 2595 { 2596 kOCEntryCopyFile(pEntry, pEntry->New.pszObjName, 2597 MakePathFromDirAndFile(pFrom->New.pszObjName 2598 ? pFrom->New.pszObjName : pFrom->Old.pszObjName, 2599 pFrom->pszDir)); 2600 } 2601 2602 2603 /** 2604 * Gets the absolute path to the cache entry. 2605 * 2606 * @returns absolute path to the cache entry. 2607 * @param pEntry The cache entry in question. 2608 */ 2609 static const char *kOCEntryAbsPath(PCKOCENTRY pEntry) 2610 { 2611 return pEntry->pszAbsPath; 2612 } 2613 2614 2615 2616 2617 2618 2619 /** 2620 * Digest of one cache entry. 2621 * 2622 * This contains all the information required to find a matching 2623 * cache entry without having to open each of the files. 2624 */ 2625 typedef struct KOCDIGEST 2626 { 2627 /** The relative path to the entry. Optional if pszAbsPath is set. */ 2628 char *pszRelPath; 2629 /** The absolute path to the entry. Optional if pszRelPath is set. */ 2630 char *pszAbsPath; 2631 /** The target os/arch identifier. */ 2632 char *pszTarget; 2633 /** A unique number assigned to the entry when it's (re)-inserted 2634 * into the cache. This is used for simple consitency checking. */ 2635 uint32_t uKey; 2636 /** The checksum of the compile argument vector. */ 2637 KOCSUM SumCompArgv; 2638 /** The list of precompiler output checksums that's . */ 2639 KOCSUM SumHead; 2640 } KOCDIGEST; 2641 /** Pointer to a file digest. */ 2642 typedef KOCDIGEST *PKOCDIGEST; 2643 /** Pointer to a const file digest. */ 2644 typedef KOCDIGEST *PCKOCDIGEST; 2645 2646 2647 /** 2648 * Initializes the specified digest. 2649 * 2650 * @param pDigest The digest. 2651 */ 2652 static void kOCDigestInit(PKOCDIGEST pDigest) 2653 { 2654 memset(pDigest, 0, sizeof(*pDigest)); 2655 kOCSumInit(&pDigest->SumHead); 2656 } 2657 2658 2659 /** 2660 * Initializes the digest for the specified entry. 2661 * 2662 * @param pDigest The (uninitialized) digest. 2663 * @param pEntry The entry. 2664 */ 2665 static void kOCDigestInitFromEntry(PKOCDIGEST pDigest, PCKOCENTRY pEntry) 2666 { 2667 kOCDigestInit(pDigest); 2668 2669 pDigest->uKey = pEntry->uKey; 2670 2671 kOCSumInit(&pDigest->SumCompArgv); 2672 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv)) 2673 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv); 2674 else 2675 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->Old.SumCompArgv); 2676 2677 kOCSumInit(&pDigest->SumHead); 2678 if (!kOCSumIsEmpty(&pEntry->New.SumHead)) 2679 kOCSumAddChain(&pDigest->SumHead, &pEntry->New.SumHead); 2680 else 2681 kOCSumAddChain(&pDigest->SumHead, &pEntry->Old.SumHead); 2682 2683 /** @todo implement selective relative path support. */ 2684 pDigest->pszRelPath = NULL; 2685 pDigest->pszAbsPath = xstrdup(kOCEntryAbsPath(pEntry)); 2686 } 2687 2688 2689 /** 2690 * Purges a digest, freeing all resources and returning 2691 * it to the initial state. 2692 * 2693 * @param pDigest The digest. 2694 */ 2695 static void kOCDigestPurge(PKOCDIGEST pDigest) 2696 { 2697 free(pDigest->pszRelPath); 2698 free(pDigest->pszAbsPath); 2699 free(pDigest->pszTarget); 2700 pDigest->pszTarget = pDigest->pszAbsPath = pDigest->pszRelPath = NULL; 2701 pDigest->uKey = 0; 2702 kOCSumDeleteChain(&pDigest->SumCompArgv); 2703 kOCSumDeleteChain(&pDigest->SumHead); 2704 } 2705 2706 2707 /** 2708 * Returns the absolute path to the entry, calculating 2709 * the path if necessary. 2710 * 2711 * @returns absolute path. 2712 * @param pDigest The digest. 2713 * @param pszDir The cache directory that it might be relative to. 2714 */ 2715 static const char *kOCDigestAbsPath(PCKOCDIGEST pDigest, const char *pszDir) 2716 { 2717 if (!pDigest->pszAbsPath) 2718 { 2719 char *pszPath = MakePathFromDirAndFile(pDigest->pszRelPath, pszDir); 2720 ((PKOCDIGEST)pDigest)->pszAbsPath = AbsPath(pszPath); 2721 free(pszPath); 2722 } 2723 return pDigest->pszAbsPath; 2724 } 2725 2726 2727 /** 2728 * Checks that the digest matches the 2729 * 2730 * @returns 1 if valid, 0 if invalid in some way. 2731 * 2732 * @param pDigest The digest to validate. 2733 * @param pEntry What to validate it against. 2734 */ 2735 static int kOCDigestIsValid(PCKOCDIGEST pDigest, PCKOCENTRY pEntry) 2736 { 2737 PCKOCSUM pSum; 2738 PCKOCSUM pSumEntry; 2739 2740 if (pDigest->uKey != pEntry->uKey) 2741 return 0; 2742 2743 if (!kOCSumIsEqual(&pDigest->SumCompArgv, 2744 kOCSumIsEmpty(&pEntry->New.SumCompArgv) 2745 ? &pEntry->Old.SumCompArgv : &pEntry->New.SumCompArgv)) 2746 return 0; 2747 2748 if (strcmp(pDigest->pszTarget, pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget)) 2749 return 0; 2750 2751 /* match the checksums */ 2752 pSumEntry = kOCSumIsEmpty(&pEntry->New.SumHead) 2753 ? &pEntry->Old.SumHead : &pEntry->New.SumHead; 2754 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext) 2755 if (!kOCSumHasEqualInChain(pSumEntry, pSum)) 2756 return 0; 2757 2758 return 1; 2759 } 2760 2761 2762 2763 2764 2765 /** 2766 * The structure for the central cache entry. 2767 */ 2768 typedef struct KOBJCACHE 2769 { 2770 /** The entry name. */ 2771 const char *pszName; 2772 /** The dir that relative names in the digest are relative to. */ 2773 char *pszDir; 2774 /** The absolute path. */ 2775 char *pszAbsPath; 2776 2777 /** The cache file descriptor. */ 2778 int fd; 2779 /** Whether it's currently locked or not. */ 2780 unsigned fLocked; 2781 /** Whether the cache file is dirty and needs writing back. */ 2782 unsigned fDirty; 2783 /** Whether this is a new cache or not. */ 2784 unsigned fNewCache; 2785 2786 /** The cache file generation. */ 2787 uint32_t uGeneration; 2788 /** The next valid key. (Determin at load time.) */ 2789 uint32_t uNextKey; 2790 2791 /** Number of digests in paDigests. */ 2792 unsigned cDigests; 2793 /** Array of digests for the KOCENTRY objects in the cache. */ 2794 PKOCDIGEST paDigests; 2795 2796 } KOBJCACHE; 2797 /** Pointer to a cache. */ 2798 typedef KOBJCACHE *PKOBJCACHE; 2799 /** Pointer to a const cache. */ 2800 typedef KOBJCACHE const *PCKOBJCACHE; 2801 2802 2803 /** 2804 * Creates an empty cache. 2805 * 2806 * This doesn't touch the file system, it just create the data structure. 2807 * 2808 * @returns Pointer to a cache. 2809 * @param pszCacheFile The cache file. 2810 */ 2811 static PKOBJCACHE kObjCacheCreate(const char *pszCacheFile) 2812 { 2813 PKOBJCACHE pCache; 2814 size_t off; 2815 2816 /* 2817 * Allocate an empty entry. 2818 */ 2819 pCache = xmallocz(sizeof(*pCache)); 2820 pCache->fd = -1; 2821 2822 /* 2823 * Setup the directory and cache file name. 2824 */ 2825 pCache->pszAbsPath = AbsPath(pszCacheFile); 2826 pCache->pszName = FindFilenameInPath(pCache->pszAbsPath); 2827 off = pCache->pszName - pCache->pszAbsPath; 2828 if (!off) 2829 FatalDie("Failed to find abs path for '%s'!\n", pszCacheFile); 2830 pCache->pszDir = xmalloc(off - 1); 2831 memcpy(pCache->pszDir, pCache->pszAbsPath, off); 2832 pCache->pszDir[off - 1] = '\0'; 2833 2834 return pCache; 2835 } 2836 2837 2838 /** 2839 * Purges the data in the cache object. 2840 * 2841 * @param pCache The cache object. 2842 */ 2843 static void kObjCachePurge(PKOBJCACHE pCache) 2844 { 2845 while (pCache->cDigests > 0) 2846 kOCDigestPurge(&pCache->paDigests[--pCache->cDigests]); 2847 free(pCache->paDigests); 2848 pCache->paDigests = NULL; 2849 pCache->uGeneration = 0; 2850 pCache->uNextKey = 0; 2851 } 2852 2853 2854 /** 2855 * (Re-)reads the file. 2856 * 2857 * @param pCache The cache to (re)-read. 2858 */ 2859 static void kObjCacheRead(PKOBJCACHE pCache) 2860 { 2861 FILE *pFile; 2862 unsigned i; 2863 int fBad = 0; 2864 2865 /* 2866 * Rewind the file and associate it with a buffered file stream. 2867 */ 2868 if (lseek(pCache->fd, 0, SEEK_SET) == -1) 2869 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno)); 2870 pFile = fdopen(pCache->fd, "rb"); 2871 if (!pFile) 2872 FatalDie("fdopen(cache-fd,rb) failed: %s\n", strerror(errno)); 2873 2874 /* 2875 * Read magic and generation. 2876 */ 2877 if ( !fgets(g_szLine, sizeof(g_szLine), pFile) 2878 || strcmp(g_szLine, "magic=kObjCache-v0.1.0\n")) 2879 { 2880 InfoMsg(1, "bad cache file (magic)\n"); 2881 fBad = 1; 2882 } 2883 else if ( !fgets(g_szLine, sizeof(g_szLine), pFile) 2884 || strncmp(g_szLine, "generation=", sizeof("generation=") - 1)) 2885 { 2886 InfoMsg(1, "bad cache file (generation)\n"); 2887 fBad = 1; 2888 } 2889 else if ( pCache->uGeneration 2890 && pCache->uGeneration == atol(&g_szLine[sizeof("generation=") - 1])) 2891 { 2892 InfoMsg(1, "cache file unchanged\n"); 2893 fBad = 0; 2894 } 2895 else 2896 { 2897 int fBadBeforeMissing; 2898 2899 /* 2900 * Read everything (anew). 2901 */ 2902 kObjCachePurge(pCache); 2903 do 2904 { 2905 PKOCDIGEST pDigest; 2906 char *pszNl; 2907 char *pszVal; 2908 char *psz; 2909 2910 /* Split the line and drop the trailing newline. */ 2911 pszVal = strchr(g_szLine, '='); 2912 if ((fBad = pszVal == NULL)) 2913 break; 2914 *pszVal++ = '\0'; 2915 2916 pszNl = strchr(pszVal, '\n'); 2917 if (pszNl) 2918 *pszNl = '\0'; 2919 2920 /* digest '#'? */ 2921 psz = strchr(g_szLine, '#'); 2922 if (psz) 2923 { 2924 i = strtoul(psz, &psz, 0); 2925 if ((fBad = psz && *psz)) 2926 break; 2927 if ((fBad = i >= pCache->cDigests)) 2928 break; 2929 pDigest = &pCache->paDigests[i]; 2930 } 2931 else 2932 pDigest = NULL; 2933 2934 2935 /* string case on value name. */ 2936 if (!strncmp(g_szLine, "sum-#", sizeof("sum-sum-#") - 1)) 2937 { 2938 KOCSUM Sum; 2939 if ((fBad = kOCSumInitFromString(&Sum, pszVal) != 0)) 2940 break; 2941 kOCSumAdd(&pDigest->SumHead, &Sum); 2942 } 2943 else if (!strncmp(g_szLine, "digest-abs-#", sizeof("digest-abs-#") - 1)) 2944 { 2945 if ((fBad = pDigest->pszAbsPath != NULL)) 2946 break; 2947 pDigest->pszAbsPath = xstrdup(pszVal); 2948 } 2949 else if (!strncmp(g_szLine, "digest-rel-#", sizeof("digest-rel-#") - 1)) 2950 { 2951 if ((fBad = pDigest->pszRelPath != NULL)) 2952 break; 2953 pDigest->pszRelPath = xstrdup(pszVal); 2954 } 2955 else if (!strncmp(g_szLine, "key-#", sizeof("key-#") - 1)) 2956 { 2957 if ((fBad = pDigest->uKey != 0)) 2958 break; 2959 pDigest->uKey = strtoul(pszVal, &psz, 0); 2960 if ((fBad = psz && *psz)) 2961 break; 2962 if (pDigest->uKey >= pCache->uNextKey) 2963 pCache->uNextKey = pDigest->uKey + 1; 2964 } 2965 else if (!strncmp(g_szLine, "comp-argv-sum-#", sizeof("comp-argv-sum-#") - 1)) 2966 { 2967 if ((fBad = !kOCSumIsEmpty(&pDigest->SumCompArgv))) 2968 break; 2969 if ((fBad = kOCSumInitFromString(&pDigest->SumCompArgv, pszVal) != 0)) 2970 break; 2971 } 2972 else if (!strncmp(g_szLine, "target-#", sizeof("target-#") - 1)) 2973 { 2974 if ((fBad = pDigest->pszTarget != NULL)) 2975 break; 2976 pDigest->pszTarget = xstrdup(pszVal); 2977 } 2978 else if (!strcmp(g_szLine, "digests")) 2979 { 2980 if ((fBad = pCache->paDigests != NULL)) 2981 break; 2982 pCache->cDigests = strtoul(pszVal, &psz, 0); 2983 if ((fBad = psz && *psz)) 2984 break; 2985 i = (pCache->cDigests + 4) & ~3; 2986 pCache->paDigests = xmalloc(i * sizeof(pCache->paDigests[0])); 2987 for (i = 0; i < pCache->cDigests; i++) 2988 kOCDigestInit(&pCache->paDigests[i]); 2989 } 2990 else if (!strcmp(g_szLine, "generation")) 2991 { 2992 if ((fBad = pCache->uGeneration != 0)) 2993 break; 2994 pCache->uGeneration = strtoul(pszVal, &psz, 0); 2995 if ((fBad = psz && *psz)) 2996 break; 2997 } 2998 else if (!strcmp(g_szLine, "the-end")) 2999 { 3000 fBad = strcmp(pszVal, "fine"); 1272 3001 break; 1273 3002 } 1274 } 1275 1276 /* 1277 * Does the object file exist? 1278 */ 1279 if ( !pEntry->fNeedCompiling 1280 && !DoesFileInDirExist(pEntry->pszObjName, pEntry->pszDir)) 1281 { 1282 pEntry->fNeedCompiling = 1; 1283 kObjCacheVerbose(pEntry, "object file doesn't exist\n"); 1284 } 1285 1286 /* 1287 * Does the precompiled output differ in any significant way? 1288 */ 1289 if (!pEntry->fNeedCompiling) 1290 { 1291 int fFound = 0; 1292 PCKOCSUM pSum; 1293 for (pSum = &pEntry->SumHead; pSum; pSum = pSum->pNext) 1294 if (kObjCacheSumIsEqual(pSum, &pEntry->NewSum)) 3003 else 1295 3004 { 1296 f Found = 1;3005 fBad = 1; 1297 3006 break; 1298 3007 } 1299 if (!fFound) 1300 { 1301 kObjCacheVerbose(pEntry, "no checksum match - comparing output\n"); 1302 if (!kObjCacheCompareOldAndNewOutput(pEntry)) 1303 pEntry->fNeedCompiling = 1; 1304 else 3008 } while (fgets(g_szLine, sizeof(g_szLine), pFile)); 3009 3010 /* 3011 * Did we find everything? 3012 */ 3013 fBadBeforeMissing = fBad; 3014 if ( !fBad 3015 && !pCache->uGeneration) 3016 fBad = 1; 3017 if (!fBad) 3018 for (i = 0; i < pCache->cDigests; i++) 1305 3019 { 1306 /* insert the sum into the list. */ 1307 pEntry->NewSum.pNext = pEntry->SumHead.pNext; 1308 pEntry->SumHead.pNext = &pEntry->NewSum; 3020 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumCompArgv))) 3021 break; 3022 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumHead))) 3023 break; 3024 if ((fBad = pCache->paDigests[i].uKey == 0)) 3025 break; 3026 if ((fBad = pCache->paDigests[i].pszAbsPath == NULL 3027 && pCache->paDigests[i].pszRelPath == NULL)) 3028 break; 3029 if ((fBad = pCache->paDigests[i].pszTarget == NULL)) 3030 break; 1309 3031 } 3032 if (fBad) 3033 InfoMsg(1, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff"); 3034 else if (ferror(pFile)) 3035 { 3036 InfoMsg(1, "cache file read error\n"); 3037 fBad = 1; 1310 3038 } 1311 3039 } 1312 1313 /* 1314 * Discard the old precompiled output if it's no longer needed. 1315 */ 1316 if ( pEntry->pszOldCppName 1317 && ( !pEntry->fPiped 1318 || pEntry->fNeedCompiling)) 1319 { 1320 UnlinkFileInDir(pEntry->pszOldCppName, pEntry->pszDir); 1321 free(pEntry->pszOldCppName); 1322 pEntry->pszOldCppName = NULL; 1323 } 1324 1325 /* 1326 * Do the compliation if found necessary. 1327 */ 1328 if (pEntry->fNeedCompiling) 1329 kObjCacheCompileIt(pEntry, papszArgvCompile, cArgvCompile, pszObjName); 1330 } 1331 1332 1333 /** 1334 * Gets the absolute path 1335 * 1336 * @returns A new heap buffer containing the absolute path. 1337 * @param pszPath The path to make absolute. (Readonly) 1338 */ 1339 static char *AbsPath(const char *pszPath) 1340 { 1341 char szTmp[PATH_MAX]; 1342 #if defined(__OS2__) || defined(__WIN__) 1343 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))) 1344 return xstrdup(pszPath); 3040 if (fBad) 3041 { 3042 kObjCachePurge(pCache); 3043 pCache->fNewCache = 1; 3044 } 3045 3046 /* 3047 * Close the stream. 3048 */ 3049 fclose(pFile); 3050 } 3051 3052 3053 /** 3054 * Re-writes the cache file. 3055 * 3056 * @param pCache The cache to commit and unlock. 3057 */ 3058 static void kObjCacheWrite(PKOBJCACHE pCache) 3059 { 3060 FILE *pFile; 3061 unsigned i; 3062 off_t cb; 3063 assert(pCache->fLocked); 3064 assert(pCache->fDirty); 3065 3066 /* 3067 * Rewind the file and associate it with a buffered file stream. 3068 */ 3069 if (lseek(pCache->fd, 0, SEEK_SET) == -1) 3070 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno)); 3071 pFile = fdopen(pCache->fd, "w+b"); 3072 if (!pFile) 3073 FatalDie("fdopen(cache-fd,w+b) failed: %s\n", strerror(errno)); 3074 3075 /* 3076 * Write the header. 3077 */ 3078 pCache->uGeneration++; 3079 fprintf(pFile, 3080 "magic=kObjCache-v0.1.0\n" 3081 "generation=%d\n" 3082 "digests=%d\n", 3083 pCache->uGeneration, 3084 pCache->cDigests); 3085 3086 /* 3087 * Write the digests. 3088 */ 3089 for (i = 0; pCache->cDigests; i++) 3090 { 3091 PCKOCDIGEST pDigest = &pCache->paDigests[i]; 3092 PKOCSUM pSum; 3093 3094 if (pDigest->pszAbsPath) 3095 fprintf(pFile, "digest-abs-#%u=%s\n", i, pDigest->pszAbsPath); 3096 if (pDigest->pszRelPath) 3097 fprintf(pFile, "digest-rel-#%u=%s\n", i, pDigest->pszRelPath); 3098 fprintf(pFile, "key-#%u=%u\n", i, pDigest->uKey); 3099 fprintf(pFile, "target-#%u=%s\n", i, pDigest->pszTarget); 3100 fprintf(pFile, "comp-argv-sum-#%u=", i); 3101 kOCSumFPrintf(&pDigest->SumCompArgv, pFile); 3102 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext) 3103 { 3104 fprintf(pFile, "sum-#%u=", i); 3105 kOCSumFPrintf(pSum, pFile); 3106 } 3107 } 3108 3109 /* 3110 * Close the stream and unlock fhe file. 3111 * (Closing the stream shouldn't close the file handle IIRC...) 3112 */ 3113 fprintf(pFile, "the-end=fine\n"); 3114 fflush(pFile); 3115 if ( fflush(pFile) < 0 3116 || ferror(pFile)) 3117 { 3118 int iErr = errno; 3119 fclose(pFile); 3120 UnlinkFileInDir(pCache->pszName, pCache->pszDir); 3121 FatalDie("Stream error occured while writing '%s' in '%s': %s\n", 3122 pCache->pszName, pCache->pszDir, strerror(iErr)); 3123 } 3124 fclose(pFile); 3125 3126 cb = lseek(pCache->fd, 0, SEEK_CUR); 3127 if (cb == -1) 3128 FatalDie("lseek(cache-file,0,CUR) failed: %s\n", strerror(errno)); 3129 #if defined(__WIN__) 3130 if (_chsize(pCache->fd, cb) == -1) 1345 3131 #else 1346 if (!realpath(pszPath, szTmp)) 1347 return xstrdup(pszPath); 3132 if (ftruncate(pCache->fd, cb) == -1) 1348 3133 #endif 1349 return xstrdup(szTmp); 1350 } 1351 1352 1353 /** 1354 * Utility function that finds the filename part in a path. 1355 * 1356 * @returns Pointer to the file name part (this may be ""). 1357 * @param pszPath The path to parse. 1358 */ 1359 static const char *FindFilenameInPath(const char *pszPath) 1360 { 1361 const char *pszFilename = strchr(pszPath, '\0') - 1; 1362 while ( pszFilename > pszPath 1363 && !IS_SLASH_DRV(pszFilename[-1])) 1364 pszFilename--; 1365 return pszFilename; 1366 } 1367 1368 1369 /** 1370 * Utility function that combines a filename and a directory into a path. 1371 * 1372 * @returns malloced buffer containing the result. 1373 * @param pszName The file name. 1374 * @param pszDir The directory path. 1375 */ 1376 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir) 1377 { 1378 size_t cchName = strlen(pszName); 1379 size_t cchDir = strlen(pszDir); 1380 char *pszBuf = xmalloc(cchName + cchDir + 2); 1381 memcpy(pszBuf, pszDir, cchDir); 1382 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1])) 1383 pszBuf[cchDir++] = PATH_SLASH; 1384 memcpy(pszBuf + cchDir, pszName, cchName + 1); 1385 return pszBuf; 1386 } 1387 1388 1389 /** 1390 * Compares two path strings to see if they are identical. 1391 * 1392 * This doesn't do anything fancy, just the case ignoring and 1393 * slash unification. 1394 * 1395 * @returns 1 if equal, 0 otherwise. 1396 * @param pszPath1 The first path. 1397 * @param pszPath2 The second path. 1398 * @param cch The number of characters to compare. 1399 */ 1400 static int ArePathsIdentical(const char *pszPath1, const char *pszPath2, size_t cch) 1401 { 1402 #if defined(__OS2__) || defined(__WIN__) 1403 if (strnicmp(pszPath1, pszPath2, cch)) 1404 { 1405 /* Slashes may differ, compare char by char. */ 1406 const char *psz1 = pszPath1; 1407 const char *psz2 = pszPath2; 1408 for (;cch; psz1++, psz2++, cch--) 1409 { 1410 if (*psz1 != *psz2) 1411 { 1412 if ( tolower(*psz1) != tolower(*psz2) 1413 && toupper(*psz1) != toupper(*psz2) 1414 && *psz1 != '/' 1415 && *psz1 != '\\' 1416 && *psz2 != '/' 1417 && *psz2 != '\\') 1418 return 0; 1419 } 3134 FatalDie("file truncation failed: %s\n", strerror(errno)); 3135 InfoMsg(1, "Wrote '%s' in '%s', %d bytes\n", pCache->pszName, pCache->pszDir, cb); 3136 } 3137 3138 3139 /** 3140 * Cleans out all invalid digests.s 3141 * 3142 * This is done periodically from the unlock routine to make 3143 * sure we don't accidentally accumulate stale digests. 3144 * 3145 * @param pCache The cache to chek. 3146 */ 3147 static void kObjCacheClean(PKOBJCACHE pCache) 3148 { 3149 unsigned i = pCache->cDigests; 3150 while (i-- > 0) 3151 { 3152 /* 3153 * Try open it and purge it if it's bad. 3154 * (We don't kill the entry file because that's kmk clean's job.) 3155 */ 3156 PCKOCDIGEST pDigest = &pCache->paDigests[i]; 3157 PKOCENTRY pEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir)); 3158 kOCEntryRead(pEntry); 3159 if ( !kOCEntryCheck(pEntry) 3160 || !kOCDigestIsValid(pDigest, pEntry)) 3161 { 3162 unsigned cLeft; 3163 kOCDigestPurge(pDigest); 3164 3165 pCache->cDigests--; 3166 cLeft = pCache->cDigests - i; 3167 if (cLeft) 3168 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest)); 3169 3170 pCache->fDirty = 1; 1420 3171 } 1421 } 1422 return 1; 3172 kOCEntryDestroy(pEntry); 3173 } 3174 } 3175 3176 3177 /** 3178 * Locks the cache for exclusive access. 3179 * 3180 * This will open the file if necessary and lock the entire file 3181 * using the best suitable platform API (tricky). 3182 * 3183 * @param pCache The cache to lock. 3184 */ 3185 static void kObjCacheLock(PKOBJCACHE pCache) 3186 { 3187 struct stat st; 3188 assert(!pCache->fLocked); 3189 3190 /* 3191 * Open it? 3192 */ 3193 if (pCache->fd < 0) 3194 { 3195 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666); 3196 if (pCache->fd == -1) 3197 { 3198 MakePath(pCache->pszDir); 3199 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666); 3200 if (pCache->fd == -1) 3201 FatalDie("Failed to create '%s' in '%s': %s\n", pCache->pszName, pCache->pszDir, strerror(errno)); 3202 } 3203 } 3204 3205 /* 3206 * Lock it. 3207 */ 3208 #if defined(__WIN__) 3209 if (!LockFileEx((HANDLE)_get_osfhandle(pCache->fd), LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, 0, NULL)) 3210 FatalDie("Failed to lock the cache file: Windows Error %d\n", GetLastError()); 1423 3211 #else 1424 return !strncmp(pszPath1, pszPath2, cch); 3212 # error port me.... 1425 3213 #endif 1426 } 1427 1428 1429 /** 1430 * Calculate how to get to pszPath from pszDir. 1431 * 1432 * @returns The relative path from pszDir to path pszPath. 1433 * @param pszPath The path to the object. 1434 * @param pszDir The directory it shall be relative to. 1435 */ 1436 static char *CalcRelativeName(const char *pszPath, const char *pszDir) 1437 { 1438 char *pszRet = NULL; 1439 char *pszAbsPath = NULL; 1440 size_t cchDir = strlen(pszDir); 1441 1442 /* 1443 * This is indeed a bit tricky, so we'll try the easy way first... 1444 */ 1445 if (ArePathsIdentical(pszPath, pszDir, cchDir)) 1446 { 1447 if (pszPath[cchDir]) 1448 pszRet = (char *)pszPath + cchDir; 1449 else 1450 pszRet = "./"; 1451 } 3214 pCache->fLocked = 1; 3215 3216 /* 3217 * Check for new cache and read it it's an existing cache. 3218 * 3219 * There is no point in initializing a new cache until we've finished 3220 * compiling and has something to put into it, so we'll leave it as a 3221 * 0 byte file. 3222 */ 3223 if (fstat(pCache->fd, &st) == -1) 3224 FatalDie("fstat(cache-fd) failed: %s\n", strerror(errno)); 3225 if (st.st_size) 3226 kObjCacheRead(pCache); 1452 3227 else 1453 { 1454 pszAbsPath = AbsPath(pszPath); 1455 if (ArePathsIdentical(pszAbsPath, pszDir, cchDir)) 1456 { 1457 if (pszPath[cchDir]) 1458 pszRet = pszAbsPath + cchDir; 1459 else 1460 pszRet = "./"; 3228 pCache->fNewCache = 1; 3229 } 3230 3231 3232 /** 3233 * Unlocks the cache (without writing anything back). 3234 * 3235 * @param pCache The cache to unlock. 3236 */ 3237 static void kObjCacheUnlock(PKOBJCACHE pCache) 3238 { 3239 assert(pCache->fLocked); 3240 3241 /* 3242 * Write it back if it's dirty. 3243 */ 3244 if (pCache->fDirty) 3245 { 3246 if ( pCache->cDigests >= 16 3247 && (pCache->uGeneration % 19) == 19) 3248 kObjCacheClean(pCache); 3249 kObjCacheWrite(pCache); 3250 pCache->fDirty = 0; 3251 } 3252 3253 /* 3254 * Lock it. 3255 */ 3256 #if defined(__WIN__) 3257 if (!UnlockFileEx((HANDLE)_get_osfhandle(pCache->fd), 0, ~0U, 0, NULL)) 3258 FatalDie("Failed to unlock the cache file: Windows Error %d\n", GetLastError()); 3259 #else 3260 # error port me.... 3261 #endif 3262 pCache->fLocked = 0; 3263 } 3264 3265 3266 /** 3267 * Removes the entry from the cache. 3268 * 3269 * The entry doesn't need to be in the cache. 3270 * The cache entry (file) itself is not touched. 3271 * 3272 * @param pCache The cache. 3273 * @param pEntry The entry. 3274 */ 3275 static void kObjCacheRemoveEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry) 3276 { 3277 unsigned i = pCache->cDigests; 3278 while (i-- > 0) 3279 { 3280 PKOCDIGEST pDigest = &pCache->paDigests[i]; 3281 if (ArePathsIdentical(kOCDigestAbsPath(pDigest, pCache->pszDir), 3282 kOCEntryAbsPath(pEntry), ~0U)) 3283 { 3284 unsigned cLeft; 3285 kOCDigestPurge(pDigest); 3286 3287 pCache->cDigests--; 3288 cLeft = pCache->cDigests - i; 3289 if (cLeft) 3290 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest)); 3291 3292 pCache->fDirty = 1; 1461 3293 } 1462 3294 } 1463 if (pszRet) 1464 { 1465 while (IS_SLASH_DRV(*pszRet)) 1466 pszRet++; 1467 pszRet = xstrdup(pszRet); 1468 free(pszAbsPath); 1469 return pszRet; 1470 } 1471 1472 /* 1473 * Damn, it's gonna be complicated. Deal with that later. 1474 */ 1475 fprintf(stderr, "kObjCache: complicated relative path stuff isn't implemented yet. sorry.\n"); 1476 exit(1); 3295 } 3296 3297 3298 /** 3299 * Inserts the entry into the cache. 3300 * 3301 * The cache entry (file) itself is not touched by this operation, 3302 * the pEntry object otoh is. 3303 * 3304 * @param pCache The cache. 3305 * @param pEntry The entry. 3306 */ 3307 static void kObjCacheInsertEntry(PKOBJCACHE pCache, PKOCENTRY pEntry) 3308 { 3309 unsigned i; 3310 3311 /* 3312 * Find a new key. 3313 */ 3314 pEntry->uKey = pCache->uNextKey++; 3315 i = pCache->cDigests; 3316 while (i-- > 0) 3317 if (pCache->paDigests[i].uKey == pEntry->uKey) 3318 { 3319 pEntry->uKey = pCache->uNextKey++; 3320 if (!pEntry->uKey) 3321 pEntry->uKey = pCache->uNextKey++; 3322 i = pCache->cDigests; 3323 } 3324 3325 /* 3326 * Reallocate the digest array? 3327 */ 3328 if ( !(pCache->cDigests & 3) 3329 && (pCache->cDigests || !pCache->paDigests)) 3330 pCache->paDigests = xrealloc(pCache->paDigests, sizeof(pCache->paDigests[0]) * pCache->cDigests + 4); 3331 3332 /* 3333 * Create a new digest. 3334 */ 3335 i = pCache->cDigests++; 3336 kOCDigestInitFromEntry(&pCache->paDigests[i], pEntry); 3337 3338 pCache->fDirty = 1; 3339 } 3340 3341 3342 /** 3343 * Find a matching cache entry. 3344 */ 3345 static PKOCENTRY kObjCacheFindMatchingEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry) 3346 { 3347 unsigned i = pCache->cDigests; 3348 3349 assert(pEntry->fNeedCompiling); 3350 assert(!kOCSumIsEmpty(&pEntry->New.SumCompArgv)); 3351 assert(!kOCSumIsEmpty(&pEntry->New.SumHead)); 3352 3353 while (i-- > 0) 3354 { 3355 /* 3356 * Matching? 3357 */ 3358 PCKOCDIGEST pDigest = &pCache->paDigests[i]; 3359 if ( kOCSumIsEqual(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv) 3360 && kOCSumHasEqualInChain(&pDigest->SumHead, &pEntry->New.SumHead)) 3361 { 3362 /* 3363 * Try open it. 3364 */ 3365 unsigned cLeft; 3366 PKOCENTRY pRetEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir)); 3367 kOCEntryRead(pRetEntry); 3368 if ( kOCEntryCheck(pRetEntry) 3369 && kOCDigestIsValid(pDigest, pRetEntry)) 3370 return pRetEntry; 3371 kOCEntryDestroy(pRetEntry); 3372 3373 /* bad entry, purge it. */ 3374 kOCDigestPurge(pDigest); 3375 3376 pCache->cDigests--; 3377 cLeft = pCache->cDigests - i; 3378 if (cLeft) 3379 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest)); 3380 3381 pCache->fDirty = 1; 3382 } 3383 } 3384 1477 3385 return NULL; 1478 3386 } … … 1480 3388 1481 3389 /** 1482 * Utility function that combines a filename and directory and passes it onto fopen. 1483 * 1484 * @returns fopen return value. 1485 * @param pszName The file name. 1486 * @param pszDir The directory path. 1487 * @param pszMode The fopen mode string. 1488 */ 1489 static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode) 1490 { 1491 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 1492 FILE *pFile = fopen(pszPath, pszMode); 1493 free(pszPath); 1494 return pFile; 1495 } 1496 1497 1498 /** 1499 * Deletes a file in a directory. 1500 * 1501 * @returns whatever unlink returns. 1502 * @param pszName The file name. 1503 * @param pszDir The directory path. 1504 */ 1505 static int UnlinkFileInDir(const char *pszName, const char *pszDir) 1506 { 1507 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 1508 int rc = unlink(pszPath); 1509 free(pszPath); 1510 return rc; 1511 } 1512 1513 1514 /** 1515 * Renames a file in a directory. 1516 * 1517 * @returns whatever unlink returns. 1518 * @param pszOldName The new file name. 1519 * @param pszNewName The old file name. 1520 * @param pszDir The directory path. 1521 */ 1522 static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir) 1523 { 1524 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir); 1525 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir); 1526 int rc = rename(pszOldPath, pszNewPath); 1527 free(pszOldPath); 1528 free(pszNewPath); 1529 return rc; 1530 } 1531 1532 1533 /** 1534 * Check if a (regular) file exists in a directory. 1535 * 1536 * @returns 1 if it exists and is a regular file, 0 if not. 1537 * @param pszName The file name. 1538 * @param pszDir The directory path. 1539 */ 1540 static int DoesFileInDirExist(const char *pszName, const char *pszDir) 1541 { 1542 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 1543 struct stat st; 1544 int rc = stat(pszPath, &st); 1545 free(pszPath); 1546 #ifdef S_ISREG 1547 return !rc && S_ISREG(st.st_mode); 1548 #elif defined(_MSC_VER) 1549 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG; 1550 #else 1551 #error "Port me" 1552 #endif 1553 } 1554 1555 1556 /** 1557 * Reads into memory an entire file. 1558 * 1559 * @returns Pointer to the heap allocation containing the file. 1560 * On failure NULL and errno is returned. 1561 * @param pszName The file. 1562 * @param pszDir The directory the file resides in. 1563 * @param pcbFile Where to store the file size. 1564 */ 1565 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile) 1566 { 1567 int SavedErrno; 1568 char *pszPath = MakePathFromDirAndFile(pszName, pszDir); 1569 int fd = open(pszPath, O_RDONLY | O_BINARY); 1570 if (fd >= 0) 1571 { 1572 off_t cbFile = lseek(fd, 0, SEEK_END); 1573 if ( cbFile >= 0 1574 && lseek(fd, 0, SEEK_SET) == 0) 1575 { 1576 char *pb = malloc(cbFile + 1); 1577 if (pb) 1578 { 1579 if (read(fd, pb, cbFile) == cbFile) 1580 { 1581 close(fd); 1582 pb[cbFile] = '\0'; 1583 *pcbFile = (size_t)cbFile; 1584 return pb; 1585 } 1586 SavedErrno = errno; 1587 free(pb); 1588 } 1589 else 1590 SavedErrno = ENOMEM; 1591 } 1592 else 1593 SavedErrno = errno; 1594 close(fd); 1595 } 1596 else 1597 SavedErrno = errno; 1598 free(pszPath); 1599 errno = SavedErrno; 1600 return NULL; 1601 } 1602 1603 1604 static void *xmalloc(size_t cb) 1605 { 1606 void *pv = malloc(cb); 1607 if (!pv) 1608 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb); 1609 return pv; 1610 } 1611 1612 1613 static void *xrealloc(void *pvOld, size_t cb) 1614 { 1615 void *pv = realloc(pvOld, cb); 1616 if (!pv) 1617 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb); 1618 return pv; 1619 } 1620 1621 1622 static char *xstrdup(const char *pszIn) 1623 { 1624 char *psz = strdup(pszIn); 1625 if (!psz) 1626 kObjCacheFatal(NULL, "out of memory (%d)\n", (int)strlen(pszIn)); 1627 return psz; 3390 * Is this a new cache? 3391 * 3392 * @returns 1 if new, 0 if not new. 3393 * @param pEntry The entry. 3394 */ 3395 static int kObjCacheIsNew(PKOBJCACHE pCache) 3396 { 3397 return pCache->fNewCache; 1628 3398 } 1629 3399 … … 1653 3423 static int usage(void) 1654 3424 { 1655 printf("syntax: kObjCache [-v|--verbose] [-f|--file] <cache-file> [-V|--version] [-r|--redir-stdout]\n" 1656 " --kObjCache-cpp <filename> <precompiler + args> \n" 1657 " --kObjCache-cc <object> <compiler + args>\n" 1658 " [--kObjCache-both [args]]\n" 1659 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n" 3425 printf("syntax: kObjCache [--kObjCache-options] [-v|--verbose]\n" 3426 " [-d|--cache-dir <cache-dir>] [-n|--name <name-in-cache>]\n" 3427 " [-f|--file <local-cache-file>] [-t|--target <target-name>\n" 3428 " [-r|--redir-stdout] [-p|--passthru]\n" 3429 " --kObjCache-cpp <filename> <precompiler + args>\n" 3430 " --kObjCache-cc <object> <compiler + args>\n" 3431 " [--kObjCache-both [args]]\n" 3432 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n" 3433 " kObjCache <-V|--version>\n" 3434 " kObjCache [-?|-h|--help]\n" 3435 "\n" 3436 "The env.var. KOBJCACHE_DIR sets the default cache diretory (-d).\n" 3437 "The env.var. KOBJCACHE_OPTS allow you to specifie additional options\n" 3438 "without having to mess with the makefiles. These are appended with " 3439 "a --kObjCache-options between them and the command args.\n" 1660 3440 "\n"); 1661 3441 return 0; … … 1665 3445 int main(int argc, char **argv) 1666 3446 { 1667 PKOBJCACHE pEntry; 1668 1669 const char *pszCacheFile; 3447 PKOBJCACHE pCache; 3448 PKOCENTRY pEntry; 3449 3450 const char *pszCacheDir = getenv("KOBJCACHE_DIR"); 3451 const char *pszCacheName = NULL; 3452 const char *pszCacheFile = NULL; 3453 const char *pszEntryFile = NULL; 1670 3454 1671 3455 const char **papszArgvPreComp = NULL; 1672 3456 unsigned cArgvPreComp = 0; 1673 3457 const char *pszPreCompName = NULL; 1674 int fRedir StdOut = 0;3458 int fRedirPreCompStdOut = 0; 1675 3459 1676 3460 const char **papszArgvCompile = NULL; 1677 3461 unsigned cArgvCompile = 0; 1678 3462 const char *pszObjName = NULL; 3463 int fRedirCompileStdIn = 0; 3464 3465 const char *pszTarget = NULL; 1679 3466 1680 3467 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options; 1681 3468 3469 char *psz; 1682 3470 int i; 3471 3472 SetErrorPrefix("kObjCache"); 3473 3474 /* 3475 * Arguments passed in the environmnet? 3476 */ 3477 psz = getenv("KOBJCACHE_OPTS"); 3478 if (psz) 3479 AppendArgs(&argc, &argv, psz, "--kObjCache-options"); 1683 3480 1684 3481 /* … … 1709 3506 else if (!strcmp(argv[i], "--kObjCache-both")) 1710 3507 enmMode = kOC_BothArgv; 3508 else if (!strcmp(argv[i], "--kObjCache-options")) 3509 enmMode = kOC_Options; 1711 3510 else if (!strcmp(argv[i], "--help")) 1712 3511 return usage(); … … 1728 3527 } 1729 3528 } 1730 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) 3529 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--entry-file")) 3530 { 3531 if (i + 1 >= argc) 3532 return SyntaxError("%s requires a cache entry filename!\n", argv[i]); 3533 pszEntryFile = argv[++i]; 3534 } 3535 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--cache-file")) 1731 3536 { 1732 3537 if (i + 1 >= argc) … … 1734 3539 pszCacheFile = argv[++i]; 1735 3540 } 3541 else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--name")) 3542 { 3543 if (i + 1 >= argc) 3544 return SyntaxError("%s requires a cache name!\n", argv[i]); 3545 pszCacheName = argv[++i]; 3546 } 3547 else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--cache-dir")) 3548 { 3549 if (i + 1 >= argc) 3550 return SyntaxError("%s requires a cache directory!\n", argv[i]); 3551 pszCacheDir = argv[++i]; 3552 } 3553 else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--target")) 3554 { 3555 if (i + 1 >= argc) 3556 return SyntaxError("%s requires a target platform/arch name!\n", argv[i]); 3557 pszTarget = argv[++i]; 3558 } 3559 else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru")) 3560 fRedirPreCompStdOut = fRedirCompileStdIn = 1; 1736 3561 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout")) 1737 fRedir StdOut = 1;3562 fRedirPreCompStdOut = 1; 1738 3563 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) 1739 g_ fVerbose = 1;3564 g_cVerbosityLevel++; 1740 3565 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet")) 1741 g_ fVerbose= 0;3566 g_cVerbosityLevel = 0; 1742 3567 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")) 1743 3568 return usage(); … … 1750 3575 return SyntaxError("Doesn't grok '%s'!\n", argv[i]); 1751 3576 } 3577 if (!pszEntryFile) 3578 return SyntaxError("No cache entry filename (-f)!\n"); 3579 if (!cArgvCompile) 3580 return SyntaxError("No compiler arguments (--kObjCache-cc)!\n"); 3581 if (!cArgvPreComp) 3582 return SyntaxError("No precompiler arguments (--kObjCache-cc)!\n"); 3583 3584 /* 3585 * Calc the cache file name. 3586 */ 1752 3587 if (!pszCacheFile) 1753 return SyntaxError("No cache file name (-f)\n"); 1754 if (!cArgvCompile) 1755 return SyntaxError("No compiler arguments (--kObjCache-cc)\n"); 1756 if (!cArgvPreComp) 1757 return SyntaxError("No precompiler arguments (--kObjCache-cc)\n"); 1758 1759 /* 1760 * Create a cache entry from the cache file (if found). 1761 */ 1762 pEntry = kObjCacheCreate(pszCacheFile); 1763 kObjCacheRead(pEntry); 1764 1765 /* 1766 * Do the compiling. 1767 */ 1768 kObjCachePreCompile(pEntry, papszArgvPreComp, cArgvPreComp, pszPreCompName, fRedirStdOut); 1769 kObjCacheCompileIfNeeded(pEntry, papszArgvCompile, cArgvCompile, pszObjName); 1770 1771 /* 1772 * Write the cache file. 1773 */ 1774 kObjCacheWrite(pEntry); 1775 /* kObjCacheDestroy(pEntry); - don't bother */ 3588 { 3589 if (!pszCacheDir) 3590 return SyntaxError("No cache dir (-d / KOBJCACHE_DIR) and no cache filename!\n"); 3591 if (!pszCacheName) 3592 { 3593 pszCacheName = FindFilenameInPath(pszCacheFile); 3594 if (!*pszCacheName) 3595 return SyntaxError("The cache file (-f) specifies a directory / nothing!\n"); 3596 pszCacheName = xstrdup(pszCacheName); 3597 psz = strrchr(pszCacheName, '.'); 3598 if (psz > pszCacheName) 3599 *psz = '\0'; 3600 } 3601 pszCacheFile = MakePathFromDirAndFile(pszCacheDir, pszCacheName); /* harmless leak. */ 3602 } 3603 3604 /* 3605 * Create and initialize the two bojects we'll be working on. 3606 * 3607 * We're supposed to be the only ones actually writing to the local file, 3608 * so it's perfectly fine to read it here before we lock it. This simplifies 3609 * the detection of object name and compiler argument changes. 3610 */ 3611 SetErrorPrefix("kObjCache - %s", FindFilenameInPath(pszCacheFile)); 3612 pCache = kObjCacheCreate(pszCacheFile); 3613 3614 pEntry = kOCEntryCreate(pszCacheFile); 3615 kOCEntryRead(pEntry); 3616 kOCEntrySetCompileArgv(pEntry, papszArgvCompile, cArgvCompile); 3617 kOCEntrySetCompileObjName(pEntry, pszObjName); 3618 kOCEntrySetTarget(pEntry, pszTarget); 3619 kOCEntrySetCppName(pEntry, pszPreCompName); 3620 kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn); 3621 3622 /* 3623 * Open (& lock) the two files and do validity checks and such. 3624 */ 3625 kObjCacheLock(pCache); 3626 if ( kObjCacheIsNew(pCache) 3627 && kOCEntryNeedsCompiling(pEntry)) 3628 { 3629 /* 3630 * Both files are missing/invalid. 3631 * Optimize this path as it is frequently used when making a clean build. 3632 */ 3633 kOCEntryPreCompileAndCompile(pEntry, papszArgvPreComp, cArgvPreComp); 3634 } 3635 else 3636 { 3637 /* 3638 * Do the precompile (don't need to lock the cache file for this). 3639 */ 3640 kObjCacheUnlock(pCache); 3641 kOCEntryPreCompile(pEntry, papszArgvPreComp, cArgvPreComp); 3642 3643 /* 3644 * Check if we need to recompile. If we do, try see if the is a cache entry first. 3645 */ 3646 kOCEntryCalcRecompile(pEntry); 3647 if (kOCEntryNeedsCompiling(pEntry)) 3648 { 3649 PKOCENTRY pUseEntry; 3650 kObjCacheLock(pCache); 3651 kObjCacheRemoveEntry(pCache, pEntry); 3652 pUseEntry = kObjCacheFindMatchingEntry(pCache, pEntry); 3653 if (pUseEntry) 3654 { 3655 kOCEntryCopy(pEntry, pUseEntry); 3656 kOCEntryDestroy(pUseEntry); 3657 } 3658 else 3659 { 3660 kObjCacheUnlock(pCache); 3661 kOCEntryCompileIt(pEntry); 3662 kObjCacheLock(pCache); 3663 } 3664 } 3665 } 3666 3667 /* 3668 * Update the cache files. 3669 */ 3670 kObjCacheInsertEntry(pCache, pEntry); 3671 kOCEntryWrite(pEntry); 3672 kObjCacheUnlock(pCache); 1776 3673 return 0; 1777 3674 }
Note:
See TracChangeset
for help on using the changeset viewer.