Index: trunk/src/kObjCache/kObjCache.c
===================================================================
--- trunk/src/kObjCache/kObjCache.c	(revision 2455)
+++ trunk/src/kObjCache/kObjCache.c	(revision 2456)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ * Copyright (c) 2007-2011 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
  *
  * This file is part of kBuild.
@@ -50,4 +50,5 @@
 #  include <unistd.h>
 #  include <sys/wait.h>
+#  include <sys/time.h>
 # endif
 # if defined(_MSC_VER)
@@ -64,4 +65,5 @@
 # include <unistd.h>
 # include <sys/wait.h>
+# include <sys/time.h>
 # ifndef O_BINARY
 #  define O_BINARY 0
@@ -235,7 +237,13 @@
 char *xstrdup(const char *pszIn)
 {
-    char *psz = strdup(pszIn);
-    if (!psz)
-        FatalDie("out of memory (%d)\n", (int)strlen(pszIn));
+    char *psz;
+    if (pszIn)
+    {
+        psz = strdup(pszIn);
+        if (!psz)
+            FatalDie("out of memory (%d)\n", (int)strlen(pszIn));
+    }
+    else
+        psz = NULL;
     return psz;
 }
@@ -252,4 +260,25 @@
 
 
+
+
+/**
+ * Returns a millisecond timestamp.
+ *
+ * @returns Millisecond timestamp.
+ */
+static uint32_t NowMs(void)
+{
+#if defined(__WIN__)
+    return GetTickCount();
+#else
+    int             iSavedErrno = errno;
+    struct timeval  tv          = {0, 0};
+
+    gettimeofday(&tv, NULL);
+    errno = iSavedErrno;
+
+    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#endif
+}
 
 
@@ -1051,4 +1080,7 @@
     /** Whether the compiler runs in piped mode (precompiler output on stdin). */
     unsigned fPipedCompile;
+    /** The name of the pipe that we're feeding the precompiled output to the
+     *  compiler via.  This is a Windows thing. */
+    char *pszNmPipeCompile;
     /** Cache entry key that's used for some quick digest validation. */
     uint32_t uKey;
@@ -1065,4 +1097,7 @@
         /** The precompiler output checksums that will produce the cached object. */
         KOCSUM SumHead;
+        /** The number of milliseconds spent precompiling. */
+        uint32_t cMsCpp;
+
         /** The object filename (relative to the cache file). */
         char *pszObjName;
@@ -1073,4 +1108,8 @@
         /** The checksum of the compiler argument vector. */
         KOCSUM SumCompArgv;
+        /** The number of milliseconds spent compiling. */
+        uint32_t cMsCompile;
+        /** @todo need a list of additional output files for MSC. */
+
         /** The target os/arch identifier. */
         char *pszTarget;
@@ -1132,6 +1171,8 @@
 static void kOCEntryDestroy(PKOCENTRY pEntry)
 {
+    /** @todo free pEntry->pszName? */
     free(pEntry->pszDir);
     free(pEntry->pszAbsPath);
+    free(pEntry->pszNmPipeCompile);
 
     kOCSumDeleteChain(&pEntry->New.SumHead);
@@ -1213,5 +1254,7 @@
          */
         if (    !fgets(g_szLine, sizeof(g_szLine), pFile)
-            ||  strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n"))
+            ||  (   strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n")
+                 && strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.1\n"))
+           )
         {
             InfoMsg(2, "bad cache file (magic)\n");
@@ -1270,4 +1313,13 @@
                     kOCSumAdd(&pEntry->Old.SumHead, &Sum);
                 }
+                else if (!strcmp(g_szLine, "cpp-ms"))
+                {
+                    char *pszNext;
+                    if ((fBad = pEntry->Old.cMsCpp != 0))
+                        break;
+                    pEntry->Old.cMsCpp = strtoul(pszVal, &pszNext, 0);
+                    if ((fBad = pszNext && *pszNext))
+                        break;
+                }
                 else if (!strcmp(g_szLine, "cc-argc"))
                 {
@@ -1290,4 +1342,13 @@
                         break;
                     if ((fBad = kOCSumInitFromString(&pEntry->Old.SumCompArgv, pszVal)))
+                        break;
+                }
+                else if (!strcmp(g_szLine, "cc-ms"))
+                {
+                    char *pszNext;
+                    if ((fBad = pEntry->Old.cMsCompile != 0))
+                        break;
+                    pEntry->Old.cMsCompile = strtoul(pszVal, &pszNext, 0);
+                    if ((fBad = pszNext && *pszNext))
                         break;
                 }
@@ -1405,10 +1466,12 @@
         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)
 
-    fprintf(pFile, "magic=kObjCacheEntry-v0.1.0\n");
+    fprintf(pFile, "magic=kObjCacheEntry-v0.1.1\n");
     CHECK_LEN(fprintf(pFile, "target=%s\n", pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget));
     CHECK_LEN(fprintf(pFile, "key=%lu\n", (unsigned long)pEntry->uKey));
-    CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName));
-    CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName));
-    CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->New.pszCppName ? pEntry->New.cbCpp : pEntry->Old.cbCpp));
+    CHECK_LEN(fprintf(pFile, "obj=%s\n",        pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName));
+    CHECK_LEN(fprintf(pFile, "cpp=%s\n",        pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName));
+    CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n",  pEntry->New.pszCppName ? pEntry->New.cbCpp      : pEntry->Old.cbCpp));
+    CHECK_LEN(fprintf(pFile, "cpp-ms=%lu\n",    pEntry->New.pszCppName ? pEntry->New.cMsCpp     : pEntry->Old.cMsCpp));
+    CHECK_LEN(fprintf(pFile, "cc-ms=%lu\n",     pEntry->New.pszCppName ? pEntry->New.cMsCompile : pEntry->Old.cMsCompile));
 
     if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
@@ -1585,9 +1648,13 @@
  * @param   fRedirPreCompStdOut     Whether the precompiler is in piped mode.
  * @param   fRedirCompileStdIn      Whether the compiler is in piped mode.
- */
-static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn)
+ * @param   pszNmPipeCompile        The name of the named pipe to use to feed
+ *                                  the microsoft compiler.
+ */
+static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn,
+                                 const char *pszNmPipeCompile)
 {
     pEntry->fPipedPreComp = fRedirPreCompStdOut;
-    pEntry->fPipedCompile = fRedirCompileStdIn;
+    pEntry->fPipedCompile = fRedirCompileStdIn || pszNmPipeCompile;
+    pEntry->pszNmPipeCompile = xstrdup(pszNmPipeCompile);
 }
 
@@ -1598,7 +1665,12 @@
  *
  * @param   papszArgv       Argument vector. The cArgv element is NULL.
+ * @param   pcMs            The cache entry member use for time keeping.  This
+ *                          will be set to the current timestamp.
  * @param   cArgv           The number of arguments in the vector.
- */
-static void kOCEntrySpawn(PCKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg, const char *pszStdOut)
+ * @param   pszMsg          Which operation this is, for use in messages.
+ * @param   pszStdOut       Where to redirect standard out.
+ */
+static void kOCEntrySpawn(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
+                          const char *pszMsg, const char *pszStdOut)
 {
 #if defined(__OS2__) || defined(__WIN__)
@@ -1623,4 +1695,5 @@
     }
 
+    *pcMs = NowMs();
     errno = 0;
 # ifdef __WIN__
@@ -1629,4 +1702,5 @@
     rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
 # endif
+    *pcMs = NowMs() - *pcMs;
     if (rc < 0)
         FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
@@ -1643,5 +1717,8 @@
     int iStatus;
     pid_t pidWait;
-    pid_t pid = fork();
+    pid_t pid;
+
+    *pcMs = NowMs();
+    pid = fork();
     if (!pid)
     {
@@ -1673,4 +1750,5 @@
     while (pidWait < 0 && errno == EINTR)
         pidWait = waitpid(pid, &iStatus, 0);
+    *pcMs = NowMs() - *pcMs;
     if (pidWait != pid)
         FatalDie("%s - waitpid failed rc=%d: %s\n",
@@ -1690,4 +1768,6 @@
  *
  * @param   pEntry          The cache entry.
+ * @param   pcMs            The cache entry member use for time keeping.  This
+ *                          will be set to the current timestamp.
  * @param   papszArgv       Argument vector. The cArgv element is NULL.
  * @param   cArgv           The number of arguments in the vector.
@@ -1696,5 +1776,6 @@
  * @param   pszMsg          Message to start the info/error messages with.
  */
-static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, int fdStdIn, int fdStdOut, const char *pszMsg)
+static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
+                                int fdStdIn, int fdStdOut, const char *pszMsg)
 {
     pid_t pid;
@@ -1729,4 +1810,5 @@
      * Create the child process.
      */
+    *pcMs = NowMs();
 #if defined(__OS2__) || defined(__WIN__)
     errno = 0;
@@ -1777,8 +1859,10 @@
  *
  * @param   pEntry      The cache entry.
+ * @param   pcMs        The millisecond timestamp that should be convert to
+ *                      elapsed time.
  * @param   pid         The child to wait for.
  * @param   pszMsg      Message to start the info/error messages with.
  */
-static void kOCEntryWaitChild(PCKOCENTRY pEntry, pid_t pid, const char *pszMsg)
+static void kOCEntryWaitChild(PCKOCENTRY pEntry, uint32_t *pcMs, pid_t pid, const char *pszMsg)
 {
     int iStatus = -1;
@@ -1788,4 +1872,5 @@
 #ifdef __WIN__
     pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
+    *pcMs = NowMs() - *pcMs;
     if (pidWait == -1)
         FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno));
@@ -1796,4 +1881,5 @@
     while (pidWait < 0 && errno == EINTR)
         pidWait = waitpid(pid, &iStatus, 0);
+    *pcMs = NowMs() - *pcMs;
     if (pidWait != pid)
         FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno));
@@ -1813,10 +1899,29 @@
  * @param   pFDs            Where to store the two file descriptors.
  * @param   pszMsg          The operation message for info/error messages.
- */
-static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *pFDs, const char *pszMsg)
+ * @param   pszPipeName     The pipe name if it is supposed to be named. (Windows only.)
+ */
+static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *pFDs, const char *pszPipeName, const char *pszMsg)
 {
     pFDs[0] = pFDs[1] = -1;
 #if defined(__WIN__)
-    if (_pipe(pFDs, 0, _O_NOINHERIT | _O_BINARY) < 0)
+    if (pszPipeName)
+    {
+        HANDLE hPipe = CreateNamedPipeA(pszPipeName,
+                                       /*PIPE_ACCESS_OUTBOUND*/ PIPE_ACCESS_DUPLEX,
+                                       PIPE_READMODE_BYTE | PIPE_WAIT,
+                                       10 /* nMaxInstances */,
+                                       0x10000 /* nOutBuffer */,
+                                       0x10000 /* nInBuffer */,
+                                       NMPWAIT_WAIT_FOREVER,
+                                       NULL /* pSecurityAttributes */);
+
+        if (hPipe == INVALID_HANDLE_VALUE)
+            FatalDie("%s - CreateNamedPipe(%s) failed: %d\n", pszMsg, pszPipeName, GetLastError());
+
+        pFDs[1 /* write */] = _open_osfhandle((intptr_t)hPipe, _O_WRONLY | _O_TEXT | _O_NOINHERIT);
+        if (pFDs[1 /* write */] == -1)
+            FatalDie("%s - _open_osfhandle failed: %d\n", pszMsg, strerror(errno));
+    }
+    else if (_pipe(pFDs, 0, _O_NOINHERIT | _O_BINARY) < 0)
 #else
     if (pipe(pFDs) < 0)
@@ -1847,15 +1952,15 @@
     pid_t pid;
 
-    kOCEntryCreatePipe(pEntry, fds, pszMsg);
-    pid = kOCEntrySpawnChild(pEntry, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg);
+    kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg);
+    pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg);
 
     pfnConsumer(pEntry, fds[0 /* read */]);
 
-    kOCEntryWaitChild(pEntry, pid, pszMsg);
-}
-
-
-/**
- * Spawns a child that consumes input on stdin.
+    kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pid, pszMsg);
+}
+
+
+/**
+ * Spawns a child that consumes input on stdin or via a named pipe.
  *
  * @param   papszArgv       Argument vector. The cArgv element is NULL.
@@ -1871,10 +1976,14 @@
     pid_t pid;
 
-    kOCEntryCreatePipe(pEntry, fds, pszMsg);
-    pid = kOCEntrySpawnChild(pEntry, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg);
+    kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg);
+    pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg);
+#ifdef __WIN__
+    if (pEntry->pszNmPipeCompile && !ConnectNamedPipe((HANDLE)_get_osfhandle(fds[1 /* write */]), NULL))
+        FatalDie("compile - ConnectNamedPipe failed: %d\n", GetLastError());
+#endif
 
     pfnProducer(pEntry, fds[1 /* write */]);
 
-    kOCEntryWaitChild(pEntry, pid, pszMsg);
+    kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pid, pszMsg);
 }
 
@@ -1901,6 +2010,6 @@
      * The producer.
      */
-    kOCEntryCreatePipe(pEntry, fds, pszMsg);
-    pidConsumer = kOCEntrySpawnChild(pEntry, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg);
+    kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg);
+    pidConsumer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg);
     fdIn = fds[0 /* read */];
 
@@ -1908,6 +2017,6 @@
      * The consumer.
      */
-    kOCEntryCreatePipe(pEntry, fds, pszMsg);
-    pidProducer = kOCEntrySpawnChild(pEntry, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg);
+    kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg);
+    pidProducer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg);
     fdOut = fds[1 /* write */];
 
@@ -1920,6 +2029,6 @@
      * Reap the children.
      */
-    kOCEntryWaitChild(pEntry, pidProducer, pszMsg);
-    kOCEntryWaitChild(pEntry, pidConsumer, pszMsg);
+    kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pidProducer, pszMsg);
+    kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pidConsumer, pszMsg);
 }
 
@@ -2072,5 +2181,5 @@
          */
         InfoMsg(3, "precompiling -> '%s'...\n", pEntry->New.pszCppName);
-        kOCEntrySpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL);
+        kOCEntrySpawn(pEntry, &pEntry->New.cMsCpp, papszArgvPreComp, cArgvPreComp, "precompile", NULL);
         kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
         kOCEntryCalcChecksum(pEntry);
@@ -2159,5 +2268,17 @@
             if (errno == EINTR)
                 continue;
+#ifdef __WIN__ /* HACK */
+            if (   errno == EINVAL
+                && pEntry->pszNmPipeCompile
+                && DisconnectNamedPipe((HANDLE)_get_osfhandle(fdOut))
+                && ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL))
+            {
+                psz = pEntry->New.pszCppMapping;
+                cbLeft = (long)pEntry->New.cbCpp;
+            }
+            FatalDie("compile - write(%d,,%ld) failed: %s - _doserrno=%d\n", fdOut, cbLeft, strerror(errno), _doserrno);
+#else
             FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno));
+#endif
         }
         psz += cbWritten;
@@ -2202,6 +2323,6 @@
             kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
         InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
-        kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile,
-                              "compile", kOCEntryCompileProducer);
+        kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile,
+                              pEntry->New.cArgvCompile, "compile", kOCEntryCompileProducer);
     }
     else
@@ -2210,5 +2331,6 @@
             kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
         InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
-        kOCEntrySpawn(pEntry, (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile, "compile", NULL);
+        kOCEntrySpawn(pEntry, &pEntry->New.cMsCompile, (const char * const *)pEntry->New.papszArgvCompile,
+                      pEntry->New.cArgvCompile, "compile", NULL);
     }
 }
@@ -2227,4 +2349,7 @@
 static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut)
 {
+#ifdef __WIN__
+    unsigned fConnectedToCompiler = fdOut == -1 || pEntry->pszNmPipeCompile == NULL;
+#endif
     KOCSUMCTX Ctx;
     long cbLeft;
@@ -2259,4 +2384,9 @@
         psz[cbRead] = '\0';
         kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
+#ifdef __WIN__
+        if (   !fConnectedToCompiler
+            && !(fConnectedToCompiler = ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL)))
+            FatalDie("precompile|compile - ConnectNamedPipe failed: %d\n", GetLastError());
+#endif
         do
         {
@@ -3695,5 +3825,5 @@
             "            <-f|--file <local-cache-file>>\n"
             "            <-t|--target <target-name>>\n"
-            "            [-r|--redir-stdout] [-p|--passthru]\n"
+            "            [-r|--redir-stdout] [-p|--passthru] [--named-pipe-compile <pipename>]\n"
             "            --kObjCache-cpp <filename> <precompiler + args>\n"
             "            --kObjCache-cc <object> <compiler + args>\n"
@@ -3733,4 +3863,5 @@
     const char *pszObjName = NULL;
     int fRedirCompileStdIn = 0;
+    const char *pszNmPipeCompile;
 
     const char *pszTarget = NULL;
@@ -3831,4 +3962,11 @@
             pszTarget = argv[++i];
         }
+        else if (!strcmp(argv[i], "--named-pipe-compile"))
+        {
+            if (i + 1 >= argc)
+                return SyntaxError("%s requires a pipe name!\n", argv[i]);
+            pszNmPipeCompile = argv[++i];
+            fRedirCompileStdIn = 0;
+        }
         else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru"))
             fRedirPreCompStdOut = fRedirCompileStdIn = 1;
@@ -3848,5 +3986,5 @@
         {
             printf("kObjCache - kBuild version %d.%d.%d ($Revision$)\n"
-                   "Copyright (c) 2007-2009  knut st. osmundsen\n",
+                   "Copyright (c) 2007-2011 knut st. osmundsen\n",
                    KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
             return 0;
@@ -3903,5 +4041,5 @@
     kOCEntrySetTarget(pEntry, pszTarget);
     kOCEntrySetCppName(pEntry, pszPreCompName);
-    kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn);
+    kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn, pszNmPipeCompile);
 
     /*
