VirtualBox

Changeset 1040 in kBuild


Ignore:
Timestamp:
Jun 8, 2007 2:08:10 AM (18 years ago)
Author:
bird
Message:

Extending the cache to. The code doesn't work (haven't even run it yet) and is therefore disabled.

Location:
trunk/src/kObjCache
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kObjCache/Makefile.kmk

    r1001 r1040  
    2929include $(PATH_KBUILD)/subheader.kmk
    3030
     31ifeq ($(USERNAME),bird) # disable it for now.
    3132PROGRAMS += kObjCache
     33endif
    3234kObjCache_TEMPLATE = BIN
    3335kObjCache_SOURCES = kObjCache.c
  • trunk/src/kObjCache/kObjCache.c

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette