]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Win32 support
authorRamiro Polla <ramiro.polla@gmail.com>
Fri, 16 Jul 2010 19:56:43 +0000 (16:56 -0300)
committerRamiro Polla <ramiro.polla@gmail.com>
Fri, 16 Jul 2010 19:56:43 +0000 (16:56 -0300)
ccache.h
execute.c
manual.txt
test.sh
util.c

index 92247326cac84a57c623e94ad6d03e6619c2136c..55f8393348934264b219574dd7eb03014c74c68d 100644 (file)
--- a/ccache.h
+++ b/ccache.h
@@ -195,6 +195,23 @@ typedef int (*COMPAR_FN_T)(const void *, const void *);
 #undef HAVE_MKSTEMP
 #endif
 
-#define PATH_DELIM ":"
+#ifdef _WIN32
+int win32execute(char *path, char **argv, int doreturn,
+                 const char *path_stdout, const char *path_stderr);
+#    ifndef _WIN32_WINNT
+#    define _WIN32_WINNT 0x0501
+#    endif
+#    include <windows.h>
+#    define mkdir(a,b) mkdir(a)
+#    define link(src,dst) (CreateHardLink(dst,src,NULL) ? 0 : -1)
+#    define lstat(a,b) stat(a,b)
+#    define execv(a,b) win32execute(a,b,0,NULL,NULL)
+#    define execute(a,b,c) win32execute(*(a),a,1,b,c)
+#    define PATH_DELIM ";"
+#    define F_RDLCK 0
+#    define F_WRLCK 0
+#else
+#    define PATH_DELIM ":"
+#endif
 
 #endif /* ifndef CCACHE_H */
index 0d689a30a643e6c42123c13767967fe10c5b9719..3a8b34afc9bece93265d78bb9a02a498d544af3f 100644 (file)
--- a/execute.c
+++ b/execute.c
 static char *
 find_executable_in_path(const char *name, const char *exclude_name, char *path);
 
+#ifdef _WIN32
+/*
+ * Re-create a win32 command line string based on **argv.
+ * http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+ */
+static char *argvtos(char *prefix, char **argv)
+{
+       char *arg;
+       char *ptr;
+       char *str;
+       int l = 0;
+       int i, j;
+
+       i = 0;
+       arg = prefix ? prefix : argv[i++];
+       do {
+               int bs = 0;
+               for (j = 0; arg[j]; j++) {
+                       switch (arg[j]) {
+                               case '\\':
+                                       bs++;
+                                       break;
+                               case '"':
+                                       bs = (bs << 1) + 1;
+                               default:
+                                       l += bs + 1;
+                                       bs = 0;
+                       }
+               }
+               l += (bs << 1) + 3;
+       } while ((arg = argv[i++]));
+
+       str = ptr = malloc(l + 1);
+       if (str == NULL)
+               return NULL;
+
+       i = 0;
+       arg = prefix ? prefix : argv[i++];
+       do {
+               int bs = 0;
+               *ptr++ = '"';
+               for (j = 0; arg[j]; j++) {
+                       switch (arg[j]) {
+                               case '\\':
+                                       bs++;
+                                       break;
+                               case '"':
+                                       bs = (bs << 1) + 1;
+                               default:
+                                       while (bs && bs--)
+                                               *ptr++ = '\\';
+                                       *ptr++ = arg[j];
+                       }
+               }
+               bs <<= 1;
+               while (bs && bs--)
+                       *ptr++ = '\\';
+               *ptr++ = '"';
+               *ptr++ = ' ';
+       } while ((arg = argv[i++]));
+       ptr[-1] = '\0';
+
+       return str;
+}
+
+int win32execute(char *path, char **argv, int doreturn,
+                 const char *path_stdout, const char *path_stderr)
+{
+       PROCESS_INFORMATION pi;
+       STARTUPINFO si;
+       BOOL ret;
+       DWORD exitcode;
+       char *path_env;
+       char *sh = NULL;
+       char *args;
+       const char *ext;
+
+       memset(&pi, 0x00, sizeof(pi));
+       memset(&si, 0x00, sizeof(si));
+
+       ext = get_extension(path);
+       if (ext && !strcasecmp(ext, ".sh") && (path_env = getenv("PATH")))
+               sh = find_executable_in_path("sh.exe", NULL, path_env);
+       if (!sh && getenv("CCACHE_DETECT_SHEBANG")) {
+               /* Detect shebang. */
+               FILE *fp;
+               fp = fopen(path, "r");
+               if (fp) {
+                       char buf[10];
+                       fgets(buf, sizeof(buf), fp);
+                       buf[9] = 0;
+                       if (!strcmp(buf, "#!/bin/sh") && (path_env = getenv("PATH")))
+                               sh = find_executable_in_path("sh.exe", NULL, path_env);
+                       fclose(fp);
+               }
+       }
+       if (sh)
+               path = sh;
+
+       si.cb = sizeof(STARTUPINFO);
+       if (path_stdout) {
+               SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+               si.hStdOutput = CreateFile(path_stdout, GENERIC_WRITE, 0, &sa,
+                                          CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY |
+                                          FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+               si.hStdError  = CreateFile(path_stderr, GENERIC_WRITE, 0, &sa,
+                                          CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY |
+                                          FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+               si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
+               si.dwFlags    = STARTF_USESTDHANDLES;
+               if (si.hStdOutput == INVALID_HANDLE_VALUE ||
+                       si.hStdError  == INVALID_HANDLE_VALUE)
+                       return -1;
+       }
+       args = argvtos(sh, argv);
+       ret = CreateProcess(path, args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);
+       free(args);
+       if (path_stdout) {
+               CloseHandle(si.hStdOutput);
+               CloseHandle(si.hStdError);
+       }
+       if (ret == 0)
+               return -1;
+       WaitForSingleObject(pi.hProcess, INFINITE);
+       GetExitCodeProcess(pi.hProcess, &exitcode);
+       CloseHandle(pi.hProcess);
+       CloseHandle(pi.hThread);
+       if (!doreturn)
+               exit(exitcode);
+       return exitcode;
+}
+
+#else
+
 /*
   execute a compiler backend, capturing all output to the given paths
   the full path to the compiler to run is in argv[0]
@@ -81,6 +215,7 @@ int execute(char **argv,
 
        return WEXITSTATUS(status);
 }
+#endif
 
 
 /*
@@ -116,6 +251,19 @@ find_executable_in_path(const char *name, const char *exclude_name, char *path)
        /* search the path looking for the first compiler of the right name
           that isn't us */
        for (tok=strtok(path, PATH_DELIM); tok; tok = strtok(NULL, PATH_DELIM)) {
+#ifdef _WIN32
+               char namebuf[MAX_PATH];
+               int ret = SearchPath(tok, name, ".exe",
+                                    sizeof(namebuf), namebuf, NULL);
+               if (!ret)
+                       ret = SearchPath(tok, name, NULL,
+                                    sizeof(namebuf), namebuf, NULL);
+               (void) exclude_name;
+               if (ret) {
+                       free(path);
+                       return x_strdup(namebuf);
+               }
+#else
                struct stat st1, st2;
                char *fname;
                x_asprintf(&fname, "%s/%s", tok, name);
@@ -146,6 +294,7 @@ find_executable_in_path(const char *name, const char *exclude_name, char *path)
                        return fname;
                }
                free(fname);
+#endif
        }
 
        free(path);
index a821a0eeeec48dfe6d332fb1e2e36b0fb9bf009b..aa22c80851374e7e7070ba01fdbc499caa990752 100644 (file)
@@ -218,6 +218,12 @@ are doing.
     intermediate filename extensions used in this optimisation, in which case
     this option could allow ccache to be used anyway.
 
+*CCACHE_DETECT_SHEBANG*::
+    The *CCACHE_DETECT_SHEBANG* environment variable only has meaning on
+    Windows. It instructs ccache to open the executable file to detect the
+    *#!/bin/sh* string, in which case ccache will search for *sh.exe* in
+    *PATH* and use that to launch the executable.
+
 *CCACHE_DIR*::
 
     The *CCACHE_DIR* environment variable specifies where ccache will keep its
diff --git a/test.sh b/test.sh
index 7abeeea0af7467a647282c17544be17cc04c8bf4..d07b95f2f8b64fe24b6f8975b6c7ad07ca591d79 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -1560,6 +1560,8 @@ cleanup
 host_os="`uname -s`"
 case $host_os in
     *MINGW*|*mingw*)
+        export CCACHE_DETECT_SHEBANG
+        CCACHE_DETECT_SHEBANG=1
         DEVNULL=NUL
         PATH_DELIM=";"
         ;;
diff --git a/util.c b/util.c
index de87746a3f7e7e841e15af23309a4e2362b38f90..61fb541bebf7cde7ea0b5d3e73b34eef7ad58ebb 100644 (file)
--- a/util.c
+++ b/util.c
 #include <sys/time.h>
 #endif
 
+#ifdef _WIN32
+#include <windows.h>
+#include <sys/locking.h>
+#endif
+
 static FILE *logfile;
 
 static int init_log(void)
@@ -186,7 +191,9 @@ int copy_file(const char *src, const char *dest, int compress_dest)
        char buf[10240];
        int n, ret;
        char *tmp_name;
+#ifndef _WIN32
        mode_t mask;
+#endif
        struct stat st;
        int errnum;
 
@@ -275,10 +282,12 @@ int copy_file(const char *src, const char *dest, int compress_dest)
                gz_out = NULL;
        }
 
+#ifndef _WIN32
        /* get perms right on the tmp file */
        mask = umask(0);
        fchmod(fd_out, 0666 & ~mask);
        umask(mask);
+#endif
 
        /* the close can fail on NFS if out of space */
        if (close(fd_out) == -1) {
@@ -614,6 +623,10 @@ char *basename(const char *s)
        char *p;
        p = strrchr(s, '/');
        if (p) s = p + 1;
+#ifdef _WIN32
+       p = strrchr(s, '\\');
+       if (p) s = p + 1;
+#endif
 
        return x_strdup(s);
 }
@@ -622,8 +635,14 @@ char *basename(const char *s)
 char *dirname(char *s)
 {
        char *p;
+       char *p2 = NULL;
        s = x_strdup(s);
        p = strrchr(s, '/');
+#ifdef _WIN32
+       p2 = strrchr(s, '\\');
+#endif
+       if (p < p2)
+               p = p2;
        if (p) {
                *p = 0;
                return s;
@@ -665,6 +684,10 @@ char *remove_extension(const char *path)
 
 static int lock_fd(int fd, short type)
 {
+#ifdef _WIN32
+       (void) type;
+       return _locking(fd, _LK_NBLCK, 1);
+#else
        struct flock fl;
        int ret;
 
@@ -680,6 +703,7 @@ static int lock_fd(int fd, short type)
                ret = fcntl(fd, F_SETLKW, &fl);
        } while (ret == -1 && errno == EINTR);
        return ret;
+#endif
 }
 
 int read_lock_fd(int fd)
@@ -695,12 +719,16 @@ int write_lock_fd(int fd)
 /* return size on disk of a file */
 size_t file_size(struct stat *st)
 {
+#ifdef _WIN32
+       return (st->st_size + 1023) & ~1023;
+#else
        size_t size = st->st_blocks * 512;
        if ((size_t)st->st_size > size) {
                /* probably a broken stat() call ... */
                size = (st->st_size + 1023) & ~1023;
        }
        return size;
+#endif
 }
 
 
@@ -758,6 +786,7 @@ size_t value_units(const char *s)
 }
 
 
+#ifndef _WIN32
 /*
   a sane realpath() function, trying to cope with stupid path limits and
   a broken API
@@ -800,6 +829,7 @@ char *x_realpath(const char *path)
        free(ret);
        return NULL;
 }
+#endif /* !_WIN32 */
 
 /* a getcwd that will returns an allocated buffer */
 char *gnu_getcwd(void)
@@ -887,7 +917,18 @@ char *get_cwd(void)
 int
 compare_executable_name(const char *s1, const char *s2)
 {
+#ifdef _WIN32
+       int ret = !strcasecmp(s1, s2);
+       if (!ret) {
+               char *tmp;
+               x_asprintf(&tmp, "%s.exe", s2);
+               ret = !strcasecmp(s1, tmp);
+               free(tmp);
+       }
+       return ret;
+#else
        return !strcmp(s1, s2);
+#endif
 }
 
 /*
@@ -956,7 +997,11 @@ char *get_relative_path(const char *from, const char *to)
 int
 is_absolute_path(const char *path)
 {
+#ifdef _WIN32
+       return path[0] && path[1] == ':';
+#else
        return path[0] == '/';
+#endif
 }
 
 /*
@@ -967,6 +1012,10 @@ is_full_path(const char *path)
 {
        if (strchr(path, '/'))
                return 1;
+#ifdef _WIN32
+       if (strchr(path, '\\'))
+               return 1;
+#endif
        return 0;
 }
 
@@ -993,6 +1042,35 @@ void *x_fmmap(const char *fname, off_t *size, const char *errstr)
        struct stat st;
        void *data = (void *) -1;
        int fd = -1;
+#ifdef _WIN32
+       HANDLE section;
+       HANDLE file;
+       file = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, NULL,
+                         OPEN_EXISTING, 0, NULL);
+       if (file == INVALID_HANDLE_VALUE) {
+               cc_log("Failed to open %s%s", errstr, fname);
+               goto error;
+       }
+       fd = _open_osfhandle((intptr_t) file, O_RDONLY | O_BINARY);
+       if (fd == -1) {
+               cc_log("Failed to open %s%s", errstr, fname);
+               CloseHandle(file);
+               goto error;
+       }
+       if (fstat(fd, &st) == -1) {
+               cc_log("Failed to fstat %s%s", errstr, fname);
+               CloseHandle(file);
+               goto error;
+       }
+       section = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
+       CloseHandle(file);
+       if (!section) {
+               cc_log("Failed to mmap %s%s", errstr, fname);
+               goto error;
+       }
+       data = MapViewOfFile(section, FILE_MAP_READ, 0, 0, 0);
+       CloseHandle(section);
+#else
        fd = open(fname, O_RDONLY | O_BINARY);
        if (fd == -1) {
                cc_log("Failed to open %s %s", errstr, fname);
@@ -1008,6 +1086,7 @@ void *x_fmmap(const char *fname, off_t *size, const char *errstr)
        if (data == (void *) -1) {
                cc_log("Failed to mmap %s %s", errstr, fname);
        }
+#endif
        *size = st.st_size;
 error:
        return data;
@@ -1018,5 +1097,10 @@ error:
  */
 int x_munmap(void *addr, size_t length)
 {
+#ifdef _WIN32
+       (void) length;
+       return UnmapViewOfFile(addr) ? 0 : -1;
+#else
        return munmap(addr, length);
+#endif
 }