Changeset 2791 for trunk/src/kLibTweaker
- Timestamp:
- Sep 16, 2015, 12:57:44 AM (10 years ago)
- Location:
- trunk/src/kLibTweaker
- Files:
-
- 1 added
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kLibTweaker/Makefile.kmk
r2787 r2791 1 1 # $Id$ 2 2 ## @file 3 # Sub-makefile for k ObjCache.3 # Sub-makefile for kLibTweaker. 4 4 # 5 5 6 6 # 7 # Copyright (c) 2007-201 0knut st. osmundsen <bird-kBuild-spamx@anduin.net>7 # Copyright (c) 2007-2015 knut st. osmundsen <bird-kBuild-spamx@anduin.net> 8 8 # 9 9 # This file is part of kBuild. … … 27 27 include $(PATH_KBUILD)/subheader.kmk 28 28 29 PROGRAMS += kObjCache 30 kObjCache_TEMPLATE = BIN 31 kObjCache_DEFS.release = NASSERT 32 kObjCache_SOURCES = kObjCache.c 33 kObjCache_LIBS = \ 29 PROGRAMS += kLibTweaker 30 kLibTweaker_TEMPLATE = BIN 31 kLibTweaker_DEFS.release = NASSERT 32 kLibTweaker_SOURCES = kLibTweaker.c 33 kLibTweaker_INCS = ../lib 34 kLibTweaker_LIBS = \ 34 35 $(LIB_KDEP) \ 35 36 $(LIB_KUTIL) -
trunk/src/kLibTweaker/kLibTweaker.c
r2787 r2791 1 1 /* $Id$ */ 2 2 /** @file 3 * k ObjCache - Object Cache.3 * kLibTweaker - Import library tweaker for windows. 4 4 */ 5 5 6 6 /* 7 * Copyright (c) 2007-201 2knut st. osmundsen <bird-kBuild-spamx@anduin.net>7 * Copyright (c) 2007-2015 knut st. osmundsen <bird-kBuild-spamx@anduin.net> 8 8 * 9 9 * This file is part of kBuild. … … 24 24 */ 25 25 26 /******************************************************************************* 27 * Header Files *28 ******************************************************************************* /26 /********************************************************************************************************************************* 27 * Header Files * 28 *********************************************************************************************************************************/ 29 29 #if 0 30 30 # define ELECTRIC_HEAP … … 32 32 # include "../kmk/electric.c" 33 33 #endif 34 #include <ctype.h> 34 35 #include <string.h> 35 36 #include <stdlib.h> … … 38 39 #include <errno.h> 39 40 #include <assert.h> 40 #include <sys/stat.h> 41 #include <fcntl.h> 42 #include <limits.h> 43 #include <ctype.h> 44 #ifndef PATH_MAX 45 # define PATH_MAX _MAX_PATH /* windows */ 46 #endif 47 #if defined(__OS2__) || defined(__WIN__) 48 # include <process.h> 49 # include <io.h> 50 # ifdef __OS2__ 51 # include <unistd.h> 52 # include <sys/wait.h> 53 # include <sys/time.h> 54 # endif 55 # if defined(_MSC_VER) 56 # include <direct.h> 57 typedef intptr_t pid_t; 58 # endif 59 # ifndef _P_WAIT 60 # define _P_WAIT P_WAIT 61 # endif 62 # ifndef _P_NOWAIT 63 # define _P_NOWAIT P_NOWAIT 64 # endif 65 #else 66 # include <unistd.h> 67 # include <sys/wait.h> 68 # include <sys/time.h> 69 # ifndef O_BINARY 70 # define O_BINARY 0 71 # endif 72 #endif 73 #if defined(__WIN__) 74 # include <Windows.h> 75 # include "quoted_spawn.h" 76 #endif 77 #if defined(__HAIKU__) 78 # include <posix/sys/file.h> 79 #endif 80 81 #include "crc32.h" 82 #include "md5.h" 83 #include "kDep.h" 84 85 86 /******************************************************************************* 87 * Defined Constants And Macros * 88 *******************************************************************************/ 89 /** The max line length in a cache file. */ 90 #define KOBJCACHE_MAX_LINE_LEN 16384 91 #if defined(__WIN__) 92 # define PATH_SLASH '\\' 93 #else 94 # define PATH_SLASH '/' 95 #endif 96 #if defined(__OS2__) || defined(__WIN__) 97 # define IS_SLASH(ch) ((ch) == '/' || (ch) == '\\') 98 # define IS_SLASH_DRV(ch) ((ch) == '/' || (ch) == '\\' || (ch) == ':') 99 #else 100 # define IS_SLASH(ch) ((ch) == '/') 101 # define IS_SLASH_DRV(ch) ((ch) == '/') 102 #endif 103 104 #ifndef STDIN_FILENO 105 # define STDIN_FILENO 0 106 #endif 107 #ifndef STDOUT_FILENO 108 # define STDOUT_FILENO 1 109 #endif 110 #ifndef STDERR_FILENO 111 # define STDERR_FILENO 2 112 #endif 113 114 #define MY_IS_BLANK(a_ch) ((a_ch) == ' ' || (a_ch) == '\t') 115 116 #define KOC_BUF_MIN KOC_BUF_ALIGNMENT 117 #define KOC_BUF_INCR KOC_BUF_ALIGNMENT 118 #define KOC_BUF_ALIGNMENT (4U*1024U*1024U) 119 120 121 /******************************************************************************* 122 * Global Variables * 123 *******************************************************************************/ 41 42 #include "k/kLdrFmts/pe.h" 43 44 45 /********************************************************************************************************************************* 46 * Structures and Typedefs * 47 *********************************************************************************************************************************/ 48 /** 49 * Microsoft import library archive header. 50 * 51 * This has the same size as a COFF header, which is probably not a coincidence. 52 */ 53 typedef struct COFFIMPLIBHDR 54 { 55 KU16 uSig1; /* 0 */ 56 KU16 uSig2; /* 0xffff */ 57 KU16 uVersion; /* 0 */ 58 KU16 uMachine; /* IMAGE_FILE_MACHINE_I386, ... */ 59 KU32 uTimeDateStamp; 60 KU32 cbData; 61 KU16 uOrdinalOrHint; 62 KU16 uFlags; 63 } COFFIMPLIBHDR; 64 65 /** 66 * COFF symbol. 67 * 68 * This one has an odd size and will cause misaligned accesses on platforms 69 * which cares about such. 70 */ 71 #pragma pack(1) 72 typedef struct COFFSYMBOL 73 { 74 union 75 { 76 char e_name[8]; 77 struct 78 { 79 KU32 e_zeros; /**< Zero to distinguish it from ascii name. */ 80 KU32 e_offset; /**< String table offset. */ 81 } e; 82 } e; 83 KU32 e_value; 84 KI16 e_scnum; 85 KU16 e_type; 86 KU8 e_sclass; 87 KU8 e_numaux; 88 } COFFSYMBOL; 89 #pragma pack() 90 91 /** 92 * Archive file header. 93 */ 94 typedef struct ARCHFILEHDR 95 { 96 char achName[16]; 97 char achModtime[12]; 98 char achOwnerId[6]; 99 char achGroupId[6]; 100 char achMode[8]; 101 char achSize[10]; 102 char achMagic[2]; 103 } ARCHFILEHDR; 104 105 106 /********************************************************************************************************************************* 107 * Global Variables * 108 *********************************************************************************************************************************/ 124 109 /** Whether verbose output is enabled. */ 125 110 static unsigned g_cVerbosityLevel = 0; … … 127 112 static char g_szErrorPrefix[128]; 128 113 129 /** Read buffer shared by the cache components. */130 static char g_szLine[KOBJCACHE_MAX_LINE_LEN + 16];131 132 /** How many times we've moved memory around. */133 static size_t g_cMemMoves = 0;134 /** How much memory we've moved. */135 static size_t g_cbMemMoved = 0;136 137 138 /*******************************************************************************139 * Internal Functions *140 *******************************************************************************/141 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);142 static char *CalcRelativeName(const char *pszPath, const char *pszDir);143 static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode);144 static int UnlinkFileInDir(const char *pszName, const char *pszDir);145 static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir);146 static int DoesFileInDirExist(const char *pszName, const char *pszDir);147 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);148 149 114 150 115 void FatalMsg(const char *pszFormat, ...) … … 180 145 181 146 182 #if 0 /* unused */ 183 static void ErrorMsg(const char *pszFormat, ...) 147 static int ErrorMsg(const char *pszFormat, ...) 184 148 { 185 149 va_list va; … … 193 157 vfprintf(stderr, pszFormat, va); 194 158 va_end(va); 195 } 196 #endif /* unused */ 159 160 return 1; 161 } 197 162 198 163 … … 275 240 276 241 277 278 279 280 /**281 * Returns a millisecond timestamp.282 *283 * @returns Millisecond timestamp.284 */285 static uint32_t NowMs(void)286 {287 #if defined(__WIN__)288 return GetTickCount();289 #else290 int iSavedErrno = errno;291 struct timeval tv = {0, 0};292 293 gettimeofday(&tv, NULL);294 errno = iSavedErrno;295 296 return tv.tv_sec * 1000 + tv.tv_usec / 1000;297 #endif298 }299 300 301 /**302 * Gets the absolute path303 *304 * @returns A new heap buffer containing the absolute path.305 * @param pszPath The path to make absolute. (Readonly)306 */307 static char *AbsPath(const char *pszPath)308 {309 /** @todo this isn't really working as it should... */310 char szTmp[PATH_MAX];311 #if defined(__OS2__)312 if ( _fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))313 && !realpath(pszPath, szTmp))314 return xstrdup(pszPath);315 #elif defined(__WIN__)316 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))317 return xstrdup(pszPath);318 #else319 if (!realpath(pszPath, szTmp))320 return xstrdup(pszPath);321 #endif322 return xstrdup(szTmp);323 }324 325 326 /**327 * Utility function that finds the filename part in a path.328 *329 * @returns Pointer to the file name part (this may be "").330 * @param pszPath The path to parse.331 */332 static const char *FindFilenameInPath(const char *pszPath)333 {334 const char *pszFilename = strchr(pszPath, '\0') - 1;335 if (pszFilename < pszPath)336 return pszPath;337 while ( pszFilename > pszPath338 && !IS_SLASH_DRV(pszFilename[-1]))339 pszFilename--;340 return pszFilename;341 }342 343 344 /**345 * Utility function that combines a filename and a directory into a path.346 *347 * @returns malloced buffer containing the result.348 * @param pszName The file name.349 * @param pszDir The directory path.350 */351 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)352 {353 size_t cchName = strlen(pszName);354 size_t cchDir = strlen(pszDir);355 char *pszBuf = xmalloc(cchName + cchDir + 2);356 memcpy(pszBuf, pszDir, cchDir);357 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1]))358 pszBuf[cchDir++] = PATH_SLASH;359 memcpy(pszBuf + cchDir, pszName, cchName + 1);360 return pszBuf;361 }362 363 364 /**365 * Compares two path strings to see if they are identical.366 *367 * This doesn't do anything fancy, just the case ignoring and368 * slash unification.369 *370 * @returns 1 if equal, 0 otherwise.371 * @param pszPath1 The first path.372 * @param pszPath2 The second path.373 */374 static int ArePathsIdentical(const char *pszPath1, const char *pszPath2)375 {376 #if defined(__OS2__) || defined(__WIN__)377 if (stricmp(pszPath1, pszPath2))378 {379 /* Slashes may differ, compare char by char. */380 const char *psz1 = pszPath1;381 const char *psz2 = pszPath2;382 for (;;)383 {384 if (*psz1 != *psz2)385 {386 if ( tolower(*psz1) != tolower(*psz2)387 && toupper(*psz1) != toupper(*psz2)388 && *psz1 != '/'389 && *psz1 != '\\'390 && *psz2 != '/'391 && *psz2 != '\\')392 return 0;393 }394 if (!*psz1)395 break;396 psz1++;397 psz2++;398 }399 }400 return 1;401 #else402 return !strcmp(pszPath1, pszPath2);403 #endif404 }405 406 /**407 * Compares two path strings to see if they are identical.408 *409 * This doesn't do anything fancy, just the case ignoring and410 * slash unification.411 *412 * @returns 1 if equal, 0 otherwise.413 * @param pszPath1 The first path.414 * @param pszPath2 The second path.415 * @param cch The number of characters to compare.416 */417 static int ArePathsIdenticalN(const char *pszPath1, const char *pszPath2, size_t cch)418 {419 #if defined(__OS2__) || defined(__WIN__)420 if (strnicmp(pszPath1, pszPath2, cch))421 {422 /* Slashes may differ, compare char by char. */423 const char *psz1 = pszPath1;424 const char *psz2 = pszPath2;425 for ( ; cch; psz1++, psz2++, cch--)426 {427 if (*psz1 != *psz2)428 {429 if ( tolower(*psz1) != tolower(*psz2)430 && toupper(*psz1) != toupper(*psz2)431 && *psz1 != '/'432 && *psz1 != '\\'433 && *psz2 != '/'434 && *psz2 != '\\')435 return 0;436 }437 }438 }439 return 1;440 #else441 return !strncmp(pszPath1, pszPath2, cch);442 #endif443 }444 445 446 /**447 * Calculate how to get to pszPath from pszDir.448 *449 * @returns The relative path from pszDir to path pszPath.450 * @param pszPath The path to the object.451 * @param pszDir The directory it shall be relative to.452 */453 static char *CalcRelativeName(const char *pszPath, const char *pszDir)454 {455 char *pszRet = NULL;456 char *pszAbsPath = NULL;457 size_t cchDir = strlen(pszDir);458 459 /*460 * This is indeed a bit tricky, so we'll try the easy way first...461 */462 if (ArePathsIdenticalN(pszPath, pszDir, cchDir))463 {464 if (pszPath[cchDir])465 pszRet = (char *)pszPath + cchDir;466 else467 pszRet = "./";468 }469 else470 {471 pszAbsPath = AbsPath(pszPath);472 if (ArePathsIdenticalN(pszAbsPath, pszDir, cchDir))473 {474 if (pszPath[cchDir])475 pszRet = pszAbsPath + cchDir;476 else477 pszRet = "./";478 }479 }480 if (pszRet)481 {482 while (IS_SLASH_DRV(*pszRet))483 pszRet++;484 pszRet = xstrdup(pszRet);485 free(pszAbsPath);486 return pszRet;487 }488 489 /*490 * Damn, it's gonna be complicated. Deal with that later.491 */492 FatalDie("complicated relative path stuff isn't implemented yet. sorry.\n");493 return NULL;494 }495 496 497 /**498 * Utility function that combines a filename and directory and passes it onto fopen.499 *500 * @returns fopen return value.501 * @param pszName The file name.502 * @param pszDir The directory path.503 * @param pszMode The fopen mode string.504 */505 static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)506 {507 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);508 FILE *pFile = fopen(pszPath, pszMode);509 free(pszPath);510 return pFile;511 }512 513 514 /**515 * Utility function that combines a filename and directory and passes it onto open.516 *517 * @returns open return value.518 * @param pszName The file name.519 * @param pszDir The directory path.520 * @param fFlags The open flags.521 * @param fCreateMode The file creation mode.522 */523 static int OpenFileInDir(const char *pszName, const char *pszDir, int fFlags, int fCreateMode)524 {525 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);526 int fd = open(pszPath, fFlags, fCreateMode);527 free(pszPath);528 return fd;529 }530 531 532 533 /**534 * Deletes a file in a directory.535 *536 * @returns whatever unlink returns.537 * @param pszName The file name.538 * @param pszDir The directory path.539 */540 static int UnlinkFileInDir(const char *pszName, const char *pszDir)541 {542 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);543 int rc = unlink(pszPath);544 free(pszPath);545 return rc;546 }547 548 549 /**550 * Renames a file in a directory.551 *552 * @returns whatever rename returns.553 * @param pszOldName The new file name.554 * @param pszNewName The old file name.555 * @param pszDir The directory path.556 */557 static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)558 {559 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);560 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);561 int rc = rename(pszOldPath, pszNewPath);562 free(pszOldPath);563 free(pszNewPath);564 return rc;565 }566 567 568 /**569 * Check if a (regular) file exists in a directory.570 *571 * @returns 1 if it exists and is a regular file, 0 if not.572 * @param pszName The file name.573 * @param pszDir The directory path.574 */575 static int DoesFileInDirExist(const char *pszName, const char *pszDir)576 {577 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);578 struct stat st;579 int rc = stat(pszPath, &st);580 free(pszPath);581 #ifdef S_ISREG582 return !rc && S_ISREG(st.st_mode);583 #elif defined(_MSC_VER)584 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;585 #else586 #error "Port me"587 #endif588 }589 590 591 /**592 * Reads into memory an entire file.593 *594 * @returns Pointer to the heap allocation containing the file.595 * On failure NULL and errno is returned.596 * @param pszName The file.597 * @param pszDir The directory the file resides in.598 * @param pcbFile Where to store the file size.599 */600 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)601 {602 int SavedErrno;603 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);604 int fd = open(pszPath, O_RDONLY | O_BINARY);605 if (fd >= 0)606 {607 off_t cbFile = lseek(fd, 0, SEEK_END);608 if ( cbFile >= 0609 && lseek(fd, 0, SEEK_SET) == 0)610 {611 char *pb = malloc(cbFile + 1);612 if (pb)613 {614 if (read(fd, pb, cbFile) == cbFile)615 {616 close(fd);617 pb[cbFile] = '\0';618 *pcbFile = (size_t)cbFile;619 return pb;620 }621 SavedErrno = errno;622 free(pb);623 }624 else625 SavedErrno = ENOMEM;626 }627 else628 SavedErrno = errno;629 close(fd);630 }631 else632 SavedErrno = errno;633 free(pszPath);634 errno = SavedErrno;635 return NULL;636 }637 638 639 /**640 * Creates a directory including all necessary parent directories.641 *642 * @returns 0 on success, -1 + errno on failure.643 * @param pszDir The directory.644 */645 static int MakePath(const char *pszPath)646 {647 int iErr = 0;648 char *pszAbsPath = AbsPath(pszPath);649 char *psz = pszAbsPath;650 651 /* Skip to the root slash (PC). */652 while (!IS_SLASH(*psz) && *psz)653 psz++;654 /** @todo UNC */655 for (;;)656 {657 char chSaved;658 659 /* skip slashes */660 while (IS_SLASH(*psz))661 psz++;662 if (!*psz)663 break;664 665 /* find the next slash or end and terminate the string. */666 while (!IS_SLASH(*psz) && *psz)667 psz++;668 chSaved = *psz;669 *psz = '\0';670 671 /* try create the directory, ignore failure because the directory already exists. */672 errno = 0;673 #ifdef _MSC_VER674 if ( _mkdir(pszAbsPath)675 && errno != EEXIST)676 #else677 if ( mkdir(pszAbsPath, 0777)678 && errno != EEXIST679 && errno != ENOSYS /* Solaris nonsensical mkdir crap. */680 && errno != EACCES /* Solaris nonsensical mkdir crap. */681 )682 #endif683 {684 iErr = errno;685 break;686 }687 688 /* restore the slash/terminator */689 *psz = chSaved;690 }691 692 free(pszAbsPath);693 return iErr ? -1 : 0;694 }695 696 697 242 /** 698 243 * Adds the arguments found in the pszCmdLine string to argument vector. … … 770 315 771 316 772 /** 773 * Dependency collector state. 774 */ 775 typedef struct KOCDEP 776 { 777 /** The statemachine for processing the preprocessed code stream. */ 778 enum KOCDEPSTATE 779 { 780 kOCDepState_Invalid = 0, 781 kOCDepState_NeedNewLine, 782 kOCDepState_NeedHash, 783 kOCDepState_NeedLine_l, 784 kOCDepState_NeedLine_l_HaveSpace, 785 kOCDepState_NeedLine_i, 786 kOCDepState_NeedLine_n, 787 kOCDepState_NeedLine_e, 788 kOCDepState_NeedSpaceBeforeDigit, 789 kOCDepState_NeedFirstDigit, 790 kOCDepState_NeedMoreDigits, 791 kOCDepState_NeedQuote, 792 kOCDepState_NeedEndQuote 793 } enmState; 794 /** Current offset into the filename buffer. */ 795 uint32_t offFilename; 796 /** The amount of space currently allocated for the filename buffer. */ 797 uint32_t cbFilenameAlloced; 798 /** Pointer to the filename buffer. */ 799 char *pszFilename; 800 /** The current dependency file. */ 801 PDEP pCurDep; 802 } KOCDEP; 803 /** Pointer to a KOCDEP. */ 804 typedef KOCDEP *PKOCDEP; 805 806 807 /** 808 * Initializes the dependency collector state. 809 * 810 * @param pDepState The dependency collector state. 811 */ 812 static void kOCDepInit(PKOCDEP pDepState) 813 { 814 pDepState->enmState = kOCDepState_NeedHash; 815 pDepState->offFilename = 0; 816 pDepState->cbFilenameAlloced = 0; 817 pDepState->pszFilename = NULL; 818 pDepState->pCurDep = NULL; 819 } 820 821 822 /** 823 * Deletes the dependency collector state, releasing all resources. 824 * 825 * @param pDepState The dependency collector state. 826 */ 827 static void kOCDepDelete(PKOCDEP pDepState) 828 { 829 pDepState->enmState = kOCDepState_Invalid; 830 free(pDepState->pszFilename); 831 pDepState->pszFilename = NULL; 832 depCleanup(); 833 } 834 835 836 /** 837 * Unescapes a string in place. 838 * 839 * @returns The new string length. 840 * @param psz The string to unescape (input and output). 841 */ 842 static size_t kOCDepUnescape(char *psz) 843 { 844 char *pszSrc = psz; 845 char *pszDst = psz; 846 char ch; 847 848 while ((ch = *pszSrc++) != '\0') 849 { 850 if (ch == '\\') 851 { 852 char ch2 = *pszSrc; 853 if (ch2) 317 318 319 static fpos_t kLibTweakerAsciiToSize(const char *pch, size_t cch) 320 { 321 fpos_t cb = 0; 322 323 /* strip leading spaces. */ 324 while (cch > 0 && (*pch == ' ' || *pch == '\t')) 325 cch--, pch++; 326 327 /* Convert decimal to binary. */ 328 while (cch-- > 0) 329 { 330 char ch = *pch++; 331 if (ch >= '0' && ch <= '9') 332 { 333 cb *= 10; 334 cb += ch - '0'; 335 } 336 else 337 break; 338 } 339 340 return cb; 341 } 342 343 344 static int kLibMyReadAt(FILE *pFile, void *pv, size_t cb, fpos_t off, int fEofOk) 345 { 346 if (fsetpos(pFile, &off) == 0) 347 { 348 size_t cbActual = fread(pv, 1, cb, pFile); 349 if (cbActual == cb) 350 return 0; 351 if (!fEofOk || !feof(pFile)) 352 ErrorMsg("fread returned %#lx, expected %#lx!\n", (unsigned long)cbActual, (unsigned long)cb); 353 } 354 else 355 ErrorMsg("seek error!\n"); 356 return 1; 357 } 358 359 360 static int kLibMyWriteAt(FILE *pFile, const void *pv, size_t cb, fpos_t off) 361 { 362 if (fsetpos(pFile, &off) == 0) 363 { 364 size_t cbActual = fwrite(pv, 1, cb, pFile); 365 if (cbActual == cb) 366 return 0; 367 ErrorMsg("fwrite returned %#lx, expected %#lx!\n", (unsigned long)cbActual, (unsigned long)cb); 368 } 369 else 370 ErrorMsg("seek error!\n"); 371 return 1; 372 } 373 374 375 static int kLibFillNullThunkData(FILE *pFile, fpos_t cbFile, fpos_t offFileBytes) 376 { 377 size_t off; 378 IMAGE_FILE_HEADER CoffHdr; 379 IMAGE_SECTION_HEADER SecHdr; 380 unsigned iSecHdr; 381 fpos_t offCur; 382 unsigned cbMachineWord; 383 KU32 cbStrTab; 384 COFFSYMBOL *paSymbols; 385 int rcRet = 0; 386 387 /* 388 * Read the COFF file header and filter out unlikly files based on 389 * section and symbol counts. 390 */ 391 if (cbFile <= sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) * 2 + 4) 392 return 0; 393 if (kLibMyReadAt(pFile, &CoffHdr, sizeof(CoffHdr), offFileBytes, 0) != 0) 394 return 1; 395 if ( CoffHdr.Machine != IMAGE_FILE_MACHINE_I386 396 && CoffHdr.Machine != IMAGE_FILE_MACHINE_AMD64) 397 return 0; 398 cbMachineWord = CoffHdr.Machine == IMAGE_FILE_MACHINE_I386 ? 4 : 8; 399 if ( CoffHdr.NumberOfSections == 0 400 || CoffHdr.NumberOfSymbols == 0) 401 return 0; 402 off = sizeof(IMAGE_FILE_HEADER) + CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); 403 if ((fpos_t)off >= cbFile) 404 return 0; 405 if ( CoffHdr.PointerToSymbolTable >= (KU64)cbFile 406 || CoffHdr.PointerToSymbolTable < off) 407 return 0; 408 409 /* 410 * Search for the .idata$5 section which the thunk data is usually found in. 411 */ 412 offCur = offFileBytes + sizeof(CoffHdr); 413 for (iSecHdr = 0; iSecHdr < CoffHdr.NumberOfSections; iSecHdr++) 414 { 415 if (kLibMyReadAt(pFile, &SecHdr, sizeof(SecHdr), offCur, 0) != 0) 416 return 1; 417 InfoMsg(2, "#2: %.8s VirtualSize=%#lx\n", SecHdr.Name, SecHdr.SizeOfRawData); 418 if ( !memcmp(SecHdr.Name, ".idata$5", 8) 419 && SecHdr.SizeOfRawData == cbMachineWord) 420 break; 421 offCur += sizeof(SecHdr); 422 } 423 if (iSecHdr == CoffHdr.NumberOfSections) 424 return 0; 425 426 /* 427 * Read in the symbo and string tables. 428 */ 429 off = CoffHdr.PointerToSymbolTable + CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL); 430 if (kLibMyReadAt(pFile, &cbStrTab, sizeof(cbStrTab), offFileBytes + off, 0) != 0) 431 return 1; 432 InfoMsg(2, "#2: Found COFF file header, cbStrTab=%#x; off=%#lx NumberOfSymbols=%#x PointerToSymbolTable=%#x\n", 433 cbStrTab, (long)off, CoffHdr.NumberOfSymbols, CoffHdr.PointerToSymbolTable); 434 if ( cbStrTab <= 4U 435 || cbStrTab >= 16*1024*1024U /* 16MB */ 436 || (fpos_t)off + cbStrTab > cbFile) 437 return 0; 438 paSymbols = xmalloc(CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL) + cbStrTab + 1); 439 if (kLibMyReadAt(pFile, paSymbols, CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL) + cbStrTab, 440 offFileBytes + CoffHdr.PointerToSymbolTable, 0) == 0) 441 { 442 char *pchStrTab = (char *)&paSymbols[CoffHdr.NumberOfSymbols]; 443 unsigned iSym; 444 445 pchStrTab[cbStrTab] = '\0'; 446 pchStrTab[0] = '\0'; 447 pchStrTab[1] = '\0'; 448 pchStrTab[2] = '\0'; 449 pchStrTab[3] = '\0'; 450 451 for (iSym = 0; iSym < CoffHdr.NumberOfSymbols; iSym++) 452 { 453 static char const s_szSuffix[] = "NULL_THUNK_DATA"; 454 const char *pchName; 455 size_t cchName; 456 if (paSymbols[iSym].e.e.e_zeros != 0) 854 457 { 855 pszSrc++; 856 ch = ch2; 458 pchName = &paSymbols[iSym].e.e_name[0]; 459 cchName = (char *)memchr(pchName, '\0', sizeof(paSymbols[iSym].e.e_name)) - pchName; 460 if (cchName > sizeof(paSymbols[iSym].e.e_name)) 461 cchName = sizeof(paSymbols[iSym].e.e_name); 857 462 } 858 /* else: cannot happen / just ignore */ 859 } 860 *pszDst++ = ch; 861 } 862 863 *pszDst = '\0'; 864 return pszDst - psz; 865 } 866 867 868 /** 869 * Checks if the character at @a offChar is escaped or not. 870 * 871 * @returns 1 if escaped, 0 if not. 872 * @param pach The string (not terminated). 873 * @param offChar The offset of the character in question. 874 */ 875 static int kOCDepIsEscaped(char *pach, size_t offChar) 876 { 877 while (offChar > 0 && pach[offChar - 1] == '\\') 878 { 879 if ( offChar == 1 880 || pach[offChar - 2] != '\\') 881 return 1; 882 offChar -= 2; 883 } 884 return 0; 885 } 886 887 888 static void kOCDepEnter(PKOCDEP pDepState, const char *pszUnescFilename, size_t cchFilename) 889 { 890 if (cchFilename + 1 >= pDepState->cbFilenameAlloced) 891 { 892 pDepState->cbFilenameAlloced = (cchFilename + 1 + 15) & ~15; 893 pDepState->pszFilename = (char *)xrealloc(pDepState->pszFilename, pDepState->cbFilenameAlloced); 894 } 895 896 memcpy(pDepState->pszFilename, pszUnescFilename, cchFilename); 897 pDepState->pszFilename[cchFilename] = '\0'; 898 cchFilename = kOCDepUnescape(pDepState->pszFilename); 899 900 if ( !pDepState->pCurDep 901 || cchFilename != pDepState->pCurDep->cchFilename 902 || strcmp(pDepState->pszFilename, pDepState->pCurDep->szFilename)) 903 pDepState->pCurDep = depAdd(pDepState->pszFilename, cchFilename); 904 } 905 906 907 /** 908 * This consumes the preprocessor output and generate dependencies from it. 909 * 910 * The trick is to look at the line directives and which files get listed there. 911 * 912 * @returns The new state. This is a convenience for saving code space and it 913 * isn't really meant to be of any use to the caller. 914 * @param pDepState The dependency collector state. 915 * @param pszInput The input. 916 * @param cchInput The input length. 917 */ 918 static enum KOCDEPSTATE 919 kOCDepConsumer(PKOCDEP pDepState, const char *pszInput, size_t cchInput) 920 { 921 enum KOCDEPSTATE enmState = pDepState->enmState; 922 const char *psz; 923 924 while (cchInput > 0) 925 { 926 switch (enmState) 927 { 928 case kOCDepState_NeedNewLine: 929 psz = (const char *)memchr(pszInput, '\n', cchInput); 930 if (!psz) 931 return enmState; 932 psz++; 933 cchInput -= psz - pszInput; 934 pszInput = psz; 935 936 case kOCDepState_NeedHash: 937 while (cchInput > 0 && MY_IS_BLANK(*pszInput)) 938 cchInput--, pszInput++; 939 if (!cchInput) 940 return pDepState->enmState = kOCDepState_NeedHash; 941 942 if (*pszInput != '#') 943 break; 944 pszInput++; 945 cchInput--; 946 enmState = kOCDepState_NeedLine_l; 947 948 case kOCDepState_NeedLine_l: 949 case kOCDepState_NeedLine_l_HaveSpace: 950 while (cchInput > 0 && MY_IS_BLANK(*pszInput)) 463 else if ( paSymbols[iSym].e.e.e_offset == 0 464 || paSymbols[iSym].e.e.e_offset >= cbStrTab) 465 continue; 466 else 467 { 468 pchName = &pchStrTab[paSymbols[iSym].e.e.e_offset]; 469 cchName = strlen(pchName); 470 } 471 472 if ( *pchName == 0x7f 473 && cchName >= sizeof(s_szSuffix) 474 && memcmp(&pchName[cchName - sizeof(s_szSuffix) + 1], s_szSuffix, sizeof(s_szSuffix) - 1) == 0) 475 { 476 if (pchName[cchName] == '\0') 477 InfoMsg(1, "#2: Found '%s': value=%#lx\n", pchName, paSymbols[iSym].e_value); 478 else 479 InfoMsg(1, "#2: Found '%.8s': value=%#lx\n", pchName, paSymbols[iSym].e_value); 480 if ( paSymbols[iSym].e_scnum > 0 481 && paSymbols[iSym].e_scnum <= CoffHdr.NumberOfSections) 951 482 { 952 enmState = kOCDepState_NeedLine_l_HaveSpace; 953 cchInput--, pszInput++; 954 } 955 if (!cchInput) 956 return pDepState->enmState = enmState; 957 958 if (*pszInput != 'l') 959 { 960 /* # <digit> "<file>" */ 961 if (enmState != kOCDepState_NeedLine_l_HaveSpace || !isdigit(*pszInput)) 962 break; 963 pszInput++; 964 cchInput--; 965 enmState = kOCDepState_NeedMoreDigits; 966 continue; 967 } 968 pszInput++; 969 if (!--cchInput) 970 return pDepState->enmState = kOCDepState_NeedLine_i; 971 972 case kOCDepState_NeedLine_i: 973 if (*pszInput != 'i') 974 break; 975 pszInput++; 976 if (!--cchInput) 977 return pDepState->enmState = kOCDepState_NeedLine_n; 978 979 case kOCDepState_NeedLine_n: 980 if (*pszInput != 'n') 981 break; 982 pszInput++; 983 if (!--cchInput) 984 return pDepState->enmState = kOCDepState_NeedLine_e; 985 986 case kOCDepState_NeedLine_e: 987 if (*pszInput != 'e') 988 break; 989 pszInput++; 990 if (!--cchInput) 991 return pDepState->enmState = kOCDepState_NeedSpaceBeforeDigit; 992 993 case kOCDepState_NeedSpaceBeforeDigit: 994 if (!MY_IS_BLANK(*pszInput)) 995 break; 996 pszInput++; 997 cchInput--; 998 999 case kOCDepState_NeedFirstDigit: 1000 while (cchInput > 0 && MY_IS_BLANK(*pszInput)) 1001 cchInput--, pszInput++; 1002 if (!cchInput) 1003 return pDepState->enmState = kOCDepState_NeedFirstDigit; 1004 1005 if (!isdigit(*pszInput)) 1006 break; 1007 pszInput++; 1008 cchInput--; 1009 1010 case kOCDepState_NeedMoreDigits: 1011 while (cchInput > 0 && isdigit(*pszInput)) 1012 cchInput--, pszInput++; 1013 if (!cchInput) 1014 return pDepState->enmState = kOCDepState_NeedMoreDigits; 1015 1016 case kOCDepState_NeedQuote: 1017 while (cchInput > 0 && MY_IS_BLANK(*pszInput)) 1018 cchInput--, pszInput++; 1019 if (!cchInput) 1020 return pDepState->enmState = kOCDepState_NeedQuote; 1021 1022 if (*pszInput != '"') 1023 break; 1024 pszInput++; 1025 cchInput--; 1026 1027 case kOCDepState_NeedEndQuote: 1028 { 1029 uint32_t off = pDepState->offFilename; 1030 for (;;) 1031 { 1032 char ch; 1033 1034 if (!cchInput) 483 if (paSymbols[iSym].e_scnum != iSecHdr + 1) 484 InfoMsg(0, "#2: '%s' in section %u, expected %u\n", pchName, paSymbols[iSym].e_scnum, iSecHdr); 485 else if (paSymbols[iSym].e_value != 0) 486 InfoMsg(0, "#2: '%s' in value %#xu, expected 0x0\n", pchName, paSymbols[iSym].e_value); 487 else if ( SecHdr.PointerToRawData < sizeof(CoffHdr) + CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) 488 || (fpos_t)SecHdr.PointerToRawData + cbMachineWord > cbFile) 489 InfoMsg(0, "#2: Unexpected PointerToRawData value: %#x\n", SecHdr.PointerToRawData); 490 else 1035 491 { 1036 pDepState->offFilename = off; 1037 return pDepState->enmState = kOCDepState_NeedEndQuote; 1038 } 1039 1040 if (off + 1 >= pDepState->cbFilenameAlloced) 1041 { 1042 if (!pDepState->cbFilenameAlloced) 1043 pDepState->cbFilenameAlloced = 32; 1044 else 1045 pDepState->cbFilenameAlloced *= 2; 1046 pDepState->pszFilename = (char *)xrealloc(pDepState->pszFilename, pDepState->cbFilenameAlloced); 1047 } 1048 pDepState->pszFilename[off] = ch = *pszInput++; 1049 cchInput--; 1050 1051 if ( ch == '"' 1052 && ( off == 0 1053 || pDepState->pszFilename[off - 1] != '\\' 1054 || !kOCDepIsEscaped(pDepState->pszFilename, off))) 1055 { 1056 /* Done, unescape and add the file. */ 1057 size_t cchFilename; 1058 1059 pDepState->pszFilename[off] = '\0'; 1060 cchFilename = kOCDepUnescape(pDepState->pszFilename); 1061 1062 if ( !pDepState->pCurDep 1063 || cchFilename != pDepState->pCurDep->cchFilename 1064 || strcmp(pDepState->pszFilename, pDepState->pCurDep->szFilename)) 1065 pDepState->pCurDep = depAdd(pDepState->pszFilename, cchFilename); 1066 pDepState->offFilename = 0; 492 union 493 { 494 KU8 ab[8]; 495 KU32 u32; 496 KU64 u64; 497 } uBuf; 498 uBuf.u64 = 0; 499 off = offFileBytes + SecHdr.PointerToRawData; 500 if (kLibMyReadAt(pFile, &uBuf, cbMachineWord, off, 0) == 0) 501 { 502 static const KU8 s_abGarbage[8] = { 0xaa, 0x99, 0x88, 0xbb, 0xbb, 0xaa, 0x88, 0x99 }; 503 static const KU8 s_abZero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 504 if (memcmp(&uBuf, s_abZero,cbMachineWord) == 0) 505 { 506 rcRet = kLibMyWriteAt(pFile, s_abGarbage, cbMachineWord, off); 507 if (!rcRet) 508 InfoMsg(1, "#2: Updated '%s'\n", pchName); 509 } 510 else if (memcmp(&uBuf, s_abGarbage, cbMachineWord) == 0) 511 { 512 InfoMsg(1, "#2: Already modified '%s'\n", pchName); 513 rcRet = 0; 514 } 515 else 516 rcRet = ErrorMsg(0, "#2: Unexpected '%s' data: %02x %02x %02x %02x %02x %02x %02x %02x\n", 517 pchName, 518 uBuf.ab[0], uBuf.ab[1], uBuf.ab[2], uBuf.ab[3], 519 uBuf.ab[4], uBuf.ab[5], uBuf.ab[6], uBuf.ab[7]); 520 } 1067 521 break; 1068 522 } 1069 1070 off++;1071 523 } 1072 524 } 1073 525 } 1074 526 1075 /* next newline */1076 enmState = kOCDepState_NeedNewLine;1077 }1078 1079 return pDepState->enmState = enmState;527 } 528 else 529 rcRet = 1; 530 free(paSymbols); 531 return rcRet; 1080 532 } 1081 533 1082 534 1083 535 /** 1084 * Writes the dependencies to the specified file. 1085 * 1086 * @param pDepState The dependency collector state. 1087 * @param pszFilename The name of the dependency file. 1088 * @param pszObjFile The object file name, relative to @a pszObjDir. 1089 * @param pszObjDir The object file directory. 1090 * @param fFixCase Whether to fix the case of dependency files. 1091 * @param fQuiet Whether to be quiet about the dependencies. 1092 * @param fGenStubs Whether to generate stubs. 1093 */ 1094 static void kOCDepWriteToFile(PKOCDEP pDepState, const char *pszFilename, const char *pszObjFile, const char *pszObjDir, 1095 int fFixCase, int fQuiet, int fGenStubs) 1096 { 1097 char *pszObjFileAbs; 1098 char *psz; 1099 FILE *pFile = fopen(pszFilename, "w"); 1100 if (!pFile) 1101 FatalMsg("Failed to open dependency file '%s': %s\n", pszFilename, strerror(errno)); 1102 1103 depOptimize(fFixCase, fQuiet); 1104 1105 /* Make object file name with unix slashes. */ 1106 pszObjFileAbs = MakePathFromDirAndFile(pszObjFile, pszObjDir); 1107 psz = pszObjFileAbs; 1108 while ((psz = strchr(psz, '\\')) != NULL) 1109 *psz++ = '/'; 1110 1111 fprintf(pFile, "%s:", pszObjFileAbs); 1112 free(pszObjFileAbs); 1113 depPrint(pFile); 1114 if (fGenStubs) 1115 depPrintStubs(pFile); 1116 1117 if (fclose(pFile) != 0) 1118 FatalMsg("Failed to write dependency file '%s': %s\n", pszFilename, strerror(errno)); 1119 } 1120 1121 1122 /** 1123 * Preprocessor output reader state. 1124 */ 1125 typedef struct KOCCPPRD 1126 { 1127 /** Pointer to the preprocessor output. */ 1128 char *pszBuf; 1129 /** Allocated buffer size. */ 1130 size_t cbBufAlloc; 1131 /** Amount preprocessor output that we've completed optimizations for. */ 1132 size_t cchDstOptimized; 1133 /** Offset to the start of the unoptimized source. */ 1134 size_t offSrcUnoptimized; 1135 /** The offset of the next bits to process. */ 1136 size_t offSrcCur; 1137 /** The offset where to put more raw preprocessor output. */ 1138 size_t offSrcRead; 1139 /** The line number corresponding to offOptimized. */ 1140 uint32_t uOptLineNo; 1141 /** The current line number. */ 1142 uint32_t uCurLineNo; 1143 /** Set if we're done, clear if we're expecting more preprocessor output. */ 1144 int fDone; 1145 /** The saved character at cchOptimized. */ 1146 char chSaved; 1147 /** Whether the optimizations are enabled. */ 1148 int fOptimize; 1149 1150 /** Buffer holding the current file name (unescaped). */ 1151 char *pszFileNmBuf; 1152 /** The size of the file name buffer. */ 1153 size_t cbFileNmBuf; 1154 /** The length of the current file string. */ 1155 size_t cchCurFileNm; 1156 1157 /** Line directive / new line sequence buffer. */ 1158 char *pszLineBuf; 1159 /** The size of the buffer pointed to by pszLineBuf. */ 1160 size_t cbLineBuf; 1161 1162 /** Set if we should work the dependency generator as well. */ 1163 PKOCDEP pDepState; 1164 } KOCCPPRD; 1165 /** Pointer to a preprocessor reader state. */ 1166 typedef KOCCPPRD *PKOCCPPRD; 1167 1168 1169 /** 1170 * Allocate the initial C preprocessor output buffer. 1171 * 1172 * @param pCppRd The C preprocessor reader instance. 1173 * @param cbOldCpp The size of the output the last time. This is 0 if 1174 * there was not previous run. 1175 * @param fOptimize Whether optimizations are enabled. 1176 * @param pDepState Pointer to the dependency generator. Must only be set 1177 * if @a fOptimize is also set. 1178 */ 1179 static void kOCCppRdInit(PKOCCPPRD pCppRd, size_t cbOldCpp, int fOptimize, PKOCDEP pDepState) 1180 { 1181 assert(!pDepState || fOptimize); 1182 1183 pCppRd->cbBufAlloc = cbOldCpp ? (cbOldCpp + KOC_BUF_INCR) & ~(KOC_BUF_ALIGNMENT - 1) : KOC_BUF_MIN; 1184 pCppRd->pszBuf = xmalloc(pCppRd->cbBufAlloc); 1185 pCppRd->cchCurFileNm = 0; 1186 pCppRd->cchDstOptimized = 0; 1187 pCppRd->offSrcUnoptimized = 0; 1188 pCppRd->offSrcCur = 0; 1189 pCppRd->offSrcRead = 0; 1190 pCppRd->uOptLineNo = 1; 1191 pCppRd->uCurLineNo = 1; 1192 pCppRd->fDone = 0; 1193 pCppRd->chSaved = 0; 1194 pCppRd->fOptimize = fOptimize; 1195 1196 pCppRd->pszFileNmBuf = NULL; 1197 pCppRd->cbFileNmBuf = 0; 1198 pCppRd->cchCurFileNm = 0; 1199 1200 pCppRd->pszLineBuf = NULL; 1201 pCppRd->cbLineBuf = 0; 1202 1203 pCppRd->pDepState = pDepState; 1204 } 1205 1206 1207 static void kOCCppRdDelete(PKOCCPPRD pCppRd) 1208 { 1209 free(pCppRd->pszBuf); 1210 pCppRd->pszBuf = NULL; 1211 1212 free(pCppRd->pszFileNmBuf); 1213 pCppRd->pszFileNmBuf = NULL; 1214 1215 free(pCppRd->pszLineBuf); 1216 pCppRd->pszLineBuf = NULL; 1217 } 1218 1219 1220 /** 1221 * Allocate more buffer space for the C preprocessor output. 1222 * 1223 * @param pCppRd The C preprocessor reader instance. 1224 */ 1225 static size_t kOCCppRdGrowBuffer(PKOCCPPRD pCppRd) 1226 { 1227 pCppRd->cbBufAlloc += KOC_BUF_INCR; 1228 pCppRd->pszBuf = xrealloc(pCppRd->pszBuf, pCppRd->cbBufAlloc); 1229 1230 return pCppRd->cbBufAlloc - pCppRd->offSrcRead; 1231 } 1232 1233 1234 static size_t kOCCppRdOptInsert(PKOCCPPRD pCppRd, size_t cchSrcReplaced, const char *pchInsert, size_t cchInsert) 1235 { 1236 size_t offDelta = 0; 1237 size_t cchAvail; 1238 1239 pCppRd->offSrcUnoptimized += cchSrcReplaced; 1240 assert(pCppRd->offSrcUnoptimized <= pCppRd->offSrcCur); 1241 cchAvail = pCppRd->offSrcUnoptimized - pCppRd->cchDstOptimized; 1242 if (cchAvail < cchInsert) 1243 { 1244 size_t const cbToMove = pCppRd->offSrcRead - pCppRd->offSrcUnoptimized; 1245 assert(cbToMove <= pCppRd->offSrcRead); 1246 offDelta = cchInsert - cchAvail; 1247 1248 while (pCppRd->offSrcRead + offDelta >= pCppRd->cbBufAlloc) 1249 kOCCppRdGrowBuffer(pCppRd); 1250 1251 g_cMemMoves++; 1252 g_cbMemMoved += cbToMove + 1; 1253 memmove(pCppRd->pszBuf + pCppRd->offSrcUnoptimized + offDelta, 1254 pCppRd->pszBuf + pCppRd->offSrcUnoptimized, 1255 cbToMove + 1); 1256 1257 pCppRd->offSrcRead += offDelta; 1258 pCppRd->offSrcUnoptimized += offDelta; 1259 pCppRd->offSrcCur += offDelta; 1260 assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0'); 1261 } 1262 1263 memcpy(pCppRd->pszBuf + pCppRd->cchDstOptimized, pchInsert, cchInsert); 1264 pCppRd->cchDstOptimized += cchInsert; 1265 1266 return offDelta; 1267 } 1268 1269 1270 static void kOCCppRdOptCommit(PKOCCPPRD pCppRd) 1271 { 1272 size_t cchToCommit = pCppRd->offSrcCur - pCppRd->offSrcUnoptimized; 1273 assert(pCppRd->offSrcUnoptimized <= pCppRd->offSrcCur); 1274 1275 if (cchToCommit) 1276 { 1277 memmove(pCppRd->pszBuf + pCppRd->cchDstOptimized, pCppRd->pszBuf + pCppRd->offSrcUnoptimized, cchToCommit); 1278 pCppRd->cchDstOptimized += cchToCommit; 1279 pCppRd->offSrcUnoptimized = pCppRd->offSrcCur; 1280 } 1281 1282 pCppRd->uOptLineNo = pCppRd->uCurLineNo; 1283 } 1284 1285 1286 1287 static char *kOCCppRdOptGetEol(PKOCCPPRD pCppRd, char *pszCur, size_t cbLeft) 1288 { 1289 char *pszEol = memchr(pszCur, '\n', cbLeft); 1290 if (pszEol) 1291 { 1292 if (pszCur != pszEol && pszEol[-1] == '\r') 1293 pszEol--; 1294 } 1295 else if (pCppRd->fDone && cbLeft) 1296 pszEol = pszCur + cbLeft; 1297 return pszEol; 1298 } 1299 1300 static void kOCCppRdOptSetFile(PKOCCPPRD pCppRd, const char *pchFile, size_t cchFile) 1301 { 1302 if (cchFile >= pCppRd->cbFileNmBuf) 1303 { 1304 pCppRd->cbFileNmBuf = (cchFile + 15 + 1) & ~(size_t)15; 1305 pCppRd->pszFileNmBuf = xrealloc(pCppRd->pszFileNmBuf, pCppRd->cbFileNmBuf); 1306 } 1307 memcpy(pCppRd->pszFileNmBuf, pchFile, cchFile); 1308 pCppRd->pszFileNmBuf[cchFile] = '\0'; 1309 pCppRd->cchCurFileNm = cchFile; 1310 } 1311 1312 1313 static size_t kOCCppRdOptFmtLine(PKOCCPPRD pCppRd, uint32_t uLine, const char *pchFile, size_t cchFile) 1314 { 1315 size_t cchUsed; 1316 size_t cbNeeded; 1317 1318 /* Make sure we've got enough buffer space. */ 1319 cbNeeded = sizeof("#line 4888222111 \"\"\n") + cchFile; 1320 if (cbNeeded > pCppRd->cbLineBuf) 1321 { 1322 pCppRd->cbLineBuf = (cbNeeded + 32 + 15) & ~(size_t)15; 1323 pCppRd->pszLineBuf = xrealloc(pCppRd->pszLineBuf, pCppRd->cbLineBuf); 1324 } 1325 1326 /* Do the formatting. */ 1327 cchUsed = sprintf(pCppRd->pszLineBuf, "#line %lu", (unsigned long)uLine); 1328 if (cchFile) 1329 { 1330 pCppRd->pszLineBuf[cchUsed++] = ' '; 1331 pCppRd->pszLineBuf[cchUsed++] = '"'; 1332 memcpy(&pCppRd->pszLineBuf[cchUsed], pchFile, cchFile); 1333 cchUsed += cchFile; 1334 pCppRd->pszLineBuf[cchUsed++] = '"'; 1335 } 1336 pCppRd->pszLineBuf[cchUsed++] = '\n'; 1337 pCppRd->pszLineBuf[cchUsed] = '\0'; 1338 1339 return cchUsed; 1340 } 1341 1342 1343 static size_t kOCCppRdOptFmtNewLines(PKOCCPPRD pCppRd, uint32_t cNewLines) 1344 { 1345 if (cNewLines + 1 > pCppRd->cbLineBuf) 1346 { 1347 pCppRd->cbLineBuf = (cNewLines + 1 + 32 + 15) & ~(size_t)15; 1348 pCppRd->pszLineBuf = xrealloc(pCppRd->pszLineBuf, pCppRd->cbLineBuf); 1349 } 1350 1351 memset(pCppRd->pszLineBuf, '\n', cNewLines); 1352 pCppRd->pszLineBuf[cNewLines] = '\0'; 1353 return cNewLines; 1354 } 1355 1356 1357 static size_t kOCCppRdOptFlush(PKOCCPPRD pCppRd, size_t offSrcCur, int fLineDirNext) 1358 { 1359 size_t offDelta = 0; 1360 size_t const offSrcUnoptimized = pCppRd->offSrcUnoptimized; 1361 assert(offSrcUnoptimized <= offSrcCur); 1362 1363 if (offSrcCur > offSrcUnoptimized) 536 * Clears timestamps to avoid rebuilding stuff just because the internal 537 * timestamps changed in an import library. 538 */ 539 static int kLibClearTimestamps(FILE *pFile, fpos_t offFileHdr, ARCHFILEHDR *pFileHdr, fpos_t cbFile, fpos_t offFileBytes) 540 { 541 union 542 { 543 IMAGE_FILE_HEADER CoffHdr; 544 COFFIMPLIBHDR ImpLibHdr; 545 } u; 546 if (sizeof(u.CoffHdr) != sizeof(u.ImpLibHdr)) 547 FatalDie("Oops!"); 548 549 /* 550 * Clear the timestamp in the library file header. 551 */ 552 memset(pFileHdr->achModtime, '0', sizeof(pFileHdr->achModtime)); 553 if (kLibMyWriteAt(pFile, pFileHdr, sizeof(*pFileHdr), offFileHdr) != 0) 554 return 1; 555 556 /* 557 * Clear the timestamp in the COFF header, if we find one. 558 */ 559 if (cbFile <= sizeof(IMAGE_FILE_HEADER)) 560 return 0; 561 if (kLibMyReadAt(pFile, &u.CoffHdr, sizeof(u.CoffHdr), offFileBytes, 0) != 0) 562 return 1; 563 564 if ( ( u.CoffHdr.Machine == IMAGE_FILE_MACHINE_I386 565 || u.CoffHdr.Machine == IMAGE_FILE_MACHINE_AMD64) 566 && sizeof(IMAGE_FILE_HEADER) 567 + u.CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) 568 <= (KU64)cbFile 569 && u.CoffHdr.PointerToSymbolTable <= (KU64)cbFile) 570 { 571 InfoMsg(1, "Found COFF file header\n"); 572 if (u.CoffHdr.TimeDateStamp != 0) 573 { 574 u.CoffHdr.TimeDateStamp = 0; 575 return kLibMyWriteAt(pFile, &u.CoffHdr, sizeof(u.CoffHdr), offFileBytes); 576 } 577 } 578 else if ( u.ImpLibHdr.uSig1 == 0 579 && u.ImpLibHdr.uSig2 == 0xffff 580 && u.ImpLibHdr.uVersion == 0 581 && ( u.ImpLibHdr.uMachine == IMAGE_FILE_MACHINE_I386 582 || u.ImpLibHdr.uMachine == IMAGE_FILE_MACHINE_AMD64) 583 && u.ImpLibHdr.cbData <= cbFile) 584 { 585 InfoMsg(1, "Found COFF import library header\n"); 586 if (u.ImpLibHdr.uTimeDateStamp) 587 { 588 u.ImpLibHdr.uTimeDateStamp = 0; 589 return kLibMyWriteAt(pFile, &u.ImpLibHdr, sizeof(u.ImpLibHdr), offFileBytes); 590 } 591 } 592 else 593 InfoMsg(1, "CoffHdr.Machine=%#x ImpLibHdr.Machine=%#x\n", u.CoffHdr.Machine, u.ImpLibHdr.uMachine); 594 595 return 0; 596 } 597 598 599 static int kLibTweakerDoIt(const char *pszLib, int fClearTimestamps, int fFillNullThunkData) 600 { 601 int rcRet = 0; 602 FILE *pFile = fopen(pszLib, "r+b"); 603 if (pFile) 1364 604 { 1365 605 /* 1366 * We've got unflushed whitelines.606 * Read the header. 1367 607 */ 1368 size_t const cchSrcInQuestion = offSrcCur - offSrcUnoptimized; 1369 uint32_t const cLinesInQuestion = pCppRd->uCurLineNo - pCppRd->uOptLineNo; 1370 size_t cchLineDir; 1371 1372 if ( cLinesInQuestion <= 7 1373 || (cchLineDir = kOCCppRdOptFmtLine(pCppRd, pCppRd->uCurLineNo, NULL, 0)) >= cLinesInQuestion) 1374 cchLineDir = kOCCppRdOptFmtNewLines(pCppRd, cLinesInQuestion); 1375 1376 offDelta = kOCCppRdOptInsert(pCppRd, cchSrcInQuestion, pCppRd->pszLineBuf, cchLineDir); 1377 } 1378 1379 (void)fLineDirNext; /* Use later if required. */ 1380 return offDelta; 1381 } 1382 1383 1384 static int kOCCppRdOptParseLine(PKOCCPPRD pCppRd, char *pszCur, char *pszEol, 1385 uint32_t *puNewLineNo, char **ppszNewFile, size_t *pcchNewFile) 1386 { 1387 char *psz = pszCur; 1388 uint32_t uNewLineNo; 1389 int fIsShort; 1390 1391 /* 1392 * Check if it's a #line directive of some kind and parse it. 1393 */ 1394 if (*psz != '#') 1395 return 0; 1396 psz++; 1397 1398 fIsShort = MY_IS_BLANK(*psz); 1399 while (MY_IS_BLANK(*psz)) 1400 psz++; 1401 1402 if ( psz[0] == 'l' 1403 && psz[1] == 'i' 1404 && psz[2] == 'n' 1405 && psz[3] == 'e' 1406 && MY_IS_BLANK(psz[4]) ) 1407 { 1408 fIsShort = 0; 1409 psz += 5; 1410 while (MY_IS_BLANK(*psz)) 1411 psz++; 1412 } 1413 else if (fIsShort && isdigit(*psz)) 1414 fIsShort = 1; 1415 else 1416 return 0; 1417 1418 /* Parse the line number. */ 1419 if (!isdigit(*psz)) 1420 return 0; 1421 1422 uNewLineNo = *psz++ - '0'; 1423 while (isdigit(*psz)) 1424 { 1425 uNewLineNo *= 10; 1426 uNewLineNo += *psz++ - '0'; 1427 } 1428 if ( psz != pszEol 1429 && !MY_IS_BLANK(*psz)) 1430 return 0; 1431 1432 /* 1433 * The file name part is optional. 1434 */ 1435 while (MY_IS_BLANK(*psz)) 1436 psz++; 1437 1438 if ( psz != pszEol 1439 && *psz == '"') 1440 { 1441 *ppszNewFile = ++psz; 1442 while ( psz != pszEol 1443 && ( *psz != '"' 1444 || ( psz[-1] == '\\' 1445 && kOCDepIsEscaped(psz, psz - *ppszNewFile)) ) 1446 ) 1447 psz++; 1448 if (psz == pszEol) 1449 { 1450 /** @todo complain? */ 1451 return 0; 1452 } 1453 *pcchNewFile = psz - *ppszNewFile; 1454 1455 do 1456 psz++; 1457 while (psz != pszEol && MY_IS_BLANK(*psz)); 1458 } 1459 else 1460 { 1461 /* No file given => Same as the current. */ 1462 *ppszNewFile = pCppRd->cchCurFileNm ? pCppRd->pszFileNmBuf : NULL; 1463 *pcchNewFile = pCppRd->cchCurFileNm; 1464 } 1465 if (psz != pszEol) 1466 { 1467 /** @todo complain? */ 1468 return 0; 1469 } 1470 1471 *puNewLineNo = uNewLineNo; 1472 return 1; 1473 } 1474 1475 1476 static char *kOCCppRdOptHandleLine(PKOCCPPRD pCppRd, char *pszCur, size_t *pcbLeft, int *pfEmptyLine, char *pszEol) 1477 { 1478 size_t const offSrcLine = pCppRd->offSrcCur; 1479 size_t const cchSrcLine = pszEol - pCppRd->pszBuf - (pCppRd->fOptimize & 2 ? pCppRd->offSrcUnoptimized : pCppRd->offSrcCur); 1480 size_t const cbLeftAssert = *pcbLeft; 1481 char *pszNewFile; 1482 size_t cchNewFile; 1483 uint32_t uNewLineNo; 1484 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0'); 1485 1486 /* Advance to the end of the line before we do anything. This can be a 1487 little confusing but it saves effort and avoid trouble in the end. */ 1488 pCppRd->offSrcCur = pszEol - pCppRd->pszBuf; 1489 *pcbLeft -= pszEol - pszCur; 1490 assert(*pcbLeft <= cbLeftAssert); (void)cbLeftAssert; 1491 1492 /* 1493 * Try parse the directive a '#line' one.... 1494 */ 1495 if (!kOCCppRdOptParseLine(pCppRd, pszCur, pszEol, &uNewLineNo, &pszNewFile, &cchNewFile)) 1496 { 1497 /* 1498 * No line directive. Flush pending optimizations and indicate that 1499 * the line isn't empty and needs to be commited at EOL. 1500 */ 1501 kOCCppRdOptFlush(pCppRd, offSrcLine, 0); 1502 *pfEmptyLine = 0; 1503 } 1504 else 1505 { 1506 char *pszCurFile = pCppRd->cchCurFileNm ? pCppRd->pszFileNmBuf : NULL; 1507 if ( pszNewFile == pszCurFile 1508 || ( cchNewFile == pCppRd->cchCurFileNm 1509 && !memcmp(pszNewFile, pszCurFile, cchNewFile)) ) 1510 { 1511 /* 1512 * A #line directive specifying the same file. 1513 */ 1514 if (uNewLineNo >= pCppRd->uCurLineNo) 1515 *pfEmptyLine = 1; 1516 else 608 static char s_szMagic[] = "!<arch>\n"; 609 union 610 { 611 char ab[1024]; 612 IMAGE_FILE_HEADER CoffHdr; 613 } uBuf; 614 if ( fread(uBuf.ab, 1, sizeof(s_szMagic) - 1, pFile) == sizeof(s_szMagic) - 1 615 && memcmp(uBuf.ab, s_szMagic, sizeof(s_szMagic) - 1) == 0) 616 { 617 fpos_t offFileHdr = sizeof(s_szMagic) - 1; 618 while (!feof(pFile)) 1517 619 { 1518 /* 1519 * It went backwards, so we need to flush the old section of 1520 * the file and emit another directive for starting the new one. 1521 */ 1522 size_t cchLineDir; 1523 if (!(pCppRd->fOptimize & 2)) 1524 kOCCppRdOptFlush(pCppRd, offSrcLine, 1); 1525 1526 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, NULL, 0) - 1; /* sans \n */ 1527 kOCCppRdOptInsert(pCppRd, cchSrcLine, pCppRd->pszLineBuf, cchLineDir); 1528 1529 *pfEmptyLine = 0; 620 ARCHFILEHDR FileHdr; 621 if (kLibMyReadAt(pFile, &FileHdr, sizeof(FileHdr), offFileHdr, 1) != 0) 622 { 623 if (feof(pFile)) 624 break; 625 rcRet = ErrorMsg("failed reading the file header (offset %ld)\n", (long)offFileHdr); 626 break; 627 } 628 if ( FileHdr.achMagic[0] == 0x60 629 && FileHdr.achMagic[1] == 0x0a) 630 { 631 fpos_t const offFileBytes = offFileHdr + sizeof(FileHdr); 632 633 /* 634 * Convert the size from decimal to binary as we need it to skip to 635 * the next file header. 636 */ 637 fpos_t const cb = kLibTweakerAsciiToSize(FileHdr.achSize, sizeof(FileHdr.achSize)); 638 InfoMsg(1, "Found header at %#lx: cbFile=%#lx, bytes at %#lx\n", 639 (unsigned long)offFileHdr, (unsigned long)cb, (unsigned long)offFileBytes); 640 641 /* 642 * Make the requested changes. 643 */ 644 if (fClearTimestamps) 645 rcRet |= kLibClearTimestamps(pFile, offFileHdr, &FileHdr, cb, offFileBytes); 646 647 if (fFillNullThunkData) 648 rcRet |= kLibFillNullThunkData(pFile, cb, offFileBytes); 649 650 /* 651 * Skip to the next header. 652 */ 653 offFileHdr = offFileBytes + ((cb + 1) & ~(fpos_t)1); 654 } 655 else 656 rcRet = ErrorMsg("invalid file header magic (offset %ld)\n", (long)offFileHdr); 1530 657 } 1531 658 } 1532 659 else 1533 { 1534 /* 1535 * The #line directive changed the file. 1536 */ 1537 size_t cchLineDir; 1538 1539 kOCCppRdOptSetFile(pCppRd, pszNewFile, cchNewFile); /* save to do this early */ 1540 if (!(pCppRd->fOptimize & 2)) 1541 kOCCppRdOptFlush(pCppRd, offSrcLine, 1); 1542 1543 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, pCppRd->pszFileNmBuf, cchNewFile) - 1; /* sans \n */ 1544 kOCCppRdOptInsert(pCppRd, cchSrcLine, pCppRd->pszLineBuf, cchLineDir); 1545 1546 if (pCppRd->pDepState) 1547 kOCDepEnter(pCppRd->pDepState, pCppRd->pszFileNmBuf, cchNewFile); 1548 1549 *pfEmptyLine = 0; 1550 } 1551 1552 pCppRd->uCurLineNo = uNewLineNo - 1; 1553 } 1554 1555 return pCppRd->pszBuf + pCppRd->offSrcCur; 1556 } 1557 1558 1559 static void kOCCppRdOpt(PKOCCPPRD pCppRd) 1560 { 1561 size_t cch; 1562 char *pszEol; 1563 char *pszCur = pCppRd->pszBuf + pCppRd->offSrcCur; 1564 size_t cbTodo = pCppRd->offSrcRead - pCppRd->offSrcCur; 1565 int fEmptyLine = 1; 1566 1567 while (cbTodo > 0) 1568 { 1569 switch (*pszCur) 1570 { 1571 case ' ': 1572 case '\t': 1573 break; 1574 1575 case '\n': 1576 pCppRd->offSrcCur = pszCur - pCppRd->pszBuf + 1; 1577 pCppRd->uCurLineNo++; 1578 if (!fEmptyLine) 1579 kOCCppRdOptCommit(pCppRd); 1580 fEmptyLine = 1; 1581 break; 1582 1583 case '\r': /* "\r\n" -> "\n" */ 1584 if (cbTodo <= 1 && !pCppRd->fDone) 1585 return; 1586 if (pszCur[1] == '\n' && !fEmptyLine) 1587 { 1588 /* Commit the part up to the '\r' first, replace '\r\n' with '\n'. */ 1589 pCppRd->offSrcCur = pszCur - pCppRd->pszBuf; 1590 kOCCppRdOptCommit(pCppRd); 1591 1592 pCppRd->offSrcCur += 2; 1593 kOCCppRdOptInsert(pCppRd, 2, "\n", 1); 1594 1595 assert(cbTodo >= 2); 1596 cbTodo -= 2; 1597 pszCur += 2; 1598 1599 fEmptyLine = 1; 1600 continue; 1601 } 1602 break; 1603 1604 case '#': 1605 pszEol = kOCCppRdOptGetEol(pCppRd, pszCur + 1, cbTodo - 1); 1606 if (!pszEol) 1607 return; 1608 pszCur = kOCCppRdOptHandleLine(pCppRd, pszCur, &cbTodo, &fEmptyLine, pszEol); 1609 continue; 1610 1611 default: 1612 /* 1613 * Some non-white stuff encountered, flush pending white 1614 * line optimizations and skip to the end of the line. 1615 */ 1616 fEmptyLine = 0; 1617 pszEol = kOCCppRdOptGetEol(pCppRd, pszCur + 1, cbTodo - 1); 1618 if (!pszEol) 1619 return; 1620 cch = pszEol - pszCur; 1621 1622 pszCur += kOCCppRdOptFlush(pCppRd, pCppRd->offSrcCur, 0); 1623 1624 assert(cch <= cbTodo); 1625 cbTodo -= cch; 1626 pszCur += cch; 1627 continue; 1628 } 1629 1630 cbTodo--; 1631 pszCur++; 1632 } 1633 } 1634 1635 1636 static void kOCCppRdOptFinalize(PKOCCPPRD pCppRd) 1637 { 1638 pCppRd->fDone = 1; 1639 assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0'); 1640 pCppRd->pszBuf[pCppRd->offSrcRead] = '\0'; 1641 kOCCppRdOpt(pCppRd); 1642 1643 assert(pCppRd->offSrcCur == pCppRd->offSrcRead); 1644 kOCCppRdOptFlush(pCppRd, pCppRd->offSrcCur, 0); 1645 } 1646 1647 1648 1649 /** 1650 * Read C preprocessor output from the given file descriptor, optionally 1651 * optimzing it. 1652 * 1653 * @returns Number of bytes read. 0 indicates end of file. 1654 * 1655 * @param pCppRd The C preprocessor reader instance. 1656 * @param fdIn The file descriptor to read the raw preprocessor output 1657 * from. 1658 * @param ppszRet Where to return the pointer to the output. 1659 * 1660 * @remarks Won't return on error, calls FatalDie on those occasions. 1661 */ 1662 static long kOCCppRdRead(PKOCCPPRD pCppRd, int fdIn, const char **ppszRet) 1663 { 1664 size_t cbLeft; 1665 long cbRead; 1666 1667 if (pCppRd->fOptimize) 1668 { 1669 /* 1670 * Optimize the C preprocessor output on the way thru. 1671 */ 1672 size_t const cchOldOptimized = pCppRd->cchDstOptimized; 1673 if (pCppRd->chSaved) 1674 pCppRd->pszBuf[pCppRd->cchDstOptimized] = pCppRd->chSaved; 1675 1676 do 1677 { 1678 /* Read more raw C preprocessor output. */ 1679 cbLeft = pCppRd->cbBufAlloc - pCppRd->offSrcRead; 1680 if (cbLeft <= 1) 1681 cbLeft = kOCCppRdGrowBuffer(pCppRd); 1682 1683 do 1684 cbRead = read(fdIn, pCppRd->pszBuf + pCppRd->offSrcRead, (long)(cbLeft - 1)); 1685 while (cbRead < 0 && errno == EINTR); 1686 if (cbRead < 0) 1687 FatalDie("kOCCppRdRead - read(%d,,%ld) failed: %s\n", 1688 fdIn, (long)(cbLeft - 1), strerror(errno)); 1689 pCppRd->offSrcRead += cbRead; 1690 1691 /* Optimize it. */ 1692 if (!cbRead) 1693 { 1694 kOCCppRdOptFinalize(pCppRd); 1695 break; 1696 } 1697 kOCCppRdOpt(pCppRd); 1698 } while (pCppRd->cchDstOptimized == cchOldOptimized); 1699 1700 *ppszRet = &pCppRd->pszBuf[cchOldOptimized]; 1701 pCppRd->chSaved = pCppRd->pszBuf[pCppRd->cchDstOptimized]; 1702 pCppRd->pszBuf[pCppRd->cchDstOptimized] = '\0'; 1703 cbRead = (long)(pCppRd->cchDstOptimized - cchOldOptimized); 1704 } 1705 else 1706 { 1707 /* 1708 * Pass thru. 1709 */ 1710 char *pszBuf; 1711 cbLeft = pCppRd->cbBufAlloc - pCppRd->offSrcRead; 1712 if (cbLeft <= 1) 1713 cbLeft = kOCCppRdGrowBuffer(pCppRd); 1714 pszBuf = pCppRd->pszBuf + pCppRd->offSrcRead; 1715 1716 do 1717 cbRead = read(fdIn, pszBuf, (long)(cbLeft - 1)); 1718 while (cbRead < 0 && errno == EINTR); 1719 if (cbRead < 0) 1720 FatalDie("kOCCppRdRead - read(%d,,%ld) failed: %s\n", 1721 fdIn, (long)(cbLeft - 1), strerror(errno)); 1722 1723 *ppszRet = pszBuf; 1724 pCppRd->offSrcRead += cbRead; 1725 pszBuf[cbRead] = '\0'; 1726 } 1727 1728 return cbRead; 1729 } 1730 1731 1732 /** 1733 * Grabs the output buffer from the C preprocessor reader. 1734 * 1735 * @param pCppRd The C preprocessor reader instance. 1736 * @param ppszRet Where to return the pointer to the output. 1737 * @param pcbRet Where to return the size of the output. 1738 */ 1739 static void kOCCppRdGrabOutput(PKOCCPPRD pCppRd, char **ppszRet, size_t *pcbRet) 1740 { 1741 assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0'); 1742 *ppszRet = pCppRd->pszBuf; 1743 *pcbRet = pCppRd->fOptimize ? pCppRd->cchDstOptimized : pCppRd->offSrcRead; 1744 pCppRd->pszBuf = NULL; 1745 pCppRd->offSrcRead = 0; 1746 } 1747 1748 1749 1750 1751 1752 1753 /** A checksum list entry. 1754 * We keep a list checksums (of preprocessor output) that matches. 1755 * 1756 * The matching algorithm doesn't require the preprocessor output to be 1757 * indentical, only to produce the same object files. 1758 */ 1759 typedef struct KOCSUM 1760 { 1761 /** The next checksum. */ 1762 struct KOCSUM *pNext; 1763 /** The crc32 checksum. */ 1764 uint32_t crc32; 1765 /** The MD5 digest. */ 1766 unsigned char md5[16]; 1767 /** Valid or not. */ 1768 unsigned fUsed; 1769 } KOCSUM; 1770 /** Pointer to a KOCSUM. */ 1771 typedef KOCSUM *PKOCSUM; 1772 /** Pointer to a const KOCSUM. */ 1773 typedef const KOCSUM *PCKOCSUM; 1774 1775 1776 /** 1777 * Temporary context record used when calculating the checksum of some data. 1778 */ 1779 typedef struct KOCSUMCTX 1780 { 1781 /** The MD5 context. */ 1782 struct MD5Context MD5Ctx; 1783 } KOCSUMCTX; 1784 /** Pointer to a check context record. */ 1785 typedef KOCSUMCTX *PKOCSUMCTX; 1786 1787 1788 1789 /** 1790 * Initializes a checksum object with an associated context. 1791 * 1792 * @param pSum The checksum object. 1793 * @param pCtx The checksum context. 1794 */ 1795 static void kOCSumInitWithCtx(PKOCSUM pSum, PKOCSUMCTX pCtx) 1796 { 1797 memset(pSum, 0, sizeof(*pSum)); 1798 MD5Init(&pCtx->MD5Ctx); 1799 } 1800 1801 1802 /** 1803 * Updates the checksum calculation. 1804 * 1805 * @param pSum The checksum. 1806 * @param pCtx The checksum calcuation context. 1807 * @param pvBuf The input data to checksum. 1808 * @param cbBuf The size of the input data. 1809 */ 1810 static void kOCSumUpdate(PKOCSUM pSum, PKOCSUMCTX pCtx, const void *pvBuf, size_t cbBuf) 1811 { 1812 /* 1813 * Take in relativly small chunks to try keep it in the cache. 1814 */ 1815 const unsigned char *pb = (const unsigned char *)pvBuf; 1816 while (cbBuf > 0) 1817 { 1818 size_t cb = cbBuf >= 128*1024 ? 128*1024 : cbBuf; 1819 pSum->crc32 = crc32(pSum->crc32, pb, cb); 1820 MD5Update(&pCtx->MD5Ctx, pb, (unsigned)cb); 1821 cbBuf -= cb; 1822 } 1823 } 1824 1825 1826 /** 1827 * Finalizes a checksum calculation. 1828 * 1829 * @param pSum The checksum. 1830 * @param pCtx The checksum calcuation context. 1831 */ 1832 static void kOCSumFinalize(PKOCSUM pSum, PKOCSUMCTX pCtx) 1833 { 1834 MD5Final(&pSum->md5[0], &pCtx->MD5Ctx); 1835 pSum->fUsed = 1; 1836 } 1837 1838 1839 /** 1840 * Init a check sum chain head. 1841 * 1842 * @param pSumHead The checksum head to init. 1843 */ 1844 static void kOCSumInit(PKOCSUM pSumHead) 1845 { 1846 memset(pSumHead, 0, sizeof(*pSumHead)); 1847 } 1848 1849 1850 /** 1851 * Parses the given string into a checksum head object. 1852 * 1853 * @returns 0 on success, -1 on format error. 1854 * @param pSumHead The checksum head to init. 1855 * @param pszVal The string to initialized it from. 1856 */ 1857 static int kOCSumInitFromString(PKOCSUM pSumHead, const char *pszVal) 1858 { 1859 unsigned i; 1860 char *pszNext; 1861 char *pszMD5; 1862 1863 memset(pSumHead, 0, sizeof(*pSumHead)); 1864 1865 pszMD5 = strchr(pszVal, ':'); 1866 if (pszMD5 == NULL) 1867 return -1; 1868 *pszMD5++ = '\0'; 1869 1870 /* crc32 */ 1871 pSumHead->crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16); 1872 if (pszNext && *pszNext) 1873 return -1; 1874 1875 /* md5 */ 1876 for (i = 0; i < sizeof(pSumHead->md5) * 2; i++) 1877 { 1878 unsigned char ch = pszMD5[i]; 1879 int x; 1880 if ((unsigned char)(ch - '0') <= 9) 1881 x = ch - '0'; 1882 else if ((unsigned char)(ch - 'a') <= 5) 1883 x = ch - 'a' + 10; 1884 else if ((unsigned char)(ch - 'A') <= 5) 1885 x = ch - 'A' + 10; 1886 else 1887 return -1; 1888 if (!(i & 1)) 1889 pSumHead->md5[i >> 1] = x << 4; 1890 else 1891 pSumHead->md5[i >> 1] |= x; 1892 } 1893 1894 pSumHead->fUsed = 1; 1895 return 0; 1896 } 1897 1898 1899 /** 1900 * Delete a check sum chain. 1901 * 1902 * @param pSumHead The head of the checksum chain. 1903 */ 1904 static void kOCSumDeleteChain(PKOCSUM pSumHead) 1905 { 1906 PKOCSUM pSum = pSumHead->pNext; 1907 while (pSum) 1908 { 1909 void *pvFree = pSum; 1910 pSum = pSum->pNext; 1911 free(pvFree); 1912 } 1913 memset(pSumHead, 0, sizeof(*pSumHead)); 1914 } 1915 1916 1917 /** 1918 * Insert a check sum into the chain. 1919 * 1920 * @param pSumHead The head of the checksum list. 1921 * @param pSumAdd The checksum to add (duplicate). 1922 */ 1923 static void kOCSumAdd(PKOCSUM pSumHead, PCKOCSUM pSumAdd) 1924 { 1925 if (pSumHead->fUsed) 1926 { 1927 PKOCSUM pNew = xmalloc(sizeof(*pNew)); 1928 *pNew = *pSumAdd; 1929 pNew->pNext = pSumHead->pNext; 1930 pNew->fUsed = 1; 1931 pSumHead->pNext = pNew; 1932 } 1933 else 1934 { 1935 *pSumHead = *pSumAdd; 1936 pSumHead->pNext = NULL; 1937 pSumHead->fUsed = 1; 1938 } 1939 } 1940 1941 1942 /** 1943 * Inserts an entrie chain into the given check sum chain. 1944 * 1945 * @param pSumHead The head of the checksum list. 1946 * @param pSumHeadAdd The head of the checksum list to be added. 1947 */ 1948 static void kOCSumAddChain(PKOCSUM pSumHead, PCKOCSUM pSumHeadAdd) 1949 { 1950 while (pSumHeadAdd) 1951 { 1952 kOCSumAdd(pSumHead, pSumHeadAdd); 1953 pSumHeadAdd = pSumHeadAdd->pNext; 1954 } 1955 } 1956 1957 1958 1959 /** 1960 * Prints the checksum to the specified stream. 1961 * 1962 * @param pSum The checksum. 1963 * @param pFile The output file stream 1964 */ 1965 static void kOCSumFPrintf(PCKOCSUM pSum, FILE *pFile) 1966 { 1967 fprintf(pFile, "%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 1968 pSum->crc32, 1969 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3], 1970 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7], 1971 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11], 1972 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]); 1973 } 1974 1975 1976 /** 1977 * Displays the checksum (not chain!) using the InfoMsg() method. 1978 * 1979 * @param pSum The checksum. 1980 * @param uLevel The info message level. 1981 * @param pszMsg Message to prefix the info message with. 1982 */ 1983 static void kOCSumInfo(PCKOCSUM pSum, unsigned uLevel, const char *pszMsg) 1984 { 1985 InfoMsg(uLevel, 1986 "%s: crc32=%#010x md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 1987 pszMsg, 1988 pSum->crc32, 1989 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3], 1990 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7], 1991 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11], 1992 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]); 1993 } 1994 1995 1996 /** 1997 * Compares two check sum entries. 1998 * 1999 * @returns 1 if equal, 0 if not equal. 2000 * 2001 * @param pSum1 The first checksum. 2002 * @param pSum2 The second checksum. 2003 */ 2004 static int kOCSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2) 2005 { 2006 if (pSum1 == pSum2) 2007 return 1; 2008 if (!pSum1 || !pSum2) 2009 return 0; 2010 if (pSum1->crc32 != pSum2->crc32) 2011 return 0; 2012 if (memcmp(&pSum1->md5[0], &pSum2->md5[0], sizeof(pSum1->md5))) 2013 return 0; 2014 return 1; 2015 } 2016 2017 2018 /** 2019 * Checks if the specified checksum equals one of the 2020 * checksums in the chain. 2021 * 2022 * @returns 1 if equals one of them, 0 if not. 2023 * 2024 * @param pSumHead The checksum chain too look in. 2025 * @param pSum The checksum to look for. 2026 * @todo ugly name. fix. 2027 */ 2028 static int kOCSumHasEqualInChain(PCKOCSUM pSumHead, PCKOCSUM pSum) 2029 { 2030 for (; pSumHead; pSumHead = pSumHead->pNext) 2031 { 2032 if (pSumHead == pSum) 2033 return 1; 2034 if (pSumHead->crc32 != pSum->crc32) 2035 continue; 2036 if (memcmp(&pSumHead->md5[0], &pSum->md5[0], sizeof(pSumHead->md5))) 2037 continue; 2038 return 1; 2039 } 2040 return 0; 2041 } 2042 2043 2044 /** 2045 * Checks if the checksum (chain) empty. 2046 * 2047 * @returns 1 if empty, 0 if it there is one or more checksums. 2048 * @param pSum The checksum to test. 2049 */ 2050 static int kOCSumIsEmpty(PCKOCSUM pSum) 2051 { 2052 return !pSum->fUsed; 2053 } 2054 2055 2056 2057 2058 2059 2060 /** 2061 * The representation of a cache entry. 2062 */ 2063 typedef struct KOCENTRY 2064 { 2065 /** The name of the cache entry. */ 2066 const char *pszName; 2067 /** The dir that all other names are relative to. */ 2068 char *pszDir; 2069 /** The absolute path. */ 2070 char *pszAbsPath; 2071 /** Set if the object needs to be (re)compiled. */ 2072 unsigned fNeedCompiling; 2073 /** Whether the preprocessor runs in piped mode. If clear it's file 2074 * mode (it could be redirected stdout, but that's essentially the 2075 * same from our point of view). */ 2076 unsigned fPipedPreComp; 2077 /** Whether the compiler runs in piped mode (preprocessor output on stdin). */ 2078 unsigned fPipedCompile; 2079 /** The name of the pipe that we're feeding the preprocessed output to the 2080 * compiler via. This is a Windows thing. */ 2081 char *pszNmPipeCompile; 2082 /** Name of the dependency file (generated from #line statements in the 2083 * preprocessor output). */ 2084 char *pszMakeDepFilename; 2085 /** Whether to fix the case of the make depedencies. */ 2086 int fMakeDepFixCase; 2087 /** Whether to do the make dependencies quietly. */ 2088 int fMakeDepQuiet; 2089 /** Whether to generate stubs for headers files. */ 2090 int fMakeDepGenStubs; 2091 /** The dependency collector state. */ 2092 KOCDEP DepState; 2093 /** Whether the optimizations are enabled. */ 2094 int fOptimizeCpp; 2095 /** Cache entry key that's used for some quick digest validation. */ 2096 uint32_t uKey; 2097 2098 /** The file data. */ 2099 struct KOCENTRYDATA 2100 { 2101 /** The name of file containing the preprocessor output. */ 2102 char *pszCppName; 2103 /** Pointer to the preprocessor output. */ 2104 char *pszCppMapping; 2105 /** The size of the preprocessor output. 0 if not determined. */ 2106 size_t cbCpp; 2107 /** The preprocessor output checksums that will produce the cached object. */ 2108 KOCSUM SumHead; 2109 /** The number of milliseconds spent precompiling. */ 2110 uint32_t cMsCpp; 2111 2112 /** The object filename (relative to the cache file). */ 2113 char *pszObjName; 2114 /** The compile argument vector used to build the object. */ 2115 char **papszArgvCompile; 2116 /** The size of the compile */ 2117 unsigned cArgvCompile; 2118 /** The checksum of the compiler argument vector. */ 2119 KOCSUM SumCompArgv; 2120 /** The number of milliseconds spent compiling. */ 2121 uint32_t cMsCompile; 2122 /** @todo need a list of additional output files for MSC. */ 2123 /** @todo need compiler output (warnings). */ 2124 2125 /** The target os/arch identifier. */ 2126 char *pszTarget; 2127 } 2128 /** The old data.*/ 2129 Old, 2130 /** The new data. */ 2131 New; 2132 } KOCENTRY; 2133 /** Pointer to a KOCENTRY. */ 2134 typedef KOCENTRY *PKOCENTRY; 2135 /** Pointer to a const KOCENTRY. */ 2136 typedef const KOCENTRY *PCKOCENTRY; 2137 2138 2139 /** 2140 * Creates a cache entry for the given cache file name. 2141 * 2142 * @returns Pointer to a cache entry. 2143 * @param pszFilename The cache file name. 2144 */ 2145 static PKOCENTRY kOCEntryCreate(const char *pszFilename) 2146 { 2147 PKOCENTRY pEntry; 2148 size_t off; 2149 2150 /* 2151 * Allocate an empty entry. 2152 */ 2153 pEntry = xmallocz(sizeof(*pEntry)); 2154 2155 kOCDepInit(&pEntry->DepState); 2156 2157 kOCSumInit(&pEntry->New.SumHead); 2158 kOCSumInit(&pEntry->Old.SumHead); 2159 2160 kOCSumInit(&pEntry->New.SumCompArgv); 2161 kOCSumInit(&pEntry->Old.SumCompArgv); 2162 2163 /* 2164 * Setup the directory and cache file name. 2165 */ 2166 pEntry->pszAbsPath = AbsPath(pszFilename); 2167 pEntry->pszName = FindFilenameInPath(pEntry->pszAbsPath); 2168 off = pEntry->pszName - pEntry->pszAbsPath; 2169 if (!off) 2170 FatalDie("Failed to find abs path for '%s'!\n", pszFilename); 2171 pEntry->pszDir = xmalloc(off); 2172 memcpy(pEntry->pszDir, pEntry->pszAbsPath, off - 1); 2173 pEntry->pszDir[off - 1] = '\0'; 2174 2175 return pEntry; 2176 } 2177 2178 2179 /** 2180 * Destroys the cache entry freeing up all it's resources. 2181 * 2182 * @param pEntry The entry to free. 2183 */ 2184 static void kOCEntryDestroy(PKOCENTRY pEntry) 2185 { 2186 /** @todo free pEntry->pszName? */ 2187 free(pEntry->pszDir); 2188 free(pEntry->pszAbsPath); 2189 free(pEntry->pszNmPipeCompile); 2190 free(pEntry->pszMakeDepFilename); 2191 2192 kOCDepDelete(&pEntry->DepState); 2193 2194 kOCSumDeleteChain(&pEntry->New.SumHead); 2195 kOCSumDeleteChain(&pEntry->Old.SumHead); 2196 2197 kOCSumDeleteChain(&pEntry->New.SumCompArgv); 2198 kOCSumDeleteChain(&pEntry->Old.SumCompArgv); 2199 2200 free(pEntry->New.pszCppName); 2201 free(pEntry->Old.pszCppName); 2202 2203 free(pEntry->New.pszCppMapping); 2204 free(pEntry->Old.pszCppMapping); 2205 2206 free(pEntry->New.pszObjName); 2207 free(pEntry->Old.pszObjName); 2208 2209 free(pEntry->New.pszTarget); 2210 free(pEntry->Old.pszTarget); 2211 2212 while (pEntry->New.cArgvCompile > 0) 2213 free(pEntry->New.papszArgvCompile[--pEntry->New.cArgvCompile]); 2214 while (pEntry->Old.cArgvCompile > 0) 2215 free(pEntry->Old.papszArgvCompile[--pEntry->Old.cArgvCompile]); 2216 2217 free(pEntry->New.papszArgvCompile); 2218 free(pEntry->Old.papszArgvCompile); 2219 2220 free(pEntry); 2221 } 2222 2223 2224 /** 2225 * Calculates the checksum of an compiler argument vector. 2226 * 2227 * @param pEntry The cache entry. 2228 * @param papszArgv The argument vector. 2229 * @param cArgc The number of entries in the vector. 2230 * @param pszIgnorePath1 Path to ignore when encountered at the end of 2231 * arguments. (Not quite safe for simple file names, 2232 * but what the heck.) 2233 * @param pszIgnorePath2 Path to ignore when encountered at the end of 2234 * arguments. (Not quite safe for simple file names, 2235 * but what the heck.) 2236 * @param pSum Where to store the check sum. 2237 */ 2238 static void kOCEntryCalcArgvSum(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgc, 2239 const char *pszIgnorePath1, const char *pszIgnorePath2, PKOCSUM pSum) 2240 { 2241 size_t cchIgnorePath1 = strlen(pszIgnorePath1); 2242 size_t cchIgnorePath2 = pszIgnorePath2 ? strlen(pszIgnorePath2) : ~(size_t)0; 2243 KOCSUMCTX Ctx; 2244 unsigned i; 2245 2246 kOCSumInitWithCtx(pSum, &Ctx); 2247 for (i = 0; i < cArgc; i++) 2248 { 2249 size_t cch = strlen(papszArgv[i]); 2250 if ( ( cch < cchIgnorePath1 2251 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath1, pszIgnorePath1, cch)) 2252 && ( cch < cchIgnorePath2 2253 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath2, pszIgnorePath2, cch)) ) 2254 kOCSumUpdate(pSum, &Ctx, papszArgv[i], cch + 1); 2255 } 2256 kOCSumFinalize(pSum, &Ctx); 2257 2258 (void)pEntry; 2259 } 2260 2261 2262 /** 2263 * Reads and parses the cache file. 2264 * 2265 * @param pEntry The entry to read it into. 2266 */ 2267 static void kOCEntryRead(PKOCENTRY pEntry) 2268 { 2269 FILE *pFile; 2270 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb"); 2271 if (pFile) 2272 { 2273 InfoMsg(4, "reading cache entry...\n"); 2274 2275 /* 2276 * Check the magic. 2277 */ 2278 if ( !fgets(g_szLine, sizeof(g_szLine), pFile) 2279 || ( strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n") 2280 && strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.1\n")) 2281 ) 2282 { 2283 InfoMsg(2, "bad cache file (magic)\n"); 2284 pEntry->fNeedCompiling = 1; 2285 } 2286 else 2287 { 2288 /* 2289 * Parse the rest of the file (relaxed order). 2290 */ 2291 unsigned i; 2292 int fBad = 0; 2293 int fBadBeforeMissing = 1; 2294 while (fgets(g_szLine, sizeof(g_szLine), pFile)) 2295 { 2296 char *pszNl; 2297 char *pszVal; 2298 2299 /* Split the line and drop the trailing newline. */ 2300 pszVal = strchr(g_szLine, '='); 2301 if ((fBad = pszVal == NULL)) 2302 break; 2303 *pszVal++ = '\0'; 2304 2305 pszNl = strchr(pszVal, '\n'); 2306 if (pszNl) 2307 *pszNl = '\0'; 2308 2309 /* string case on variable name */ 2310 if (!strcmp(g_szLine, "obj")) 2311 { 2312 if ((fBad = pEntry->Old.pszObjName != NULL)) 2313 break; 2314 pEntry->Old.pszObjName = xstrdup(pszVal); 2315 } 2316 else if (!strcmp(g_szLine, "cpp")) 2317 { 2318 if ((fBad = pEntry->Old.pszCppName != NULL)) 2319 break; 2320 pEntry->Old.pszCppName = xstrdup(pszVal); 2321 } 2322 else if (!strcmp(g_szLine, "cpp-size")) 2323 { 2324 char *pszNext; 2325 if ((fBad = pEntry->Old.cbCpp != 0)) 2326 break; 2327 pEntry->Old.cbCpp = strtoul(pszVal, &pszNext, 0); 2328 if ((fBad = pszNext && *pszNext)) 2329 break; 2330 } 2331 else if (!strcmp(g_szLine, "cpp-sum")) 2332 { 2333 KOCSUM Sum; 2334 if ((fBad = kOCSumInitFromString(&Sum, pszVal))) 2335 break; 2336 kOCSumAdd(&pEntry->Old.SumHead, &Sum); 2337 } 2338 else if (!strcmp(g_szLine, "cpp-ms")) 2339 { 2340 char *pszNext; 2341 if ((fBad = pEntry->Old.cMsCpp != 0)) 2342 break; 2343 pEntry->Old.cMsCpp = strtoul(pszVal, &pszNext, 0); 2344 if ((fBad = pszNext && *pszNext)) 2345 break; 2346 } 2347 else if (!strcmp(g_szLine, "cc-argc")) 2348 { 2349 if ((fBad = pEntry->Old.papszArgvCompile != NULL)) 2350 break; 2351 pEntry->Old.cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */ 2352 pEntry->Old.papszArgvCompile = xmallocz((pEntry->Old.cArgvCompile + 1) * sizeof(pEntry->Old.papszArgvCompile[0])); 2353 } 2354 else if (!strncmp(g_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1)) 2355 { 2356 char *pszNext; 2357 unsigned i = strtoul(&g_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0); 2358 if ((fBad = i >= pEntry->Old.cArgvCompile || pEntry->Old.papszArgvCompile[i] || (pszNext && *pszNext))) 2359 break; 2360 pEntry->Old.papszArgvCompile[i] = xstrdup(pszVal); 2361 } 2362 else if (!strcmp(g_szLine, "cc-argv-sum")) 2363 { 2364 if ((fBad = !kOCSumIsEmpty(&pEntry->Old.SumCompArgv))) 2365 break; 2366 if ((fBad = kOCSumInitFromString(&pEntry->Old.SumCompArgv, pszVal))) 2367 break; 2368 } 2369 else if (!strcmp(g_szLine, "cc-ms")) 2370 { 2371 char *pszNext; 2372 if ((fBad = pEntry->Old.cMsCompile != 0)) 2373 break; 2374 pEntry->Old.cMsCompile = strtoul(pszVal, &pszNext, 0); 2375 if ((fBad = pszNext && *pszNext)) 2376 break; 2377 } 2378 else if (!strcmp(g_szLine, "target")) 2379 { 2380 if ((fBad = pEntry->Old.pszTarget != NULL)) 2381 break; 2382 pEntry->Old.pszTarget = xstrdup(pszVal); 2383 } 2384 else if (!strcmp(g_szLine, "key")) 2385 { 2386 char *pszNext; 2387 if ((fBad = pEntry->uKey != 0)) 2388 break; 2389 pEntry->uKey = strtoul(pszVal, &pszNext, 0); 2390 if ((fBad = pszNext && *pszNext)) 2391 break; 2392 } 2393 else if (!strcmp(g_szLine, "the-end")) 2394 { 2395 fBadBeforeMissing = fBad = strcmp(pszVal, "fine"); 2396 break; 2397 } 2398 else 2399 { 2400 fBad = 1; 2401 break; 2402 } 2403 } /* parse loop */ 2404 2405 /* 2406 * Did we find everything and does it add up correctly? 2407 */ 2408 if (!fBad && fBadBeforeMissing) 2409 { 2410 InfoMsg(2, "bad cache file (no end)\n"); 2411 fBad = 1; 2412 } 2413 else 2414 { 2415 fBadBeforeMissing = fBad; 2416 if ( !fBad 2417 && ( !pEntry->Old.papszArgvCompile 2418 || !pEntry->Old.pszObjName 2419 || !pEntry->Old.pszCppName 2420 || kOCSumIsEmpty(&pEntry->Old.SumHead))) 2421 fBad = 1; 2422 if (!fBad) 2423 for (i = 0; i < pEntry->Old.cArgvCompile; i++) 2424 if ((fBad = !pEntry->Old.papszArgvCompile[i])) 2425 break; 2426 if (!fBad) 2427 { 2428 KOCSUM Sum; 2429 kOCEntryCalcArgvSum(pEntry, (const char * const *)pEntry->Old.papszArgvCompile, 2430 pEntry->Old.cArgvCompile, pEntry->Old.pszObjName, pEntry->Old.pszCppName, 2431 &Sum); 2432 fBad = !kOCSumIsEqual(&pEntry->Old.SumCompArgv, &Sum); 2433 } 2434 if (fBad) 2435 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff"); 2436 else if (ferror(pFile)) 2437 { 2438 InfoMsg(2, "cache file read error\n"); 2439 fBad = 1; 2440 } 2441 2442 /* 2443 * Verify the existance of the object file. 2444 */ 2445 if (!fBad) 2446 { 2447 struct stat st; 2448 char *pszPath = MakePathFromDirAndFile(pEntry->Old.pszObjName, pEntry->pszDir); 2449 if (stat(pszPath, &st) != 0) 2450 { 2451 InfoMsg(2, "failed to stat object file: %s\n", strerror(errno)); 2452 fBad = 1; 2453 } 2454 else 2455 { 2456 /** @todo verify size and the timestamp. */ 2457 } 2458 } 2459 } 2460 pEntry->fNeedCompiling = fBad; 2461 } 2462 fclose(pFile); 2463 } 2464 else 2465 { 2466 InfoMsg(2, "no cache file\n"); 2467 pEntry->fNeedCompiling = 1; 2468 } 2469 } 2470 2471 2472 /** 2473 * Writes the cache file. 2474 * 2475 * @param pEntry The entry to write. 2476 */ 2477 static void kOCEntryWrite(PKOCENTRY pEntry) 2478 { 2479 FILE *pFile; 2480 PCKOCSUM pSum; 2481 unsigned i; 2482 2483 InfoMsg(4, "writing cache entry '%s'...\n", pEntry->pszName); 2484 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb"); 2485 if (!pFile) 2486 FatalDie("Failed to open '%s' in '%s': %s\n", 2487 pEntry->pszName, pEntry->pszDir, strerror(errno)); 2488 2489 #define CHECK_LEN(expr) \ 2490 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) 2491 2492 fprintf(pFile, "magic=kObjCacheEntry-v0.1.1\n"); 2493 CHECK_LEN(fprintf(pFile, "target=%s\n", pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget)); 2494 CHECK_LEN(fprintf(pFile, "key=%lu\n", (unsigned long)pEntry->uKey)); 2495 CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName)); 2496 CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName)); 2497 CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->New.pszCppName ? pEntry->New.cbCpp : pEntry->Old.cbCpp)); 2498 CHECK_LEN(fprintf(pFile, "cpp-ms=%lu\n", pEntry->New.pszCppName ? pEntry->New.cMsCpp : pEntry->Old.cMsCpp)); 2499 CHECK_LEN(fprintf(pFile, "cc-ms=%lu\n", pEntry->New.pszCppName ? pEntry->New.cMsCompile : pEntry->Old.cMsCompile)); 2500 2501 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv)) 2502 { 2503 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->New.cArgvCompile)); 2504 for (i = 0; i < pEntry->New.cArgvCompile; i++) 2505 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->New.papszArgvCompile[i])); 2506 fprintf(pFile, "cc-argv-sum="); 2507 kOCSumFPrintf(&pEntry->New.SumCompArgv, pFile); 2508 } 2509 else 2510 { 2511 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->Old.cArgvCompile)); 2512 for (i = 0; i < pEntry->Old.cArgvCompile; i++) 2513 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->Old.papszArgvCompile[i])); 2514 fprintf(pFile, "cc-argv-sum="); 2515 kOCSumFPrintf(&pEntry->Old.SumCompArgv, pFile); 2516 } 2517 2518 2519 for (pSum = !kOCSumIsEmpty(&pEntry->New.SumHead) ? &pEntry->New.SumHead : &pEntry->Old.SumHead; 2520 pSum; 2521 pSum = pSum->pNext) 2522 { 2523 fprintf(pFile, "cpp-sum="); 2524 kOCSumFPrintf(pSum, pFile); 2525 } 2526 2527 fprintf(pFile, "the-end=fine\n"); 2528 2529 #undef CHECK_LEN 2530 2531 /* 2532 * Flush the file and check for errors. 2533 * On failure delete the file so we won't be seeing any invalid 2534 * files the next time or upset make with new timestamps. 2535 */ 2536 errno = 0; 2537 if ( fflush(pFile) < 0 2538 || ferror(pFile)) 2539 { 2540 int iErr = errno; 2541 fclose(pFile); 2542 UnlinkFileInDir(pEntry->pszName, pEntry->pszDir); 2543 FatalDie("Stream error occured while writing '%s' in '%s': %s\n", 2544 pEntry->pszName, pEntry->pszDir, strerror(iErr)); 2545 } 2546 fclose(pFile); 2547 } 2548 2549 2550 /** 2551 * Checks that the read cache entry is valid. 2552 * It sets fNeedCompiling if it isn't. 2553 * 2554 * @returns 1 valid, 0 invalid. 2555 * @param pEntry The cache entry. 2556 */ 2557 static int kOCEntryCheck(PKOCENTRY pEntry) 2558 { 2559 return !pEntry->fNeedCompiling; 2560 } 2561 2562 2563 /** 2564 * Sets the object name and compares it with the old name if present. 2565 * 2566 * @param pEntry The cache entry. 2567 * @param pszObjName The new object name. 2568 */ 2569 static void kOCEntrySetCompileObjName(PKOCENTRY pEntry, const char *pszObjName) 2570 { 2571 assert(!pEntry->New.pszObjName); 2572 pEntry->New.pszObjName = CalcRelativeName(pszObjName, pEntry->pszDir); 2573 2574 if ( !pEntry->fNeedCompiling 2575 && ( !pEntry->Old.pszObjName 2576 || strcmp(pEntry->New.pszObjName, pEntry->Old.pszObjName))) 2577 { 2578 InfoMsg(2, "object file name differs\n"); 2579 pEntry->fNeedCompiling = 1; 2580 } 2581 2582 if ( !pEntry->fNeedCompiling 2583 && !DoesFileInDirExist(pEntry->New.pszObjName, pEntry->pszDir)) 2584 { 2585 InfoMsg(2, "object file doesn't exist\n"); 2586 pEntry->fNeedCompiling = 1; 2587 } 2588 } 2589 2590 2591 /** 2592 * Set the new compiler args, calc their checksum, and comparing them with any old ones. 2593 * 2594 * @param pEntry The cache entry. 2595 * @param papszArgvCompile The new argument vector for compilation. 2596 * @param cArgvCompile The number of arguments in the vector. 2597 * 2598 * @remark Must call kOCEntrySetCompileObjName before this function! 2599 */ 2600 static void kOCEntrySetCompileArgv(PKOCENTRY pEntry, const char * const *papszArgvCompile, unsigned cArgvCompile) 2601 { 2602 unsigned i; 2603 2604 /* call me only once! */ 2605 assert(!pEntry->New.cArgvCompile); 2606 /* call kOCEntrySetCompilerObjName first! */ 2607 assert(pEntry->New.pszObjName); 2608 2609 /* 2610 * Copy the argument vector and calculate the checksum. 2611 */ 2612 pEntry->New.cArgvCompile = cArgvCompile; 2613 pEntry->New.papszArgvCompile = xmalloc((cArgvCompile + 1) * sizeof(pEntry->New.papszArgvCompile[0])); 2614 for (i = 0; i < cArgvCompile; i++) 2615 pEntry->New.papszArgvCompile[i] = xstrdup(papszArgvCompile[i]); 2616 pEntry->New.papszArgvCompile[i] = NULL; /* for exev/spawnv */ 2617 2618 kOCEntryCalcArgvSum(pEntry, papszArgvCompile, cArgvCompile, pEntry->New.pszObjName, pEntry->New.pszCppName, 2619 &pEntry->New.SumCompArgv); 2620 kOCSumInfo(&pEntry->New.SumCompArgv, 4, "comp-argv"); 2621 2622 /* 2623 * Compare with the old argument vector. 2624 */ 2625 if ( !pEntry->fNeedCompiling 2626 && !kOCSumIsEqual(&pEntry->New.SumCompArgv, &pEntry->Old.SumCompArgv)) 2627 { 2628 InfoMsg(2, "compiler args differs\n"); 2629 pEntry->fNeedCompiling = 1; 2630 } 2631 } 2632 2633 2634 /** 2635 * Sets the arch/os target and compares it with the old name if present. 2636 * 2637 * @param pEntry The cache entry. 2638 * @param pszObjName The new object name. 2639 */ 2640 static void kOCEntrySetTarget(PKOCENTRY pEntry, const char *pszTarget) 2641 { 2642 assert(!pEntry->New.pszTarget); 2643 pEntry->New.pszTarget = xstrdup(pszTarget); 2644 2645 if ( !pEntry->fNeedCompiling 2646 && ( !pEntry->Old.pszTarget 2647 || strcmp(pEntry->New.pszTarget, pEntry->Old.pszTarget))) 2648 { 2649 InfoMsg(2, "target differs\n"); 2650 pEntry->fNeedCompiling = 1; 2651 } 2652 } 2653 2654 2655 /** 2656 * Sets the preprocessor output filename. We don't generally care if this 2657 * matches the old name or not. 2658 * 2659 * @param pEntry The cache entry. 2660 * @param pszCppName The preprocessor output filename. 2661 */ 2662 static void kOCEntrySetCppName(PKOCENTRY pEntry, const char *pszCppName) 2663 { 2664 assert(!pEntry->New.pszCppName); 2665 pEntry->New.pszCppName = CalcRelativeName(pszCppName, pEntry->pszDir); 2666 } 2667 2668 2669 /** 2670 * Sets the piped mode of the preprocessor and compiler. 2671 * 2672 * @param pEntry The cache entry. 2673 * @param fRedirPreCompStdOut Whether the preprocessor is in piped mode. 2674 * @param fRedirCompileStdIn Whether the compiler is in piped mode. 2675 * @param pszNmPipeCompile The name of the named pipe to use to feed 2676 * the microsoft compiler. 2677 */ 2678 static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn, 2679 const char *pszNmPipeCompile) 2680 { 2681 pEntry->fPipedPreComp = fRedirPreCompStdOut; 2682 pEntry->fPipedCompile = fRedirCompileStdIn || pszNmPipeCompile; 2683 pEntry->pszNmPipeCompile = xstrdup(pszNmPipeCompile); 2684 } 2685 2686 2687 /** 2688 * Sets the dependency file. 2689 * 2690 * @param pEntry The cache entry. 2691 * @param pszMakeDepFilename The dependency filename. 2692 * @param fMakeDepFixCase Whether to fix the case of dependency files. 2693 * @param fMakeDepQuiet Whether to be quiet about the dependencies. 2694 * @param fMakeDepGenStubs Whether to generate stubs. 2695 */ 2696 static void kOCEntrySetDepFilename(PKOCENTRY pEntry, const char *pszMakeDepFilename, 2697 int fMakeDepFixCase, int fMakeDepQuiet, int fMakeDepGenStubs) 2698 { 2699 pEntry->pszMakeDepFilename = xstrdup(pszMakeDepFilename); 2700 pEntry->fMakeDepFixCase = fMakeDepFixCase; 2701 pEntry->fMakeDepQuiet = fMakeDepQuiet; 2702 pEntry->fMakeDepGenStubs = fMakeDepGenStubs; 2703 } 2704 2705 2706 /** 2707 * Configures the preprocessor output optimizations. 2708 * 2709 * @param pEntry The cache entry. 2710 * @param fOptimizeCpp The one and only flag, so far. 2711 */ 2712 static void kOCEntrySetOptimizations(PKOCENTRY pEntry, int fOptimizeCpp) 2713 { 2714 pEntry->fOptimizeCpp = fOptimizeCpp; 2715 } 2716 2717 2718 /** 2719 * Spawns a child in a synchronous fashion. 2720 * Terminating on failure. 2721 * 2722 * @param papszArgv Argument vector. The cArgv element is NULL. 2723 * @param pcMs The cache entry member use for time keeping. This 2724 * will be set to the current timestamp. 2725 * @param cArgv The number of arguments in the vector. 2726 * @param pszMsg Which operation this is, for use in messages. 2727 * @param pszStdOut Where to redirect standard out. 2728 */ 2729 static void kOCEntrySpawn(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv, 2730 const char *pszMsg, const char *pszStdOut) 2731 { 2732 #if defined(__OS2__) || defined(__WIN__) 2733 intptr_t rc; 2734 int fdStdOut = -1; 2735 if (pszStdOut) 2736 { 2737 int fdReDir; 2738 fdStdOut = dup(STDOUT_FILENO); 2739 close(STDOUT_FILENO); 2740 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666); 2741 if (fdReDir < 0) 2742 FatalDie("%s - failed to create stdout redirection file '%s': %s\n", 2743 pszMsg, pszStdOut, strerror(errno)); 2744 2745 if (fdReDir != STDOUT_FILENO) 2746 { 2747 if (dup2(fdReDir, STDOUT_FILENO) < 0) 2748 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno)); 2749 close(fdReDir); 2750 } 2751 } 2752 2753 *pcMs = NowMs(); 2754 errno = 0; 2755 # ifdef __WIN__ 2756 rc = quoted_spawnvp(_P_WAIT, papszArgv[0], papszArgv); 2757 # else 2758 rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv); 2759 # endif 2760 *pcMs = NowMs() - *pcMs; 2761 if (rc < 0) 2762 FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno)); 2763 if (rc > 0) 2764 FatalDie("%s - failed rc=%d\n", pszMsg, (int)rc); 2765 if (fdStdOut != -1) 2766 { 2767 close(STDOUT_FILENO); 2768 fdStdOut = dup2(fdStdOut, STDOUT_FILENO); 2769 close(fdStdOut); 2770 } 2771 2772 #else 2773 int iStatus; 2774 pid_t pidWait; 2775 pid_t pid; 2776 2777 *pcMs = NowMs(); 2778 pid = fork(); 2779 if (!pid) 2780 { 2781 if (pszStdOut) 2782 { 2783 int fdReDir; 2784 2785 close(STDOUT_FILENO); 2786 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666); 2787 if (fdReDir < 0) 2788 FatalDie("%s - failed to create stdout redirection file '%s': %s\n", 2789 pszMsg, pszStdOut, strerror(errno)); 2790 if (fdReDir != STDOUT_FILENO) 2791 { 2792 if (dup2(fdReDir, STDOUT_FILENO) < 0) 2793 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno)); 2794 close(fdReDir); 2795 } 2796 } 2797 2798 execvp(papszArgv[0], (char **)papszArgv); 2799 FatalDie("%s - execvp failed: %s\n", 2800 pszMsg, strerror(errno)); 2801 } 2802 if (pid == -1) 2803 FatalDie("%s - fork() failed: %s\n", pszMsg, strerror(errno)); 2804 2805 pidWait = waitpid(pid, &iStatus, 0); 2806 while (pidWait < 0 && errno == EINTR) 2807 pidWait = waitpid(pid, &iStatus, 0); 2808 *pcMs = NowMs() - *pcMs; 2809 if (pidWait != pid) 2810 FatalDie("%s - waitpid failed rc=%d: %s\n", 2811 pszMsg, pidWait, strerror(errno)); 2812 if (!WIFEXITED(iStatus)) 2813 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus); 2814 if (WEXITSTATUS(iStatus)) 2815 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus)); 2816 #endif 2817 2818 (void)pEntry; (void)cArgv; 2819 } 2820 2821 2822 /** 2823 * Spawns child with optional redirection of stdin and stdout. 2824 * 2825 * @param pEntry The cache entry. 2826 * @param pcMs The cache entry member use for time keeping. This 2827 * will be set to the current timestamp. 2828 * @param papszArgv Argument vector. The cArgv element is NULL. 2829 * @param cArgv The number of arguments in the vector. 2830 * @param fdStdIn Child stdin, -1 if it should inherit our stdin. Will be closed. 2831 * @param fdStdOut Child stdout, -1 if it should inherit our stdout. Will be closed. 2832 * @param pszMsg Message to start the info/error messages with. 2833 */ 2834 static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv, 2835 int fdStdIn, int fdStdOut, const char *pszMsg) 2836 { 2837 pid_t pid; 2838 int fdSavedStdOut = -1; 2839 int fdSavedStdIn = -1; 2840 2841 /* 2842 * Setup redirection. 2843 */ 2844 if (fdStdOut != -1 && fdStdOut != STDOUT_FILENO) 2845 { 2846 fdSavedStdOut = dup(STDOUT_FILENO); 2847 if (dup2(fdStdOut, STDOUT_FILENO) < 0) 2848 FatalDie("%s - dup2(,1) failed: %s\n", pszMsg, strerror(errno)); 2849 close(fdStdOut); 2850 #ifndef __WIN__ 2851 fcntl(fdSavedStdOut, F_SETFD, FD_CLOEXEC); 2852 #endif 2853 } 2854 if (fdStdIn != -1 && fdStdIn != STDIN_FILENO) 2855 { 2856 fdSavedStdIn = dup(STDIN_FILENO); 2857 if (dup2(fdStdIn, STDIN_FILENO) < 0) 2858 FatalDie("%s - dup2(,0) failed: %s\n", pszMsg, strerror(errno)); 2859 close(fdStdIn); 2860 #ifndef __WIN__ 2861 fcntl(fdSavedStdIn, F_SETFD, FD_CLOEXEC); 2862 #endif 2863 } 2864 2865 /* 2866 * Create the child process. 2867 */ 2868 *pcMs = NowMs(); 2869 #if defined(__OS2__) || defined(__WIN__) 2870 errno = 0; 2871 # ifdef __WIN__ 2872 pid = quoted_spawnvp(_P_NOWAIT, papszArgv[0], papszArgv); 2873 # else 2874 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv); 2875 # endif 2876 if (pid == -1) 2877 FatalDie("preprocess - _spawnvp failed: %s\n", strerror(errno)); 2878 2879 #else 2880 pid = fork(); 2881 if (!pid) 2882 { 2883 execvp(papszArgv[0], (char **)papszArgv); 2884 FatalDie("preprocess - execvp failed: %s\n", strerror(errno)); 2885 } 2886 if (pid == -1) 2887 FatalDie("preprocess - fork() failed: %s\n", strerror(errno)); 2888 #endif 2889 2890 /* 2891 * Restore stdout & stdin. 2892 */ 2893 if (fdSavedStdIn != -1) 2894 { 2895 close(STDIN_FILENO); 2896 dup2(fdStdOut, STDIN_FILENO); 2897 close(fdSavedStdIn); 2898 } 2899 if (fdSavedStdOut != -1) 2900 { 2901 close(STDOUT_FILENO); 2902 dup2(fdSavedStdOut, STDOUT_FILENO); 2903 close(fdSavedStdOut); 2904 } 2905 2906 InfoMsg(3, "%s - spawned %ld\n", pszMsg, (long)pid); 2907 (void)cArgv; 2908 (void)pEntry; 2909 return pid; 2910 } 2911 2912 2913 /** 2914 * Waits for a child and exits fatally if the child failed in any way. 2915 * 2916 * @param pEntry The cache entry. 2917 * @param pcMs The millisecond timestamp that should be convert to 2918 * elapsed time. 2919 * @param pid The child to wait for. 2920 * @param pszMsg Message to start the info/error messages with. 2921 */ 2922 static void kOCEntryWaitChild(PCKOCENTRY pEntry, uint32_t *pcMs, pid_t pid, const char *pszMsg) 2923 { 2924 int iStatus = -1; 2925 pid_t pidWait; 2926 InfoMsg(3, "%s - wait-child %ld\n", pszMsg, (long)pid); 2927 2928 #ifdef __WIN__ 2929 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD); 2930 *pcMs = NowMs() - *pcMs; 2931 if (pidWait == -1) 2932 FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno)); 2933 if (iStatus) 2934 FatalDie("%s - failed with rc %d\n", pszMsg, iStatus); 2935 #else 2936 pidWait = waitpid(pid, &iStatus, 0); 2937 while (pidWait < 0 && errno == EINTR) 2938 pidWait = waitpid(pid, &iStatus, 0); 2939 *pcMs = NowMs() - *pcMs; 2940 if (pidWait != pid) 2941 FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno)); 2942 if (!WIFEXITED(iStatus)) 2943 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus); 2944 if (WEXITSTATUS(iStatus)) 2945 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus)); 2946 #endif 2947 (void)pEntry; 2948 } 2949 2950 2951 /** 2952 * Creates a pipe for setting up redirected stdin/stdout. 2953 * 2954 * @param pEntry The cache entry. 2955 * @param paFDs Where to store the two file descriptors. 2956 * @param pszMsg The operation message for info/error messages. 2957 * @param pszPipeName The pipe name if it is supposed to be named. (Windows only.) 2958 * @param fText Whether to read text mode or binary mode. 2959 */ 2960 static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *paFDs, const char *pszPipeName, const char *pszMsg, int fText) 2961 { 2962 paFDs[0] = paFDs[1] = -1; 2963 #if defined(__WIN__) 2964 if (pszPipeName) 2965 { 2966 HANDLE hPipe = CreateNamedPipeA(pszPipeName, 2967 /*PIPE_ACCESS_OUTBOUND*/ PIPE_ACCESS_DUPLEX, 2968 PIPE_READMODE_BYTE | PIPE_WAIT, 2969 10 /* nMaxInstances */, 2970 0x10000 /* nOutBuffer */, 2971 0x10000 /* nInBuffer */, 2972 NMPWAIT_WAIT_FOREVER, 2973 NULL /* pSecurityAttributes */); 2974 2975 if (hPipe == INVALID_HANDLE_VALUE) 2976 FatalDie("%s - CreateNamedPipe(%s) failed: %d\n", pszMsg, pszPipeName, GetLastError()); 2977 2978 paFDs[1 /* write */] = _open_osfhandle((intptr_t)hPipe, _O_WRONLY | _O_TEXT | _O_NOINHERIT); 2979 if (paFDs[1 /* write */] == -1) 2980 FatalDie("%s - _open_osfhandle failed: %d\n", pszMsg, strerror(errno)); 2981 } 2982 else if ( _pipe(paFDs, 256*1024, _O_NOINHERIT | (fText ? _O_TEXT : _O_BINARY)) < 0 2983 && _pipe(paFDs, 0, _O_NOINHERIT | (fText ? _O_TEXT : _O_BINARY)) < 0) 2984 #else 2985 if (pipe(paFDs) < 0) 2986 #endif 2987 FatalDie("%s - pipe failed: %s\n", pszMsg, strerror(errno)); 2988 #if !defined(__WIN__) 2989 fcntl(paFDs[0], F_SETFD, FD_CLOEXEC); 2990 fcntl(paFDs[1], F_SETFD, FD_CLOEXEC); 2991 #endif 2992 2993 (void)pEntry; 2994 } 2995 2996 2997 /** 2998 * Spawns a child that produces output to stdout. 2999 * 3000 * @param papszArgv Argument vector. The cArgv element is NULL. 3001 * @param cArgv The number of arguments in the vector. 3002 * @param pszMsg The operation message for info/error messages. 3003 * @param pfnConsumer Pointer to a consumer callback function that is responsible 3004 * for servicing the child output and closing the pipe. 3005 */ 3006 static void kOCEntrySpawnProducer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg, 3007 void (*pfnConsumer)(PKOCENTRY, int)) 3008 { 3009 int fds[2]; 3010 pid_t pid; 3011 3012 kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp); 3013 pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg); 3014 3015 pfnConsumer(pEntry, fds[0 /* read */]); 3016 3017 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pid, pszMsg); 3018 } 3019 3020 3021 /** 3022 * Spawns a child that consumes input on stdin or via a named pipe. 3023 * 3024 * @param papszArgv Argument vector. The cArgv element is NULL. 3025 * @param cArgv The number of arguments in the vector. 3026 * @param pszMsg The operation message for info/error messages. 3027 * @param pfnProducer Pointer to a producer callback function that is responsible 3028 * for serving the child input and closing the pipe. 3029 */ 3030 static void kOCEntrySpawnConsumer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg, 3031 void (*pfnProducer)(PKOCENTRY, int)) 3032 { 3033 int fds[2]; 3034 pid_t pid; 3035 3036 kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/); 3037 pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg); 3038 #ifdef __WIN__ 3039 if (pEntry->pszNmPipeCompile && !ConnectNamedPipe((HANDLE)_get_osfhandle(fds[1 /* write */]), NULL)) 3040 FatalDie("compile - ConnectNamedPipe failed: %d\n", GetLastError()); 3041 #endif 3042 3043 pfnProducer(pEntry, fds[1 /* write */]); 3044 3045 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pid, pszMsg); 3046 } 3047 3048 3049 /** 3050 * Spawns two child processes, one producing output and one consuming. 3051 * Terminating on failure. 3052 * 3053 * @param papszArgv Argument vector. The cArgv element is NULL. 3054 * @param cArgv The number of arguments in the vector. 3055 * @param pszMsg The operation message for info/error messages. 3056 * @param pfnConsumer Pointer to a consumer callback function that is responsible 3057 * for servicing the child output and closing the pipe. 3058 */ 3059 static void kOCEntrySpawnTee(PKOCENTRY pEntry, const char * const *papszProdArgv, unsigned cProdArgv, 3060 const char * const *papszConsArgv, unsigned cConsArgv, 3061 const char *pszMsg, void (*pfnTeeConsumer)(PKOCENTRY, int, int)) 3062 { 3063 int fds[2]; 3064 int fdIn, fdOut; 3065 pid_t pidProducer, pidConsumer; 3066 3067 /* 3068 * The producer. 3069 */ 3070 kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp); 3071 pidConsumer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg); 3072 fdIn = fds[0 /* read */]; 3073 3074 /* 3075 * The consumer. 3076 */ 3077 kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/); 3078 pidProducer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg); 3079 fdOut = fds[1 /* write */]; 3080 3081 /* 3082 * Hand it on to the tee consumer. 3083 */ 3084 pfnTeeConsumer(pEntry, fdIn, fdOut); 3085 3086 /* 3087 * Reap the children. 3088 */ 3089 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pidProducer, pszMsg); 3090 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pidConsumer, pszMsg); 3091 } 3092 3093 3094 /** 3095 * Reads the output from the preprocessor. 3096 * 3097 * @param pEntry The cache entry. New.cbCpp and New.pszCppMapping will be updated. 3098 * @param pWhich Specifies what to read (old/new). 3099 * @param fNonFatal Whether failure is fatal or not. 3100 */ 3101 static int kOCEntryReadCppOutput(PKOCENTRY pEntry, struct KOCENTRYDATA *pWhich, int fNonFatal) 3102 { 3103 pWhich->pszCppMapping = ReadFileInDir(pWhich->pszCppName, pEntry->pszDir, &pWhich->cbCpp); 3104 if (!pWhich->pszCppMapping) 3105 { 3106 if (!fNonFatal) 3107 FatalDie("failed to open/read '%s' in '%s': %s\n", 3108 pWhich->pszCppName, pEntry->pszDir, strerror(errno)); 3109 InfoMsg(2, "failed to open/read '%s' in '%s': %s\n", 3110 pWhich->pszCppName, pEntry->pszDir, strerror(errno)); 3111 return -1; 3112 } 3113 3114 InfoMsg(3, "preprocessed file is %lu bytes long\n", (unsigned long)pWhich->cbCpp); 3115 return 0; 3116 } 3117 3118 3119 /** 3120 * Worker for kOCEntryPreProcess and calculates the checksum of 3121 * the preprocessor output. 3122 * 3123 * @param pEntry The cache entry. NewSum will be updated. 3124 */ 3125 static void kOCEntryCalcChecksum(PKOCENTRY pEntry) 3126 { 3127 KOCSUMCTX Ctx; 3128 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx); 3129 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, pEntry->New.pszCppMapping, pEntry->New.cbCpp); 3130 kOCSumFinalize(&pEntry->New.SumHead, &Ctx); 3131 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (file)"); 3132 } 3133 3134 3135 /** 3136 * This consumes the preprocessor output and checksums it. 3137 * 3138 * @param pEntry The cache entry. 3139 * @param fdIn The preprocessor output pipe. 3140 * @param fdOut The compiler input pipe, -1 if no compiler. 3141 */ 3142 static void kOCEntryPreProcessConsumer(PKOCENTRY pEntry, int fdIn) 3143 { 3144 KOCSUMCTX Ctx; 3145 KOCCPPRD CppRd; 3146 3147 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx); 3148 kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp, 3149 pEntry->pszMakeDepFilename ? &pEntry->DepState : NULL); 3150 3151 for (;;) 3152 { 3153 /* 3154 * Read data from the pipe. 3155 */ 3156 const char *psz; 3157 long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz); 3158 if (!cbRead) 3159 break; 3160 3161 /* 3162 * Process the data. 3163 */ 3164 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead); 3165 if (pEntry->pszMakeDepFilename && !pEntry->fOptimizeCpp) 3166 kOCDepConsumer(&pEntry->DepState, psz, cbRead); 3167 } 3168 3169 close(fdIn); 3170 kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp); 3171 kOCCppRdDelete(&CppRd); 3172 kOCSumFinalize(&pEntry->New.SumHead, &Ctx); 3173 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (pipe)"); 3174 } 3175 3176 3177 3178 3179 /** 3180 * Run the preprocessor and calculate the checksum of the output. 3181 * 3182 * @param pEntry The cache entry. 3183 * @param papszArgvPreComp The argument vector for executing preprocessor. 3184 * The cArgvPreComp'th argument must be NULL. 3185 * @param cArgvPreComp The number of arguments. 3186 */ 3187 static void kOCEntryPreProcess(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp) 3188 { 3189 /* 3190 * If we're executing the preprocessor in piped mode, it's relatively simple. 3191 */ 3192 if (pEntry->fPipedPreComp) 3193 kOCEntrySpawnProducer(pEntry, papszArgvPreComp, cArgvPreComp, "preprocess", 3194 kOCEntryPreProcessConsumer); 3195 else 3196 { 3197 /* 3198 * Rename the old preprocessed output to '-old' so the preprocessor won't 3199 * overwrite it when we execute it. 3200 */ 3201 if ( pEntry->Old.pszCppName 3202 && DoesFileInDirExist(pEntry->Old.pszCppName, pEntry->pszDir)) 3203 { 3204 size_t cch = strlen(pEntry->Old.pszCppName); 3205 char *psz = xmalloc(cch + sizeof("-old")); 3206 memcpy(psz, pEntry->Old.pszCppName, cch); 3207 memcpy(psz + cch, "-old", sizeof("-old")); 3208 3209 InfoMsg(3, "renaming '%s' to '%s' in '%s'\n", pEntry->Old.pszCppName, psz, pEntry->pszDir); 3210 UnlinkFileInDir(psz, pEntry->pszDir); 3211 if (RenameFileInDir(pEntry->Old.pszCppName, psz, pEntry->pszDir)) 3212 FatalDie("failed to rename '%s' -> '%s' in '%s': %s\n", 3213 pEntry->Old.pszCppName, psz, pEntry->pszDir, strerror(errno)); 3214 free(pEntry->Old.pszCppName); 3215 pEntry->Old.pszCppName = psz; 3216 } 3217 3218 /* 3219 * Preprocess it and calculate the checksum on the output. 3220 */ 3221 InfoMsg(3, "precompiling -> '%s'...\n", pEntry->New.pszCppName); 3222 kOCEntrySpawn(pEntry, &pEntry->New.cMsCpp, papszArgvPreComp, cArgvPreComp, "preprocess", NULL); 3223 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */); 3224 kOCEntryCalcChecksum(pEntry); 3225 if (pEntry->pszMakeDepFilename) 3226 kOCDepConsumer(&pEntry->DepState, pEntry->New.pszCppMapping, pEntry->New.cbCpp); 3227 } 3228 3229 if (pEntry->pszMakeDepFilename) 3230 kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename, pEntry->New.pszObjName, pEntry->pszDir, 3231 pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs); 3232 } 3233 3234 3235 /** 3236 * Worker function for kOCEntryTeeConsumer and kOCEntryCompileIt that 3237 * writes the preprocessor output to disk. 3238 * 3239 * @param pEntry The cache entry. 3240 * @param fFreeIt Whether we can free it after writing it or not. 3241 */ 3242 static void kOCEntryWriteCppOutput(PKOCENTRY pEntry, int fFreeIt) 3243 { 3244 /* 3245 * Remove old files. 3246 */ 3247 if (pEntry->Old.pszCppName) 3248 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir); 3249 if (pEntry->New.pszCppName) 3250 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir); 3251 3252 /* 3253 * Write it to disk if we've got a file name. 3254 */ 3255 if (pEntry->New.pszCppName) 3256 { 3257 long cbLeft; 3258 char *psz; 3259 int fd = OpenFileInDir(pEntry->New.pszCppName, pEntry->pszDir, 3260 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); 3261 if (fd == -1) 3262 FatalDie("Failed to create '%s' in '%s': %s\n", 3263 pEntry->New.pszCppName, pEntry->pszDir, strerror(errno)); 3264 psz = pEntry->New.pszCppMapping; 3265 cbLeft = (long)pEntry->New.cbCpp; 3266 while (cbLeft > 0) 3267 { 3268 long cbWritten = write(fd, psz, cbLeft); 3269 if (cbWritten < 0) 3270 { 3271 int iErr = errno; 3272 if (iErr == EINTR) 3273 continue; 3274 close(fd); 3275 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir); 3276 FatalDie("error writing '%s' in '%s': %s\n", 3277 pEntry->New.pszCppName, pEntry->pszDir, strerror(iErr)); 3278 } 3279 3280 psz += cbWritten; 3281 cbLeft -= cbWritten; 3282 } 3283 close(fd); 3284 } 3285 3286 /* 3287 * Free it. 3288 */ 3289 if (fFreeIt) 3290 { 3291 free(pEntry->New.pszCppMapping); 3292 pEntry->New.pszCppMapping = NULL; 3293 } 3294 } 3295 3296 3297 /** 3298 * kOCEntrySpawnConsumer callback that passes the preprocessor output to the 3299 * compiler and writes it to the disk (latter only when necesary). 3300 * 3301 * @param pEntry The cache entry. 3302 * @param fdOut The pipe handle connected to the childs stdin. 3303 */ 3304 static void kOCEntryCompileProducer(PKOCENTRY pEntry, int fdOut) 3305 { 3306 const char *psz = pEntry->New.pszCppMapping; 3307 long cbLeft = (long)pEntry->New.cbCpp; 3308 while (cbLeft > 0) 3309 { 3310 long cbWritten = write(fdOut, psz, cbLeft); 3311 if (cbWritten < 0) 3312 { 3313 if (errno == EINTR) 3314 continue; 3315 #ifdef __WIN__ /* HACK */ 3316 if ( errno == EINVAL 3317 && pEntry->pszNmPipeCompile 3318 && DisconnectNamedPipe((HANDLE)_get_osfhandle(fdOut)) 3319 && ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL)) 3320 { 3321 psz = pEntry->New.pszCppMapping; 3322 cbLeft = (long)pEntry->New.cbCpp; 3323 } 3324 FatalDie("compile - write(%d,,%ld) failed: %s - _doserrno=%d\n", fdOut, cbLeft, strerror(errno), _doserrno); 3325 #else 3326 FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno)); 3327 #endif 3328 } 3329 psz += cbWritten; 3330 cbLeft -= cbWritten; 3331 } 3332 close(fdOut); 3333 3334 if (pEntry->fPipedPreComp) 3335 kOCEntryWriteCppOutput(pEntry, 1 /* free it */); 3336 } 3337 3338 3339 /** 3340 * Does the actual compiling. 3341 * 3342 * @param pEntry The cache entry. 3343 */ 3344 static void kOCEntryCompileIt(PKOCENTRY pEntry) 3345 { 3346 /* 3347 * Delete the object files and free old cpp output that's no longer needed. 3348 */ 3349 if (pEntry->Old.pszObjName) 3350 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir); 3351 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir); 3352 3353 free(pEntry->Old.pszCppMapping); 3354 pEntry->Old.pszCppMapping = NULL; 3355 if (!pEntry->fPipedPreComp && !pEntry->fPipedCompile) 3356 { 3357 free(pEntry->New.pszCppMapping); 3358 pEntry->New.pszCppMapping = NULL; 3359 } 3360 3361 /* 3362 * Do the (re-)compile job. 3363 */ 3364 if (pEntry->fPipedCompile) 3365 { 3366 if ( !pEntry->fPipedPreComp 3367 && !pEntry->New.pszCppMapping) 3368 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */); 3369 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName); 3370 kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile, 3371 pEntry->New.cArgvCompile, "compile", kOCEntryCompileProducer); 3372 } 3373 else 3374 { 3375 if (pEntry->fPipedPreComp) 3376 kOCEntryWriteCppOutput(pEntry, 1 /* free it */); 3377 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName); 3378 kOCEntrySpawn(pEntry, &pEntry->New.cMsCompile, (const char * const *)pEntry->New.papszArgvCompile, 3379 pEntry->New.cArgvCompile, "compile", NULL); 3380 } 3381 } 3382 3383 3384 /** 3385 * kOCEntrySpawnTee callback that works sort of like 'tee'. 3386 * 3387 * It will calculate the preprocessed output checksum and 3388 * write it to disk while the compiler is busy compiling it. 3389 * 3390 * @param pEntry The cache entry. 3391 * @param fdIn The input handle (connected to the preprocessor). 3392 * @param fdOut The output handle (connected to the compiler). 3393 */ 3394 static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut) 3395 { 3396 #ifdef __WIN__ 3397 unsigned fConnectedToCompiler = fdOut == -1 || pEntry->pszNmPipeCompile == NULL; 3398 #endif 3399 KOCSUMCTX Ctx; 3400 KOCCPPRD CppRd; 3401 3402 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx); 3403 kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp, 3404 pEntry->pszMakeDepFilename ? &pEntry->DepState : NULL); 3405 InfoMsg(3, "preprocessor|compile - starting passhtru...\n"); 3406 for (;;) 3407 { 3408 /* 3409 * Read data from the pipe. 3410 */ 3411 const char *psz; 3412 long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz); 3413 if (!cbRead) 3414 break; 3415 InfoMsg(3, "preprocessor|compile - read %d\n", cbRead); 3416 3417 /* 3418 * Process the data. 3419 */ 3420 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead); 3421 if (pEntry->pszMakeDepFilename && !pEntry->fOptimizeCpp) 3422 kOCDepConsumer(&pEntry->DepState, psz, cbRead); 3423 3424 #ifdef __WIN__ 3425 if ( !fConnectedToCompiler 3426 && !(fConnectedToCompiler = ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL))) 3427 FatalDie("preprocess|compile - ConnectNamedPipe failed: %d\n", GetLastError()); 3428 #endif 3429 do 3430 { 3431 long cbWritten = write(fdOut, psz, cbRead); 3432 if (cbWritten < 0) 3433 { 3434 if (errno == EINTR) 3435 continue; 3436 FatalDie("preprocess|compile - write(%d,,%ld) failed: %s\n", fdOut, cbRead, strerror(errno)); 3437 } 3438 psz += cbWritten; 3439 cbRead -= cbWritten; 3440 } while (cbRead > 0); 3441 3442 } 3443 InfoMsg(3, "preprocessor|compile - done passhtru\n"); 3444 3445 close(fdIn); 3446 close(fdOut); 3447 kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp); 3448 kOCCppRdDelete(&CppRd); 3449 kOCSumFinalize(&pEntry->New.SumHead, &Ctx); 3450 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (tee)"); 3451 3452 /* 3453 * Write the preprocessor output to disk and free the memory it 3454 * occupies while the compiler is busy compiling. 3455 */ 3456 kOCEntryWriteCppOutput(pEntry, 1 /* free it */); 3457 } 3458 3459 3460 /** 3461 * Performs pre-compile and compile in one go (typical clean build scenario). 3462 * 3463 * @param pEntry The cache entry. 3464 * @param papszArgvPreComp The argument vector for executing preprocessor. 3465 * The cArgvPreComp'th argument must be NULL. 3466 * @param cArgvPreComp The number of arguments. 3467 */ 3468 static void kOCEntryPreProcessAndCompile(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp) 3469 { 3470 if ( pEntry->fPipedCompile 3471 && pEntry->fPipedPreComp) 3472 { 3473 /* 3474 * Clean up old stuff first. 3475 */ 3476 if (pEntry->Old.pszObjName) 3477 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir); 3478 if (pEntry->New.pszObjName) 3479 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir); 3480 if (pEntry->Old.pszCppName) 3481 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir); 3482 if (pEntry->New.pszCppName) 3483 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir); 3484 3485 /* 3486 * Do the actual compile and write the preprocessor output to disk. 3487 */ 3488 kOCEntrySpawnTee(pEntry, papszArgvPreComp, cArgvPreComp, 3489 (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile, 3490 "preprocess|compile", kOCEntryTeeConsumer); 3491 if (pEntry->pszMakeDepFilename) 3492 kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename, pEntry->New.pszObjName, pEntry->pszDir, 3493 pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs); 3494 } 3495 else 3496 { 3497 kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp); 3498 kOCEntryCompileIt(pEntry); 3499 } 3500 } 3501 3502 3503 /** 3504 * Check whether the string is a '#line' statement. 3505 * 3506 * @returns 1 if it is, 0 if it isn't. 3507 * @param psz The line to examin. 3508 * @parma piLine Where to store the line number. 3509 * @parma ppszFile Where to store the start of the filename. 3510 */ 3511 static int kOCEntryIsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile) 3512 { 3513 unsigned iLine; 3514 3515 /* Expect a hash. */ 3516 if (*psz++ != '#') 3517 return 0; 3518 3519 /* Skip blanks between '#' and the line / number */ 3520 while (*psz == ' ' || *psz == '\t') 3521 psz++; 3522 3523 /* Skip the 'line' if present. */ 3524 if (!strncmp(psz, "line", sizeof("line") - 1)) 3525 psz += sizeof("line"); 3526 3527 /* Expect a line number now. */ 3528 if ((unsigned char)(*psz - '0') > 9) 3529 return 0; 3530 iLine = 0; 3531 do 3532 { 3533 iLine *= 10; 3534 iLine += (*psz - '0'); 3535 psz++; 3536 } 3537 while ((unsigned char)(*psz - '0') <= 9); 3538 3539 /* Expect one or more space now. */ 3540 if (*psz != ' ' && *psz != '\t') 3541 return 0; 3542 do psz++; 3543 while (*psz == ' ' || *psz == '\t'); 3544 3545 /* that's good enough. */ 3546 *piLine = iLine; 3547 *ppszFile = psz; 3548 return 1; 3549 } 3550 3551 3552 /** 3553 * Scan backwards for the previous #line statement. 3554 * 3555 * @returns The filename in the previous statement. 3556 * @param pszStart Where to start. 3557 * @param pszStop Where to stop. Less than pszStart. 3558 * @param piLine The line number count to adjust. 3559 */ 3560 static const char *kOCEntryFindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine) 3561 { 3562 unsigned iLine = *piLine; 3563 assert(pszStart >= pszStop); 3564 while (pszStart >= pszStop) 3565 { 3566 if (*pszStart == '\n') 3567 iLine++; 3568 else if (*pszStart == '#') 3569 { 3570 unsigned iLineTmp; 3571 const char *pszFile; 3572 const char *psz = pszStart - 1; 3573 while (psz >= pszStop && (*psz == ' ' || *psz =='\t')) 3574 psz--; 3575 if ( (psz < pszStop || *psz == '\n') 3576 && kOCEntryIsLineStatement(pszStart, &iLineTmp, &pszFile)) 3577 { 3578 *piLine = iLine + iLineTmp - 1; 3579 return pszFile; 3580 } 3581 } 3582 pszStart--; 3583 } 3584 return NULL; 3585 } 3586 3587 3588 /** 3589 * Worker for kOCEntryCompareOldAndNewOutput() that compares the 3590 * preprocessed output using a fast but not very good method. 3591 * 3592 * @returns 1 if matching, 0 if not matching. 3593 * @param pEntry The entry containing the names of the files to compare. 3594 * The entry is not updated in any way. 3595 */ 3596 static int kOCEntryCompareFast(PCKOCENTRY pEntry) 3597 { 3598 const char * psz1 = pEntry->New.pszCppMapping; 3599 const char * const pszEnd1 = psz1 + pEntry->New.cbCpp; 3600 const char * psz2 = pEntry->Old.pszCppMapping; 3601 const char * const pszEnd2 = psz2 + pEntry->Old.cbCpp; 3602 3603 assert(*pszEnd1 == '\0'); 3604 assert(*pszEnd2 == '\0'); 3605 3606 /* 3607 * Iterate block by block and backtrack when we find a difference. 3608 */ 3609 for (;;) 3610 { 3611 size_t cch = pszEnd1 - psz1; 3612 if (cch > (size_t)(pszEnd2 - psz2)) 3613 cch = pszEnd2 - psz2; 3614 if (cch > 4096) 3615 cch = 4096; 3616 if ( cch 3617 && !memcmp(psz1, psz2, cch)) 3618 { 3619 /* no differences */ 3620 psz1 += cch; 3621 psz2 += cch; 3622 } 3623 else 3624 { 3625 /* 3626 * Pinpoint the difference exactly and the try find the start 3627 * of that line. Then skip forward until we find something to 3628 * work on that isn't spaces, #line statements or closing curly 3629 * braces. 3630 * 3631 * The closing curly braces are ignored because they are frequently 3632 * found at the end of header files (__END_DECLS) and the worst 3633 * thing that may happen if it isn't one of these braces we're 3634 * ignoring is that the final line in a function block is a little 3635 * bit off in the debug info. 3636 * 3637 * Since we might be skipping a few new empty headers, it is 3638 * possible that we will omit this header from the dependencies 3639 * when using VCC. This might not be a problem, since it seems 3640 * we'll have to use the preprocessor output to generate the deps 3641 * anyway. 3642 */ 3643 const char *psz; 3644 const char *pszMismatch1; 3645 const char *pszFile1 = NULL; 3646 unsigned iLine1 = 0; 3647 unsigned cCurlyBraces1 = 0; 3648 const char *pszMismatch2; 3649 const char *pszFile2 = NULL; 3650 unsigned iLine2 = 0; 3651 unsigned cCurlyBraces2 = 0; 3652 3653 /* locate the difference. */ 3654 while (cch >= 512 && !memcmp(psz1, psz2, 512)) 3655 psz1 += 512, psz2 += 512, cch -= 512; 3656 while (cch >= 64 && !memcmp(psz1, psz2, 64)) 3657 psz1 += 64, psz2 += 64, cch -= 64; 3658 while (*psz1 == *psz2 && cch > 0) 3659 psz1++, psz2++, cch--; 3660 3661 /* locate the start of that line. */ 3662 psz = psz1; 3663 while ( psz > pEntry->New.pszCppMapping 3664 && psz[-1] != '\n') 3665 psz--; 3666 psz2 -= (psz1 - psz); 3667 pszMismatch2 = psz2; 3668 pszMismatch1 = psz1 = psz; 3669 3670 /* Parse the 1st file line by line. */ 3671 while (psz1 < pszEnd1) 3672 { 3673 if (*psz1 == '\n') 3674 { 3675 psz1++; 3676 iLine1++; 3677 } 3678 else 3679 { 3680 psz = psz1; 3681 while (isspace(*psz) && *psz != '\n') 3682 psz++; 3683 if (*psz == '\n') 3684 { 3685 psz1 = psz + 1; 3686 iLine1++; 3687 } 3688 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine1, &pszFile1)) 3689 { 3690 psz1 = memchr(psz, '\n', pszEnd1 - psz); 3691 if (!psz1++) 3692 psz1 = pszEnd1; 3693 } 3694 else if (*psz == '}') 3695 { 3696 do psz++; 3697 while (isspace(*psz) && *psz != '\n'); 3698 if (*psz == '\n') 3699 iLine1++; 3700 else if (psz != pszEnd1) 3701 break; 3702 cCurlyBraces1++; 3703 psz1 = psz; 3704 } 3705 else if (psz == pszEnd1) 3706 psz1 = psz; 3707 else /* found something that can be compared. */ 3708 break; 3709 } 3710 } 3711 3712 /* Ditto for the 2nd file. */ 3713 while (psz2 < pszEnd2) 3714 { 3715 if (*psz2 == '\n') 3716 { 3717 psz2++; 3718 iLine2++; 3719 } 3720 else 3721 { 3722 psz = psz2; 3723 while (isspace(*psz) && *psz != '\n') 3724 psz++; 3725 if (*psz == '\n') 3726 { 3727 psz2 = psz + 1; 3728 iLine2++; 3729 } 3730 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine2, &pszFile2)) 3731 { 3732 psz2 = memchr(psz, '\n', pszEnd2 - psz); 3733 if (!psz2++) 3734 psz2 = pszEnd2; 3735 } 3736 else if (*psz == '}') 3737 { 3738 do psz++; 3739 while (isspace(*psz) && *psz != '\n'); 3740 if (*psz == '\n') 3741 iLine2++; 3742 else if (psz != pszEnd2) 3743 break; 3744 cCurlyBraces2++; 3745 psz2 = psz; 3746 } 3747 else if (psz == pszEnd2) 3748 psz2 = psz; 3749 else /* found something that can be compared. */ 3750 break; 3751 } 3752 } 3753 3754 /* Match the number of ignored closing curly braces. */ 3755 if (cCurlyBraces1 != cCurlyBraces2) 3756 return 0; 3757 3758 /* Reaching the end of any of them means the return statement can decide. */ 3759 if ( psz1 == pszEnd1 3760 || psz2 == pszEnd2) 3761 break; 3762 3763 /* Match the current line. */ 3764 psz = memchr(psz1, '\n', pszEnd1 - psz1); 3765 if (!psz++) 3766 psz = pszEnd1; 3767 cch = psz - psz1; 3768 if (psz2 + cch > pszEnd2) 3769 break; 3770 if (memcmp(psz1, psz2, cch)) 3771 break; 3772 3773 /* Check that we're at the same location now. */ 3774 if (!pszFile1) 3775 pszFile1 = kOCEntryFindFileStatement(pszMismatch1, pEntry->New.pszCppMapping, &iLine1); 3776 if (!pszFile2) 3777 pszFile2 = kOCEntryFindFileStatement(pszMismatch2, pEntry->Old.pszCppMapping, &iLine2); 3778 if (pszFile1 && pszFile2) 3779 { 3780 if (iLine1 != iLine2) 3781 break; 3782 while (*pszFile1 == *pszFile2 && *pszFile1 != '\n' && *pszFile1) 3783 pszFile1++, pszFile2++; 3784 if (*pszFile1 != *pszFile2) 3785 break; 3786 } 3787 else if (pszFile1 || pszFile2) 3788 { 3789 assert(0); /* this shouldn't happen. */ 3790 break; 3791 } 3792 3793 /* Advance. We might now have a misaligned buffer, but that's memcmps problem... */ 3794 psz1 += cch; 3795 psz2 += cch; 3796 } 3797 } 3798 3799 return psz1 == pszEnd1 3800 && psz2 == pszEnd2; 3801 } 3802 3803 3804 /** 3805 * Worker for kOCEntryCompileIfNeeded that compares the 3806 * preprocessed output. 3807 * 3808 * @returns 1 if matching, 0 if not matching. 3809 * @param pEntry The entry containing the names of the files to compare. 3810 * This will load the old cpp output (changing pszOldCppName and Old.cbCpp). 3811 */ 3812 static int kOCEntryCompareOldAndNewOutput(PKOCENTRY pEntry) 3813 { 3814 /* 3815 * I may implement a more sophisticated alternative method later... maybe. 3816 */ 3817 if (kOCEntryReadCppOutput(pEntry, &pEntry->Old, 1 /* nonfatal */) == -1) 3818 return 0; 3819 /*if () 3820 return kOCEntryCompareBest(pEntry);*/ 3821 return kOCEntryCompareFast(pEntry); 3822 } 3823 3824 3825 /** 3826 * Check if re-compilation is required. 3827 * This sets the fNeedCompile flag. 3828 * 3829 * @param pEntry The cache entry. 3830 */ 3831 static void kOCEntryCalcRecompile(PKOCENTRY pEntry) 3832 { 3833 if (pEntry->fNeedCompiling) 3834 return; 3835 3836 /* 3837 * Check if the preprocessor output differ in any significant way? 3838 */ 3839 if (!kOCSumHasEqualInChain(&pEntry->Old.SumHead, &pEntry->New.SumHead)) 3840 { 3841 if (pEntry->fOptimizeCpp & 2) 3842 { 3843 InfoMsg(2, "no checksum match - no need to compare output, -O2.\n"); 3844 pEntry->fNeedCompiling = 1; 3845 } 3846 else 3847 { 3848 InfoMsg(2, "no checksum match - comparing output\n"); 3849 if (!kOCEntryCompareOldAndNewOutput(pEntry)) 3850 pEntry->fNeedCompiling = 1; 3851 else 3852 kOCSumAddChain(&pEntry->New.SumHead, &pEntry->Old.SumHead); 3853 } 3854 } 3855 } 3856 3857 3858 /** 3859 * Does this cache entry need compiling or what? 3860 * 3861 * @returns 1 if it does, 0 if it doesn't. 3862 * @param pEntry The cache entry in question. 3863 */ 3864 static int kOCEntryNeedsCompiling(PCKOCENTRY pEntry) 3865 { 3866 return pEntry->fNeedCompiling; 3867 } 3868 3869 3870 /** 3871 * Tries to hardlink a file. 3872 * 3873 * @returns 1 if it succeeded, 0 if it didn't. 3874 * @param pszLink The name of the hardlink. 3875 * @param pszLinkTo The file to hardlinkg @a pszDst to. 3876 */ 3877 static int kOCEntryTryHardlink(const char *pszLink, const char *pszLinkTo) 3878 { 3879 #ifdef __WIN__ 3880 typedef BOOL (WINAPI *PFNCREATEHARDLINKA)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); 3881 static PFNCREATEHARDLINKA s_pfnCreateHardLinkA = NULL; 3882 static int s_fTried = FALSE; 3883 3884 /* The API was introduced in Windows 2000, so resolve it dynamically. */ 3885 if (!s_pfnCreateHardLinkA) 3886 { 3887 if (!s_fTried) 3888 { 3889 HMODULE hmod = LoadLibrary("KERNEL32.DLL"); 3890 if (hmod) 3891 *(FARPROC *)&s_pfnCreateHardLinkA = GetProcAddress(hmod, "CreateHardLinkA"); 3892 s_fTried = TRUE; 3893 } 3894 if (!s_pfnCreateHardLinkA) 3895 return 0; 3896 } 3897 3898 if (!s_pfnCreateHardLinkA(pszLink, pszLinkTo, NULL)) 3899 return 0; 3900 #else 3901 if (link(pszLinkTo, pszLink) != 0) 3902 return 0; 3903 #endif 3904 return 1; 3905 } 3906 3907 3908 3909 /** 3910 * Worker function for kOCEntryCopy. 3911 * 3912 * @param pEntry The entry we're coping to, which pszTo is relative to. 3913 * @param pszTo The destination. 3914 * @param pszFrom The source. This path will be freed. 3915 */ 3916 static void kOCEntryCopyFile(PCKOCENTRY pEntry, const char *pszTo, char *pszSrc) 3917 { 3918 char *pszDst = MakePathFromDirAndFile(pszTo, pEntry->pszDir); 3919 unlink(pszDst); 3920 if (!kOCEntryTryHardlink(pszDst, pszSrc)) 3921 { 3922 char *pszBuf = xmalloc(256 * 1024); 3923 char *psz; 3924 int fdSrc; 3925 int fdDst; 3926 3927 /* 3928 * Open the files. 3929 */ 3930 fdSrc = open(pszSrc, O_RDONLY | O_BINARY); 3931 if (fdSrc == -1) 3932 FatalDie("failed to open '%s': %s\n", pszSrc, strerror(errno)); 3933 3934 fdDst = open(pszDst, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); 3935 if (fdDst == -1) 3936 FatalDie("failed to create '%s': %s\n", pszDst, strerror(errno)); 3937 3938 /* 3939 * Copy them. 3940 */ 3941 for (;;) 3942 { 3943 /* read a chunk. */ 3944 long cbRead = read(fdSrc, pszBuf, 256*1024); 3945 if (cbRead < 0) 3946 { 3947 if (errno == EINTR) 3948 continue; 3949 FatalDie("read '%s' failed: %s\n", pszSrc, strerror(errno)); 3950 } 3951 if (!cbRead) 3952 break; /* eof */ 3953 3954 /* write the chunk. */ 3955 psz = pszBuf; 3956 do 3957 { 3958 long cbWritten = write(fdDst, psz, cbRead); 3959 if (cbWritten < 0) 3960 { 3961 if (errno == EINTR) 3962 continue; 3963 FatalDie("write '%s' failed: %s\n", pszSrc, strerror(errno)); 3964 } 3965 psz += cbWritten; 3966 cbRead -= cbWritten; 3967 } while (cbRead > 0); 3968 } 3969 3970 /* cleanup */ 3971 if (close(fdDst) != 0) 3972 FatalDie("closing '%s' failed: %s\n", pszDst, strerror(errno)); 3973 close(fdSrc); 3974 free(pszBuf); 3975 } 3976 free(pszDst); 3977 free(pszSrc); 3978 } 3979 3980 3981 /** 3982 * Copies the object (and whatever else) from one cache entry to another. 3983 * 3984 * This is called when a matching cache entry has been found and we don't 3985 * need to recompile anything. 3986 * 3987 * @param pEntry The entry to copy to. 3988 * @param pFrom The entry to copy from. 3989 */ 3990 static void kOCEntryCopy(PKOCENTRY pEntry, PCKOCENTRY pFrom) 3991 { 3992 kOCEntryCopyFile(pEntry, pEntry->New.pszObjName, 3993 MakePathFromDirAndFile(pFrom->New.pszObjName 3994 ? pFrom->New.pszObjName : pFrom->Old.pszObjName, 3995 pFrom->pszDir)); 3996 } 3997 3998 3999 /** 4000 * Gets the absolute path to the cache entry. 4001 * 4002 * @returns absolute path to the cache entry. 4003 * @param pEntry The cache entry in question. 4004 */ 4005 static const char *kOCEntryAbsPath(PCKOCENTRY pEntry) 4006 { 4007 return pEntry->pszAbsPath; 4008 } 4009 4010 4011 4012 4013 4014 4015 /** 4016 * Digest of one cache entry. 4017 * 4018 * This contains all the information required to find a matching 4019 * cache entry without having to open each of the files. 4020 */ 4021 typedef struct KOCDIGEST 4022 { 4023 /** The relative path to the entry. Optional if pszAbsPath is set. */ 4024 char *pszRelPath; 4025 /** The absolute path to the entry. Optional if pszRelPath is set. */ 4026 char *pszAbsPath; 4027 /** The target os/arch identifier. */ 4028 char *pszTarget; 4029 /** A unique number assigned to the entry when it's (re)-inserted 4030 * into the cache. This is used for simple consitency checking. */ 4031 uint32_t uKey; 4032 /** The checksum of the compile argument vector. */ 4033 KOCSUM SumCompArgv; 4034 /** The list of preprocessor output checksums that's . */ 4035 KOCSUM SumHead; 4036 } KOCDIGEST; 4037 /** Pointer to a file digest. */ 4038 typedef KOCDIGEST *PKOCDIGEST; 4039 /** Pointer to a const file digest. */ 4040 typedef KOCDIGEST *PCKOCDIGEST; 4041 4042 4043 /** 4044 * Initializes the specified digest. 4045 * 4046 * @param pDigest The digest. 4047 */ 4048 static void kOCDigestInit(PKOCDIGEST pDigest) 4049 { 4050 memset(pDigest, 0, sizeof(*pDigest)); 4051 kOCSumInit(&pDigest->SumHead); 4052 } 4053 4054 4055 /** 4056 * Initializes the digest for the specified entry. 4057 * 4058 * @param pDigest The (uninitialized) digest. 4059 * @param pEntry The entry. 4060 */ 4061 static void kOCDigestInitFromEntry(PKOCDIGEST pDigest, PCKOCENTRY pEntry) 4062 { 4063 kOCDigestInit(pDigest); 4064 4065 pDigest->uKey = pEntry->uKey; 4066 pDigest->pszTarget = xstrdup(pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget); 4067 4068 kOCSumInit(&pDigest->SumCompArgv); 4069 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv)) 4070 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv); 4071 else 4072 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->Old.SumCompArgv); 4073 4074 kOCSumInit(&pDigest->SumHead); 4075 if (!kOCSumIsEmpty(&pEntry->New.SumHead)) 4076 kOCSumAddChain(&pDigest->SumHead, &pEntry->New.SumHead); 4077 else 4078 kOCSumAddChain(&pDigest->SumHead, &pEntry->Old.SumHead); 4079 4080 /** @todo implement selective relative path support. */ 4081 pDigest->pszRelPath = NULL; 4082 pDigest->pszAbsPath = xstrdup(kOCEntryAbsPath(pEntry)); 4083 } 4084 4085 4086 /** 4087 * Purges a digest, freeing all resources and returning 4088 * it to the initial state. 4089 * 4090 * @param pDigest The digest. 4091 */ 4092 static void kOCDigestPurge(PKOCDIGEST pDigest) 4093 { 4094 free(pDigest->pszRelPath); 4095 free(pDigest->pszAbsPath); 4096 free(pDigest->pszTarget); 4097 pDigest->pszTarget = pDigest->pszAbsPath = pDigest->pszRelPath = NULL; 4098 pDigest->uKey = 0; 4099 kOCSumDeleteChain(&pDigest->SumCompArgv); 4100 kOCSumDeleteChain(&pDigest->SumHead); 4101 } 4102 4103 4104 /** 4105 * Returns the absolute path to the entry, calculating 4106 * the path if necessary. 4107 * 4108 * @returns absolute path. 4109 * @param pDigest The digest. 4110 * @param pszDir The cache directory that it might be relative to. 4111 */ 4112 static const char *kOCDigestAbsPath(PCKOCDIGEST pDigest, const char *pszDir) 4113 { 4114 if (!pDigest->pszAbsPath) 4115 { 4116 char *pszPath = MakePathFromDirAndFile(pDigest->pszRelPath, pszDir); 4117 ((PKOCDIGEST)pDigest)->pszAbsPath = AbsPath(pszPath); 4118 free(pszPath); 4119 } 4120 return pDigest->pszAbsPath; 4121 } 4122 4123 4124 /** 4125 * Checks that the digest matches the 4126 * 4127 * @returns 1 if valid, 0 if invalid in some way. 4128 * 4129 * @param pDigest The digest to validate. 4130 * @param pEntry What to validate it against. 4131 */ 4132 static int kOCDigestIsValid(PCKOCDIGEST pDigest, PCKOCENTRY pEntry) 4133 { 4134 PCKOCSUM pSum; 4135 PCKOCSUM pSumEntry; 4136 4137 if (pDigest->uKey != pEntry->uKey) 4138 return 0; 4139 4140 if (!kOCSumIsEqual(&pDigest->SumCompArgv, 4141 kOCSumIsEmpty(&pEntry->New.SumCompArgv) 4142 ? &pEntry->Old.SumCompArgv : &pEntry->New.SumCompArgv)) 4143 return 0; 4144 4145 if (strcmp(pDigest->pszTarget, pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget)) 4146 return 0; 4147 4148 /* match the checksums */ 4149 pSumEntry = kOCSumIsEmpty(&pEntry->New.SumHead) 4150 ? &pEntry->Old.SumHead : &pEntry->New.SumHead; 4151 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext) 4152 if (!kOCSumHasEqualInChain(pSumEntry, pSum)) 4153 return 0; 4154 4155 return 1; 4156 } 4157 4158 4159 4160 4161 4162 /** 4163 * The structure for the central cache entry. 4164 */ 4165 typedef struct KOBJCACHE 4166 { 4167 /** The entry name. */ 4168 const char *pszName; 4169 /** The dir that relative names in the digest are relative to. */ 4170 char *pszDir; 4171 /** The absolute path. */ 4172 char *pszAbsPath; 4173 4174 /** The cache file descriptor. */ 4175 int fd; 4176 /** The stream associated with fd. */ 4177 FILE *pFile; 4178 /** Whether it's currently locked or not. */ 4179 unsigned fLocked; 4180 /** Whether the cache file is dirty and needs writing back. */ 4181 unsigned fDirty; 4182 /** Whether this is a new cache or not. */ 4183 unsigned fNewCache; 4184 4185 /** The cache file generation. */ 4186 uint32_t uGeneration; 4187 /** The next valid key. (Determin at load time.) */ 4188 uint32_t uNextKey; 4189 4190 /** Number of digests in paDigests. */ 4191 unsigned cDigests; 4192 /** Array of digests for the KOCENTRY objects in the cache. */ 4193 PKOCDIGEST paDigests; 4194 4195 } KOBJCACHE; 4196 /** Pointer to a cache. */ 4197 typedef KOBJCACHE *PKOBJCACHE; 4198 /** Pointer to a const cache. */ 4199 typedef KOBJCACHE const *PCKOBJCACHE; 4200 4201 4202 /** 4203 * Creates an empty cache. 4204 * 4205 * This doesn't touch the file system, it just create the data structure. 4206 * 4207 * @returns Pointer to a cache. 4208 * @param pszCacheFile The cache file. 4209 */ 4210 static PKOBJCACHE kObjCacheCreate(const char *pszCacheFile) 4211 { 4212 PKOBJCACHE pCache; 4213 size_t off; 4214 4215 /* 4216 * Allocate an empty entry. 4217 */ 4218 pCache = xmallocz(sizeof(*pCache)); 4219 pCache->fd = -1; 4220 4221 /* 4222 * Setup the directory and cache file name. 4223 */ 4224 pCache->pszAbsPath = AbsPath(pszCacheFile); 4225 pCache->pszName = FindFilenameInPath(pCache->pszAbsPath); 4226 off = pCache->pszName - pCache->pszAbsPath; 4227 if (!off) 4228 FatalDie("Failed to find abs path for '%s'!\n", pszCacheFile); 4229 pCache->pszDir = xmalloc(off); 4230 memcpy(pCache->pszDir, pCache->pszAbsPath, off - 1); 4231 pCache->pszDir[off - 1] = '\0'; 4232 4233 return pCache; 4234 } 4235 4236 4237 /** 4238 * Destroys the cache - closing any open files, freeing up heap memory and such. 4239 * 4240 * @param pCache The cache. 4241 */ 4242 static void kObjCacheDestroy(PKOBJCACHE pCache) 4243 { 4244 if (pCache->pFile) 4245 { 4246 errno = 0; 4247 if (fclose(pCache->pFile) != 0) 4248 FatalMsg("fclose failed: %s\n", strerror(errno)); 4249 pCache->pFile = NULL; 4250 pCache->fd = -1; 4251 } 4252 free(pCache->paDigests); 4253 free(pCache->pszAbsPath); 4254 free(pCache->pszDir); 4255 free(pCache); 4256 } 4257 4258 4259 /** 4260 * Purges the data in the cache object. 4261 * 4262 * @param pCache The cache object. 4263 */ 4264 static void kObjCachePurge(PKOBJCACHE pCache) 4265 { 4266 while (pCache->cDigests > 0) 4267 kOCDigestPurge(&pCache->paDigests[--pCache->cDigests]); 4268 free(pCache->paDigests); 4269 pCache->paDigests = NULL; 4270 pCache->uGeneration = 0; 4271 pCache->uNextKey = 0; 4272 } 4273 4274 4275 /** 4276 * (Re-)reads the file. 4277 * 4278 * @param pCache The cache to (re)-read. 4279 */ 4280 static void kObjCacheRead(PKOBJCACHE pCache) 4281 { 4282 unsigned i; 4283 char szBuf[8192]; 4284 int fBad = 0; 4285 4286 InfoMsg(4, "reading cache file...\n"); 4287 4288 /* 4289 * Rewind the file & stream, and associate a temporary buffer 4290 * with the stream to speed up reading. 4291 */ 4292 if (lseek(pCache->fd, 0, SEEK_SET) == -1) 4293 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno)); 4294 rewind(pCache->pFile); 4295 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0) 4296 FatalDie("fdopen(cache-fd,rb) failed: %s\n", strerror(errno)); 4297 4298 /* 4299 * Read magic and generation. 4300 */ 4301 if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile) 4302 || strcmp(g_szLine, "magic=kObjCache-v0.1.0\n")) 4303 { 4304 InfoMsg(2, "bad cache file (magic)\n"); 4305 fBad = 1; 4306 } 4307 else if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile) 4308 || strncmp(g_szLine, "generation=", sizeof("generation=") - 1)) 4309 { 4310 InfoMsg(2, "bad cache file (generation)\n"); 4311 fBad = 1; 4312 } 4313 else if ( pCache->uGeneration 4314 && (long)pCache->uGeneration == atol(&g_szLine[sizeof("generation=") - 1])) 4315 { 4316 InfoMsg(3, "drop re-read unmodified cache file\n"); 4317 fBad = 0; 4318 } 4319 else 4320 { 4321 int fBadBeforeMissing; 4322 4323 /* 4324 * Read everything (anew). 4325 */ 4326 kObjCachePurge(pCache); 4327 do 4328 { 4329 PKOCDIGEST pDigest; 4330 char *pszNl; 4331 char *pszVal; 4332 char *psz; 4333 4334 /* Split the line and drop the trailing newline. */ 4335 pszVal = strchr(g_szLine, '='); 4336 if ((fBad = pszVal == NULL)) 4337 break; 4338 *pszVal++ = '\0'; 4339 4340 pszNl = strchr(pszVal, '\n'); 4341 if (pszNl) 4342 *pszNl = '\0'; 4343 4344 /* digest '#'? */ 4345 psz = strchr(g_szLine, '#'); 4346 if (psz) 4347 { 4348 char *pszNext; 4349 i = strtoul(++psz, &pszNext, 0); 4350 if ((fBad = pszNext && *pszNext)) 4351 break; 4352 if ((fBad = i >= pCache->cDigests)) 4353 break; 4354 pDigest = &pCache->paDigests[i]; 4355 *psz = '\0'; 4356 } 4357 else 4358 pDigest = NULL; 4359 4360 4361 /* string case on value name. */ 4362 if (!strcmp(g_szLine, "sum-#")) 4363 { 4364 KOCSUM Sum; 4365 if ((fBad = kOCSumInitFromString(&Sum, pszVal) != 0)) 4366 break; 4367 kOCSumAdd(&pDigest->SumHead, &Sum); 4368 } 4369 else if (!strcmp(g_szLine, "digest-abs-#")) 4370 { 4371 if ((fBad = pDigest->pszAbsPath != NULL)) 4372 break; 4373 pDigest->pszAbsPath = xstrdup(pszVal); 4374 } 4375 else if (!strcmp(g_szLine, "digest-rel-#")) 4376 { 4377 if ((fBad = pDigest->pszRelPath != NULL)) 4378 break; 4379 pDigest->pszRelPath = xstrdup(pszVal); 4380 } 4381 else if (!strcmp(g_szLine, "key-#")) 4382 { 4383 if ((fBad = pDigest->uKey != 0)) 4384 break; 4385 pDigest->uKey = strtoul(pszVal, &psz, 0); 4386 if ((fBad = psz && *psz)) 4387 break; 4388 if (pDigest->uKey >= pCache->uNextKey) 4389 pCache->uNextKey = pDigest->uKey + 1; 4390 } 4391 else if (!strcmp(g_szLine, "comp-argv-sum-#")) 4392 { 4393 if ((fBad = !kOCSumIsEmpty(&pDigest->SumCompArgv))) 4394 break; 4395 if ((fBad = kOCSumInitFromString(&pDigest->SumCompArgv, pszVal) != 0)) 4396 break; 4397 } 4398 else if (!strcmp(g_szLine, "target-#")) 4399 { 4400 if ((fBad = pDigest->pszTarget != NULL)) 4401 break; 4402 pDigest->pszTarget = xstrdup(pszVal); 4403 } 4404 else if (!strcmp(g_szLine, "digests")) 4405 { 4406 if ((fBad = pCache->paDigests != NULL)) 4407 break; 4408 pCache->cDigests = strtoul(pszVal, &psz, 0); 4409 if ((fBad = psz && *psz)) 4410 break; 4411 i = (pCache->cDigests + 4) & ~3; 4412 pCache->paDigests = xmalloc(i * sizeof(pCache->paDigests[0])); 4413 for (i = 0; i < pCache->cDigests; i++) 4414 kOCDigestInit(&pCache->paDigests[i]); 4415 } 4416 else if (!strcmp(g_szLine, "generation")) 4417 { 4418 if ((fBad = pCache->uGeneration != 0)) 4419 break; 4420 pCache->uGeneration = strtoul(pszVal, &psz, 0); 4421 if ((fBad = psz && *psz)) 4422 break; 4423 } 4424 else if (!strcmp(g_szLine, "the-end")) 4425 { 4426 fBad = strcmp(pszVal, "fine"); 4427 break; 4428 } 4429 else 4430 { 4431 fBad = 1; 4432 break; 4433 } 4434 } while (fgets(g_szLine, sizeof(g_szLine), pCache->pFile)); 4435 4436 /* 4437 * Did we find everything? 4438 */ 4439 fBadBeforeMissing = fBad; 4440 if ( !fBad 4441 && !pCache->uGeneration) 4442 fBad = 1; 4443 if (!fBad) 4444 for (i = 0; i < pCache->cDigests; i++) 4445 { 4446 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumCompArgv))) 4447 break; 4448 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumHead))) 4449 break; 4450 if ((fBad = pCache->paDigests[i].uKey == 0)) 4451 break; 4452 if ((fBad = pCache->paDigests[i].pszAbsPath == NULL 4453 && pCache->paDigests[i].pszRelPath == NULL)) 4454 break; 4455 if ((fBad = pCache->paDigests[i].pszTarget == NULL)) 4456 break; 4457 InfoMsg(4, "digest-%u: %s\n", i, pCache->paDigests[i].pszAbsPath 4458 ? pCache->paDigests[i].pszAbsPath : pCache->paDigests[i].pszRelPath); 4459 } 4460 if (fBad) 4461 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff"); 4462 else if (ferror(pCache->pFile)) 4463 { 4464 InfoMsg(2, "cache file read error\n"); 4465 fBad = 1; 4466 } 4467 } 4468 if (fBad) 4469 { 4470 kObjCachePurge(pCache); 4471 pCache->fNewCache = 1; 4472 } 4473 4474 /* 4475 * Disassociate the buffer from the stream changing 4476 * it to non-buffered mode. 4477 */ 4478 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0) 4479 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno)); 4480 } 4481 4482 4483 /** 4484 * Re-writes the cache file. 4485 * 4486 * @param pCache The cache to commit and unlock. 4487 */ 4488 static void kObjCacheWrite(PKOBJCACHE pCache) 4489 { 4490 unsigned i; 4491 off_t cb; 4492 char szBuf[8192]; 4493 assert(pCache->fLocked); 4494 assert(pCache->fDirty); 4495 4496 /* 4497 * Rewind the file & stream, and associate a temporary buffer 4498 * with the stream to speed up the writing. 4499 */ 4500 if (lseek(pCache->fd, 0, SEEK_SET) == -1) 4501 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno)); 4502 rewind(pCache->pFile); 4503 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0) 4504 FatalDie("setvbuf failed: %s\n", strerror(errno)); 4505 4506 /* 4507 * Write the header. 4508 */ 4509 pCache->uGeneration++; 4510 fprintf(pCache->pFile, 4511 "magic=kObjCache-v0.1.0\n" 4512 "generation=%d\n" 4513 "digests=%d\n", 4514 pCache->uGeneration, 4515 pCache->cDigests); 4516 4517 /* 4518 * Write the digests. 4519 */ 4520 for (i = 0; i < pCache->cDigests; i++) 4521 { 4522 PCKOCDIGEST pDigest = &pCache->paDigests[i]; 4523 PKOCSUM pSum; 4524 4525 if (pDigest->pszAbsPath) 4526 fprintf(pCache->pFile, "digest-abs-#%u=%s\n", i, pDigest->pszAbsPath); 4527 if (pDigest->pszRelPath) 4528 fprintf(pCache->pFile, "digest-rel-#%u=%s\n", i, pDigest->pszRelPath); 4529 fprintf(pCache->pFile, "key-#%u=%u\n", i, pDigest->uKey); 4530 fprintf(pCache->pFile, "target-#%u=%s\n", i, pDigest->pszTarget); 4531 fprintf(pCache->pFile, "comp-argv-sum-#%u=", i); 4532 kOCSumFPrintf(&pDigest->SumCompArgv, pCache->pFile); 4533 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext) 4534 { 4535 fprintf(pCache->pFile, "sum-#%u=", i); 4536 kOCSumFPrintf(pSum, pCache->pFile); 4537 } 4538 } 4539 4540 /* 4541 * Close the stream and unlock fhe file. 4542 * (Closing the stream shouldn't close the file handle IIRC...) 4543 */ 4544 fprintf(pCache->pFile, "the-end=fine\n"); 4545 errno = 0; 4546 if ( fflush(pCache->pFile) < 0 4547 || ferror(pCache->pFile)) 4548 { 4549 int iErr = errno; 4550 fclose(pCache->pFile); 4551 UnlinkFileInDir(pCache->pszName, pCache->pszDir); 4552 FatalDie("Stream error occured while writing '%s' in '%s': %s\n", 4553 pCache->pszName, pCache->pszDir, strerror(iErr)); 4554 } 4555 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0) 4556 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno)); 4557 4558 cb = lseek(pCache->fd, 0, SEEK_CUR); 4559 if (cb == -1) 4560 FatalDie("lseek(cache-file,0,CUR) failed: %s\n", strerror(errno)); 4561 #if defined(__WIN__) 4562 if (_chsize(pCache->fd, cb) == -1) 4563 #else 4564 if (ftruncate(pCache->fd, cb) == -1) 4565 #endif 4566 FatalDie("file truncation failed: %s\n", strerror(errno)); 4567 InfoMsg(4, "wrote '%s' in '%s', %d bytes\n", pCache->pszName, pCache->pszDir, cb); 4568 } 4569 4570 4571 /** 4572 * Cleans out all invalid digests.s 4573 * 4574 * This is done periodically from the unlock routine to make 4575 * sure we don't accidentally accumulate stale digests. 4576 * 4577 * @param pCache The cache to chek. 4578 */ 4579 static void kObjCacheClean(PKOBJCACHE pCache) 4580 { 4581 unsigned i = pCache->cDigests; 4582 while (i-- > 0) 4583 { 4584 /* 4585 * Try open it and purge it if it's bad. 4586 * (We don't kill the entry file because that's kmk clean's job.) 4587 */ 4588 PCKOCDIGEST pDigest = &pCache->paDigests[i]; 4589 PKOCENTRY pEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir)); 4590 kOCEntryRead(pEntry); 4591 if ( !kOCEntryCheck(pEntry) 4592 || !kOCDigestIsValid(pDigest, pEntry)) 4593 { 4594 unsigned cLeft; 4595 kOCDigestPurge(pDigest); 4596 4597 pCache->cDigests--; 4598 cLeft = pCache->cDigests - i; 4599 if (cLeft) 4600 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest)); 4601 4602 pCache->fDirty = 1; 4603 } 4604 kOCEntryDestroy(pEntry); 4605 } 4606 } 4607 4608 4609 /** 4610 * Locks the cache for exclusive access. 4611 * 4612 * This will open the file if necessary and lock the entire file 4613 * using the best suitable platform API (tricky). 4614 * 4615 * @param pCache The cache to lock. 4616 */ 4617 static void kObjCacheLock(PKOBJCACHE pCache) 4618 { 4619 struct stat st; 4620 #if defined(__WIN__) 4621 OVERLAPPED OverLapped; 4622 #endif 4623 4624 assert(!pCache->fLocked); 4625 4626 /* 4627 * Open it? 4628 */ 4629 if (pCache->fd < 0) 4630 { 4631 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666); 4632 if (pCache->fd == -1) 4633 { 4634 MakePath(pCache->pszDir); 4635 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666); 4636 if (pCache->fd == -1) 4637 FatalDie("Failed to create '%s' in '%s': %s\n", pCache->pszName, pCache->pszDir, strerror(errno)); 4638 } 4639 4640 pCache->pFile = fdopen(pCache->fd, "r+b"); 4641 if (!pCache->pFile) 4642 FatalDie("fdopen failed: %s\n", strerror(errno)); 4643 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0) 4644 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno)); 4645 } 4646 4647 /* 4648 * Lock it. 4649 */ 4650 #if defined(__WIN__) 4651 memset(&OverLapped, 0, sizeof(OverLapped)); 4652 if (!LockFileEx((HANDLE)_get_osfhandle(pCache->fd), LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, 0, &OverLapped)) 4653 FatalDie("Failed to lock the cache file: Windows Error %d\n", GetLastError()); 4654 #elif defined(__sun__) 4655 { 4656 struct flock fl; 4657 fl.l_whence = 0; 4658 fl.l_start = 0; 4659 fl.l_len = 0; 4660 fl.l_type = F_WRLCK; 4661 if (fcntl(pCache->fd, F_SETLKW, &fl) != 0) 4662 FatalDie("Failed to lock the cache file: %s\n", strerror(errno)); 4663 } 4664 #else 4665 if (flock(pCache->fd, LOCK_EX) != 0) 4666 FatalDie("Failed to lock the cache file: %s\n", strerror(errno)); 4667 #endif 4668 pCache->fLocked = 1; 4669 4670 /* 4671 * Check for new cache and read it it's an existing cache. 4672 * 4673 * There is no point in initializing a new cache until we've finished 4674 * compiling and has something to put into it, so we'll leave it as a 4675 * 0 byte file. 4676 */ 4677 if (fstat(pCache->fd, &st) == -1) 4678 FatalDie("fstat(cache-fd) failed: %s\n", strerror(errno)); 4679 if (st.st_size) 4680 kObjCacheRead(pCache); 4681 else 4682 { 4683 pCache->fNewCache = 1; 4684 InfoMsg(2, "the cache file is empty\n"); 4685 } 4686 } 4687 4688 4689 /** 4690 * Unlocks the cache (without writing anything back). 4691 * 4692 * @param pCache The cache to unlock. 4693 */ 4694 static void kObjCacheUnlock(PKOBJCACHE pCache) 4695 { 4696 #if defined(__WIN__) 4697 OVERLAPPED OverLapped; 4698 #endif 4699 assert(pCache->fLocked); 4700 4701 /* 4702 * Write it back if it's dirty. 4703 */ 4704 if (pCache->fDirty) 4705 { 4706 if ( pCache->cDigests >= 16 4707 && (pCache->uGeneration % 19) == 19) 4708 kObjCacheClean(pCache); 4709 kObjCacheWrite(pCache); 4710 pCache->fDirty = 0; 4711 } 4712 4713 /* 4714 * Lock it. 4715 */ 4716 #if defined(__WIN__) 4717 memset(&OverLapped, 0, sizeof(OverLapped)); 4718 if (!UnlockFileEx((HANDLE)_get_osfhandle(pCache->fd), 0, ~0U, 0, &OverLapped)) 4719 FatalDie("Failed to unlock the cache file: Windows Error %d\n", GetLastError()); 4720 #elif defined(__sun__) 4721 { 4722 struct flock fl; 4723 fl.l_whence = 0; 4724 fl.l_start = 0; 4725 fl.l_len = 0; 4726 fl.l_type = F_UNLCK; 4727 if (fcntl(pCache->fd, F_SETLKW, &fl) != 0) 4728 FatalDie("Failed to lock the cache file: %s\n", strerror(errno)); 4729 } 4730 #else 4731 if (flock(pCache->fd, LOCK_UN) != 0) 4732 FatalDie("Failed to unlock the cache file: %s\n", strerror(errno)); 4733 #endif 4734 pCache->fLocked = 0; 4735 } 4736 4737 4738 /** 4739 * Removes the entry from the cache. 4740 * 4741 * The entry doesn't need to be in the cache. 4742 * The cache entry (file) itself is not touched. 4743 * 4744 * @param pCache The cache. 4745 * @param pEntry The entry. 4746 */ 4747 static void kObjCacheRemoveEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry) 4748 { 4749 unsigned i = pCache->cDigests; 4750 while (i-- > 0) 4751 { 4752 PKOCDIGEST pDigest = &pCache->paDigests[i]; 4753 if (ArePathsIdentical(kOCDigestAbsPath(pDigest, pCache->pszDir), 4754 kOCEntryAbsPath(pEntry))) 4755 { 4756 unsigned cLeft; 4757 kOCDigestPurge(pDigest); 4758 4759 pCache->cDigests--; 4760 cLeft = pCache->cDigests - i; 4761 if (cLeft) 4762 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest)); 4763 4764 pCache->fDirty = 1; 4765 InfoMsg(3, "removing entry '%s'; %d left.\n", kOCEntryAbsPath(pEntry), pCache->cDigests); 4766 } 4767 } 4768 } 4769 4770 4771 /** 4772 * Inserts the entry into the cache. 4773 * 4774 * The cache entry (file) itself is not touched by this operation, 4775 * the pEntry object otoh is. 4776 * 4777 * @param pCache The cache. 4778 * @param pEntry The entry. 4779 */ 4780 static void kObjCacheInsertEntry(PKOBJCACHE pCache, PKOCENTRY pEntry) 4781 { 4782 unsigned i; 4783 4784 /* 4785 * Find a new key. 4786 */ 4787 pEntry->uKey = pCache->uNextKey++; 4788 if (!pEntry->uKey) 4789 pEntry->uKey = pCache->uNextKey++; 4790 i = pCache->cDigests; 4791 while (i-- > 0) 4792 if (pCache->paDigests[i].uKey == pEntry->uKey) 4793 { 4794 pEntry->uKey = pCache->uNextKey++; 4795 if (!pEntry->uKey) 4796 pEntry->uKey = pCache->uNextKey++; 4797 i = pCache->cDigests; 4798 } 4799 4800 /* 4801 * Reallocate the digest array? 4802 */ 4803 if ( !(pCache->cDigests & 3) 4804 && (pCache->cDigests || !pCache->paDigests)) 4805 pCache->paDigests = xrealloc(pCache->paDigests, sizeof(pCache->paDigests[0]) * (pCache->cDigests + 4)); 4806 4807 /* 4808 * Create a new digest. 4809 */ 4810 kOCDigestInitFromEntry(&pCache->paDigests[pCache->cDigests], pEntry); 4811 pCache->cDigests++; 4812 InfoMsg(4, "Inserted digest #%u: %s\n", pCache->cDigests - 1, kOCEntryAbsPath(pEntry)); 4813 4814 pCache->fDirty = 1; 4815 } 4816 4817 4818 /** 4819 * Find a matching cache entry. 4820 */ 4821 static PKOCENTRY kObjCacheFindMatchingEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry) 4822 { 4823 unsigned i = pCache->cDigests; 4824 4825 assert(pEntry->fNeedCompiling); 4826 assert(!kOCSumIsEmpty(&pEntry->New.SumCompArgv)); 4827 assert(!kOCSumIsEmpty(&pEntry->New.SumHead)); 4828 4829 while (i-- > 0) 4830 { 4831 /* 4832 * Matching? 4833 */ 4834 PCKOCDIGEST pDigest = &pCache->paDigests[i]; 4835 if ( kOCSumIsEqual(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv) 4836 && kOCSumHasEqualInChain(&pDigest->SumHead, &pEntry->New.SumHead)) 4837 { 4838 /* 4839 * Try open it. 4840 */ 4841 unsigned cLeft; 4842 PKOCENTRY pRetEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir)); 4843 kOCEntryRead(pRetEntry); 4844 if ( kOCEntryCheck(pRetEntry) 4845 && kOCDigestIsValid(pDigest, pRetEntry)) 4846 return pRetEntry; 4847 kOCEntryDestroy(pRetEntry); 4848 4849 /* bad entry, purge it. */ 4850 InfoMsg(3, "removing bad digest '%s'\n", kOCDigestAbsPath(pDigest, pCache->pszDir)); 4851 kOCDigestPurge(pDigest); 4852 4853 pCache->cDigests--; 4854 cLeft = pCache->cDigests - i; 4855 if (cLeft) 4856 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest)); 4857 4858 pCache->fDirty = 1; 4859 } 4860 } 4861 4862 return NULL; 4863 } 4864 4865 4866 /** 4867 * Is this a new cache? 4868 * 4869 * @returns 1 if new, 0 if not new. 4870 * @param pEntry The entry. 4871 */ 4872 static int kObjCacheIsNew(PKOBJCACHE pCache) 4873 { 4874 return pCache->fNewCache; 660 rcRet = ErrorMsg("Didn't find '!<arch>\\n' magic in '%s' (or read error)\n", pszLib); 661 662 if (fclose(pFile) != 0) 663 rcRet = ErrorMsg("Error closing '%s'\n"); 664 } 665 else 666 rcRet = ErrorMsg("Failed to open '%s' for read+write\n", pszLib); 667 return rcRet; 4875 668 } 4876 669 … … 4901 694 { 4902 695 fprintf(pOut, 4903 "syntax: kObjCache [--kObjCache-options] [-v|--verbose]\n" 4904 " < [-c|--cache-file <cache-file>]\n" 4905 " | [-n|--name <name-in-cache>] [[-d|--cache-dir <cache-dir>]] >\n" 4906 " <-f|--file <local-cache-file>>\n" 4907 " <-t|--target <target-name>>\n" 4908 " [-r|--redir-stdout] [-p|--passthru] [--named-pipe-compile <pipename>]\n" 4909 " --kObjCache-cpp <filename> <preprocessor + args>\n" 4910 " --kObjCache-cc <object> <compiler + args>\n" 4911 " [--kObjCache-both [args]]\n" 4912 ); 4913 fprintf(pOut, 4914 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n" 4915 " kObjCache <-V|--version>\n" 4916 " kObjCache [-?|/?|-h|/h|--help|/help]\n" 4917 "\n" 4918 "The env.var. KOBJCACHE_DIR sets the default cache diretory (-d).\n" 4919 "The env.var. KOBJCACHE_OPTS allow you to specifie additional options\n" 4920 "without having to mess with the makefiles. These are appended with " 4921 "a --kObjCache-options between them and the command args.\n" 696 "syntax: kLibTweaker [-v|--verbose] [--clear-timestamps] <lib>\n" 4922 697 "\n"); 4923 698 return 0; … … 4927 702 int main(int argc, char **argv) 4928 703 { 4929 PKOBJCACHE pCache;4930 PKOCENTRY pEntry;4931 4932 const char *pszCacheDir = getenv("KOBJCACHE_DIR");4933 const char *pszCacheName = NULL;4934 const char *pszCacheFile = NULL;4935 const char *pszEntryFile = NULL;4936 4937 const char **papszArgvPreComp = NULL;4938 unsigned cArgvPreComp = 0;4939 const char *pszPreCompName = NULL;4940 int fRedirPreCompStdOut = 0;4941 4942 const char **papszArgvCompile = NULL;4943 unsigned cArgvCompile = 0;4944 const char *pszObjName = NULL;4945 int fRedirCompileStdIn = 0;4946 const char *pszNmPipeCompile = NULL;4947 4948 const char *pszMakeDepFilename = NULL;4949 int fMakeDepFixCase = 0;4950 int fMakeDepGenStubs = 0;4951 int fMakeDepQuiet = 0;4952 int fOptimizePreprocessorOutput = 0;4953 4954 const char *pszTarget = NULL;4955 4956 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;4957 4958 size_t cch;4959 704 char *psz; 4960 705 int i; 4961 706 4962 SetErrorPrefix("kObjCache"); 707 int fClearTimestamps = 0; 708 int fFillNullThunkData = 0; 709 const char *pszLib = NULL; 710 711 SetErrorPrefix("kLibTweaker"); 4963 712 4964 713 /* 4965 714 * Arguments passed in the environmnet? 4966 715 */ 4967 psz = getenv("K OBJCACHE_OPTS");716 psz = getenv("KLIBTWEAKER_OPTS"); 4968 717 if (psz) 4969 AppendArgs(&argc, &argv, psz, "--kObjCache-options"); 718 AppendArgs(&argc, &argv, psz, NULL); 719 720 /** @todo Add the capability to produce import/stub libraries from ELF shared 721 * objects that we can use while linking and break up linking dependencies 722 * (i.e. not relink everything just because something in VBoxRT change that 723 * didn't make any difference to the symbols it exports). */ 4970 724 4971 725 /* … … 4973 727 */ 4974 728 if (argc <= 1) 4975 return usage(stderr) ;729 return usage(stderr) + 1; 4976 730 for (i = 1; i < argc; i++) 4977 731 { 4978 if (!strcmp(argv[i], "--kObjCache-cpp")) 4979 { 4980 enmMode = kOC_CppArgv; 4981 if (!pszPreCompName) 4982 { 4983 if (++i >= argc) 4984 return SyntaxError("--kObjCache-cpp requires an object filename!\n"); 4985 pszPreCompName = argv[i]; 4986 } 4987 } 4988 else if (!strcmp(argv[i], "--kObjCache-cc")) 4989 { 4990 enmMode = kOC_CcArgv; 4991 if (!pszObjName) 4992 { 4993 if (++i >= argc) 4994 return SyntaxError("--kObjCache-cc requires an preprocessor output filename!\n"); 4995 pszObjName = argv[i]; 4996 } 4997 } 4998 else if (!strcmp(argv[i], "--kObjCache-both")) 4999 enmMode = kOC_BothArgv; 5000 else if (!strcmp(argv[i], "--kObjCache-options")) 5001 enmMode = kOC_Options; 732 if (!strcmp(argv[i], "--clear-timestamps")) 733 fClearTimestamps = 1; 734 else if (!strcmp(argv[i], "--fill-null_thunk_data")) 735 fFillNullThunkData = 1; 736 /* Standard stuff: */ 5002 737 else if (!strcmp(argv[i], "--help")) 5003 738 return usage(stderr); 5004 else if (enmMode != kOC_Options)5005 {5006 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)5007 {5008 if (!(cArgvPreComp % 16))5009 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));5010 papszArgvPreComp[cArgvPreComp++] = argv[i];5011 papszArgvPreComp[cArgvPreComp] = NULL;5012 }5013 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)5014 {5015 if (!(cArgvCompile % 16))5016 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));5017 papszArgvCompile[cArgvCompile++] = argv[i];5018 papszArgvCompile[cArgvCompile] = NULL;5019 }5020 }5021 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--entry-file"))5022 {5023 if (i + 1 >= argc)5024 return SyntaxError("%s requires a cache entry filename!\n", argv[i]);5025 pszEntryFile = argv[++i];5026 }5027 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--cache-file"))5028 {5029 if (i + 1 >= argc)5030 return SyntaxError("%s requires a cache filename!\n", argv[i]);5031 pszCacheFile = argv[++i];5032 }5033 else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--name"))5034 {5035 if (i + 1 >= argc)5036 return SyntaxError("%s requires a cache name!\n", argv[i]);5037 pszCacheName = argv[++i];5038 }5039 else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--cache-dir"))5040 {5041 if (i + 1 >= argc)5042 return SyntaxError("%s requires a cache directory!\n", argv[i]);5043 pszCacheDir = argv[++i];5044 }5045 else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--target"))5046 {5047 if (i + 1 >= argc)5048 return SyntaxError("%s requires a target platform/arch name!\n", argv[i]);5049 pszTarget = argv[++i];5050 }5051 else if (!strcmp(argv[i], "--named-pipe-compile"))5052 {5053 if (i + 1 >= argc)5054 return SyntaxError("%s requires a pipe name!\n", argv[i]);5055 pszNmPipeCompile = argv[++i];5056 fRedirCompileStdIn = 0;5057 }5058 else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--make-dep-file"))5059 {5060 if (i + 1 >= argc)5061 return SyntaxError("%s requires a filename!\n", argv[i]);5062 pszMakeDepFilename = argv[++i];5063 }5064 else if (!strcmp(argv[i], "--make-dep-fix-case"))5065 fMakeDepFixCase = 1;5066 else if (!strcmp(argv[i], "--make-dep-gen-stubs"))5067 fMakeDepGenStubs = 1;5068 else if (!strcmp(argv[i], "--make-dep-quiet"))5069 fMakeDepQuiet = 1;5070 else if (!strcmp(argv[i], "-O1") || !strcmp(argv[i], "--optimize-1"))5071 fOptimizePreprocessorOutput = 1;5072 else if (!strcmp(argv[i], "-O2") || !strcmp(argv[i], "--optimize-2"))5073 fOptimizePreprocessorOutput = 1 | 2;5074 else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru"))5075 fRedirPreCompStdOut = fRedirCompileStdIn = 1;5076 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))5077 fRedirPreCompStdOut = 1;5078 739 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) 5079 740 g_cVerbosityLevel++; … … 5082 743 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?") 5083 744 || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?") || !strcmp(argv[i], "/help")) 5084 { 5085 usage(stdout); 5086 return 0; 5087 } 745 return usage(stdout); 5088 746 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) 5089 747 { 5090 printf("k ObjCache- kBuild version %d.%d.%d ($Revision$)\n"5091 "Copyright (c) 2007-201 2knut st. osmundsen\n",748 printf("kLibTweaker - kBuild version %d.%d.%d ($Revision$)\n" 749 "Copyright (c) 2007-2015 knut st. osmundsen\n", 5092 750 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH); 5093 751 return 0; 5094 752 } 753 else if (!strcmp(argv[i], "--")) 754 { 755 i++; 756 if (i == argc) 757 return SyntaxError("No library given!\n"); 758 if (i + 1 != argc || pszLib) 759 return SyntaxError("Only one library can be tweaked at a time!\n"); 760 pszLib = argv[i]; 761 break; 762 } 763 else if (argv[i][0] == '-') 764 return SyntaxError("Doesn't grok '%s'!\n", argv[i]); 765 else if (!pszLib) 766 pszLib = argv[i]; 5095 767 else 5096 return SyntaxError("Doesn't grok '%s'!\n", argv[i]); 5097 } 5098 if (!pszEntryFile) 5099 return SyntaxError("No cache entry filename (-f)!\n"); 5100 if (!pszTarget) 5101 return SyntaxError("No target name (-t)!\n"); 5102 if (!cArgvCompile) 5103 return SyntaxError("No compiler arguments (--kObjCache-cc)!\n"); 5104 if (!cArgvPreComp) 5105 return SyntaxError("No preprocessor arguments (--kObjCache-cc)!\n"); 5106 5107 /* 5108 * Calc the cache file name. 5109 * It's a bit messy since the extension has to be replaced. 5110 */ 5111 if (!pszCacheFile) 5112 { 5113 if (!pszCacheDir) 5114 return SyntaxError("No cache dir (-d / KOBJCACHE_DIR) and no cache filename!\n"); 5115 if (!pszCacheName) 5116 { 5117 psz = (char *)FindFilenameInPath(pszEntryFile); 5118 if (!*psz) 5119 return SyntaxError("The cache file (-f) specifies a directory / nothing!\n"); 5120 cch = psz - pszEntryFile; 5121 pszCacheName = memcpy(xmalloc(cch + 5), psz, cch + 1); 5122 psz = strrchr(pszCacheName, '.'); 5123 if (!psz || psz <= pszCacheName) 5124 psz = (char *)pszCacheName + cch; 5125 memcpy(psz, ".koc", sizeof(".koc")); 5126 } 5127 pszCacheFile = MakePathFromDirAndFile(pszCacheName, pszCacheDir); 5128 } 5129 5130 /* 5131 * Create and initialize the two objects we'll be working on. 5132 * 5133 * We're supposed to be the only ones actually writing to the local file, 5134 * so it's perfectly fine to read it here before we lock it. This simplifies 5135 * the detection of object name and compiler argument changes. 5136 */ 5137 SetErrorPrefix("kObjCache - %s", FindFilenameInPath(pszCacheFile)); 5138 pCache = kObjCacheCreate(pszCacheFile); 5139 5140 pEntry = kOCEntryCreate(pszEntryFile); 5141 kOCEntryRead(pEntry); 5142 kOCEntrySetCppName(pEntry, pszPreCompName); 5143 kOCEntrySetCompileObjName(pEntry, pszObjName); 5144 kOCEntrySetCompileArgv(pEntry, papszArgvCompile, cArgvCompile); 5145 kOCEntrySetTarget(pEntry, pszTarget); 5146 kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn, pszNmPipeCompile); 5147 kOCEntrySetDepFilename(pEntry, pszMakeDepFilename, fMakeDepFixCase, fMakeDepQuiet, fMakeDepGenStubs); 5148 kOCEntrySetOptimizations(pEntry, fOptimizePreprocessorOutput); 5149 5150 /* 5151 * Open (& lock) the two files and do validity checks and such. 5152 */ 5153 kObjCacheLock(pCache); 5154 if ( kObjCacheIsNew(pCache) 5155 && kOCEntryNeedsCompiling(pEntry)) 5156 { 5157 /* 5158 * Both files are missing/invalid. 5159 * Optimize this path as it is frequently used when making a clean build. 5160 */ 5161 kObjCacheUnlock(pCache); 5162 InfoMsg(1, "doing full compile\n"); 5163 kOCEntryPreProcessAndCompile(pEntry, papszArgvPreComp, cArgvPreComp); 5164 kObjCacheLock(pCache); 5165 } 5166 else 5167 { 5168 /* 5169 * Do the preprocess (don't need to lock the cache file for this). 5170 */ 5171 kObjCacheUnlock(pCache); 5172 kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp); 5173 5174 /* 5175 * Check if we need to recompile. If we do, try see if the is a cache entry first. 5176 */ 5177 kOCEntryCalcRecompile(pEntry); 5178 if (kOCEntryNeedsCompiling(pEntry)) 5179 { 5180 PKOCENTRY pUseEntry; 5181 kObjCacheLock(pCache); 5182 kObjCacheRemoveEntry(pCache, pEntry); 5183 pUseEntry = kObjCacheFindMatchingEntry(pCache, pEntry); 5184 if (pUseEntry) 5185 { 5186 InfoMsg(1, "using cache entry '%s'\n", kOCEntryAbsPath(pUseEntry)); 5187 kOCEntryCopy(pEntry, pUseEntry); 5188 kOCEntryDestroy(pUseEntry); 5189 } 5190 else 5191 { 5192 kObjCacheUnlock(pCache); 5193 InfoMsg(1, "recompiling\n"); 5194 kOCEntryCompileIt(pEntry); 5195 kObjCacheLock(pCache); 5196 } 5197 } 5198 else 5199 { 5200 InfoMsg(1, "no need to recompile\n"); 5201 kObjCacheLock(pCache); 5202 } 5203 } 5204 5205 /* 5206 * Update the cache files. 5207 */ 5208 kObjCacheRemoveEntry(pCache, pEntry); 5209 kObjCacheInsertEntry(pCache, pEntry); 5210 kOCEntryWrite(pEntry); 5211 kObjCacheUnlock(pCache); 5212 kObjCacheDestroy(pCache); 5213 if (fOptimizePreprocessorOutput) 5214 { 5215 InfoMsg(3, "g_cbMemMoved=%#x (%d)\n", g_cbMemMoved, g_cbMemMoved); 5216 InfoMsg(3, "g_cMemMoves=%#x (%d)\n", g_cMemMoves, g_cMemMoves); 5217 } 5218 5219 return 0; 5220 } 5221 5222 5223 /** @page kObjCache Benchmarks. 5224 * 5225 * (2007-06-10) 5226 * 5227 * Mac OS X debug -j 3 cached clobber build (rm -Rf out ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1): 5228 * real 11m28.811s 5229 * user 13m59.291s 5230 * sys 3m24.590s 5231 * 5232 * Mac OS X debug -j 3 cached depend build [cdefs.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1): 5233 * real 1m26.895s 5234 * user 1m26.971s 5235 * sys 0m32.532s 5236 * 5237 * Mac OS X debug -j 3 cached depend build [err.h] (touch include/iprt/err.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1): 5238 * real 1m18.049s 5239 * user 1m20.462s 5240 * sys 0m27.887s 5241 * 5242 * Mac OS X release -j 3 cached clobber build (rm -Rf out/darwin.x86/release ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1 BUILD_TYPE=release): 5243 * real 13m27.751s 5244 * user 18m12.654s 5245 * sys 3m25.170s 5246 * 5247 * Mac OS X profile -j 3 cached clobber build (rm -Rf out/darwin.x86/profile ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1 BUILD_TYPE=profile): 5248 * real 9m9.720s 5249 * user 8m53.005s 5250 * sys 2m13.110s 5251 * 5252 * Mac OS X debug -j 3 clobber build (rm -Rf out/darwin.x86/debug ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug): 5253 * real 10m18.129s 5254 * user 12m52.687s 5255 * sys 2m51.277s 5256 * 5257 * Mac OS X debug -j 3 debug build [cdefs.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug): 5258 * real 4m46.147s 5259 * user 5m27.087s 5260 * sys 1m11.775s 5261 * 5262 * Mac OS X debug -j 3 debug build [err.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug): 5263 * real 4m17.572s 5264 * user 5m7.450s 5265 * sys 1m3.450s 5266 * 5267 * Mac OS X release -j 3 clobber build (rm -Rf out/darwin.x86/release ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=release): 5268 * real 12m14.742s 5269 * user 17m11.794s 5270 * sys 2m51.454s 5271 * 5272 * Mac OS X profile -j 3 clobber build (rm -Rf out/darwin.x86/profile ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=profile): 5273 * real 12m33.821s 5274 * user 17m35.086s 5275 * sys 2m53.312s 5276 * 5277 * Note. The profile build can pick object files from the release build. 5278 * (all with KOBJCACHE_OPTS=-v; which means a bit more output and perhaps a second or two slower.) 5279 */ 5280 768 return SyntaxError("Only one library can be tweaked at a time!\n"); 769 } 770 if (!pszLib) 771 return SyntaxError("No library given!\n"); 772 773 return kLibTweakerDoIt(pszLib, fClearTimestamps, fFillNullThunkData); 774 } 775
Note:
See TracChangeset
for help on using the changeset viewer.