]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Rewrite absolute paths under CCACHE_BASEDIR to relative
authorJoel Rosdahl <joel@rosdahl.net>
Thu, 26 Nov 2009 22:36:19 +0000 (23:36 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Tue, 5 Jan 2010 17:53:02 +0000 (18:53 +0100)
NEWS
ccache.c
ccache.h
test.sh
util.c

diff --git a/NEWS b/NEWS
index abf2b3d2d97667507480b31549eab0d8d8aa7515..47f2744c3b122b3fdf57dbcbdeeff3579e0b8833 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,14 @@ New features and improvements:
     speedup will be higher when I/O is fast (e.g., when files are in the disk
     cache). The direct mode can be disabled by setting CCACHE_NODIRECT.
 
+  - When hashing the output from the preprocessor, absolute paths for which the
+    path specified by CCACHE_BASEDIR is a prefix are rewritten to relative.
+    Paths specified by -I and similar flags get the same treatment. This is
+    done to get cache hits even when compiling with -g and when using absolute
+    include directory paths. Absolute paths in the standard error text will
+    also be more accurate. CCACHE_BASEDIR defaults to the current working
+    directory.
+
   - Object files are now by default stored compressed in the cache. The runtime
     cost is negligible, and more files will fit in the ccache directory and in
     the disk cache. CCACHE_NOCOMPRESS can be set to disable object file
index 2dc1e5691c64094c2f1bf6e76e606d3e9957ee24..ef64fca2596e9f16fbed2bb99fc71e447bc1fe7a 100644 (file)
--- a/ccache.c
+++ b/ccache.c
@@ -30,6 +30,9 @@
 
 #include <getopt.h>
 
+/* current working directory taken from $PWD, or getcwd() if $PWD is bad */
+char *current_working_dir;
+
 /* the base cache directory */
 char *cache_dir = NULL;
 
@@ -39,6 +42,9 @@ char *temp_dir = NULL;
 /* the debug logfile name, if set */
 char *cache_logfile = NULL;
 
+/* base directory (from CCACHE_BASEDIR) */
+char *base_dir;
+
 /* the argument list after processing */
 static ARGS *stripped_args;
 
@@ -306,11 +312,28 @@ ignore:
        }
 }
 
+/*
+ * Make a relative path from CCACHE_BASEDIR to path. Takes over ownership of
+ * path. Caller frees.
+ */
+static char *make_relative_path(char *path)
+{
+       char *relpath;
+
+       if (!base_dir || strncmp(path, base_dir, strlen(base_dir)) != 0) {
+               return path;
+       }
+
+       relpath = get_relative_path(current_working_dir, path);
+       free(path);
+       return relpath;
+}
+
 /*
  * This function reads and hashes a file. While doing this, it also does these
  * things with preprocessor lines starting with a hash:
  *
- * - TODO: Makes include file paths that match CCACHE_RELDIRS relative.
+ * - Makes include file paths whose prefix is CCACHE_BASEDIR relative.
  * - Stores the paths of included files in the global variable included_files.
  */
 static void process_preprocessed_file(struct mdfour *hash, const char *path)
@@ -367,10 +390,12 @@ static void process_preprocessed_file(struct mdfour *hash, const char *path)
                        }
                        /* p and q span the include file path */
                        path = x_strndup(p, q - p);
-                       /* TODO: Rewrite path using CCACHE_RELDIRS here. */
+                       path = make_relative_path(path);
                        hash_string(hash, path);
                        if (enable_direct) {
                                remember_include_file(path, q - p);
+                       } else {
+                               free(path);
                        }
                        p = q;
                } else {
@@ -644,28 +669,36 @@ static int find_hash(ARGS *args, enum findhash_call_mode mode)
                        continue;
                }
 
-               if (mode == FINDHASH_CPP_MODE) {
-                       /* When using the preprocessor, some arguments don't
-                          contribute to the hash. The theory is that these
-                          arguments will change the output of -E if they are
-                          going to have any effect at all. */
-                       if (i < args->argc-1) {
-                               if (strcmp(args->argv[i], "-I") == 0 ||
-                                   strcmp(args->argv[i], "-include") == 0 ||
-                                   strcmp(args->argv[i], "-D") == 0 ||
-                                   strcmp(args->argv[i], "-idirafter") == 0 ||
-                                   strcmp(args->argv[i], "-isystem") == 0) {
-                                       i++;
-                                       continue;
-                               }
-                       }
-                       if (strncmp(args->argv[i], "-I", 2) == 0 ||
-                           strncmp(args->argv[i], "-D", 2) == 0 ||
-                           strncmp(args->argv[i], "-idirafter", 10) == 0 ||
-                           strncmp(args->argv[i], "-isystem", 8) == 0) {
+               /* When using the preprocessor, some arguments don't contribute
+                  to the hash. The theory is that these arguments will change
+                  the output of -E if they are going to have any effect at
+                  all. */
+               if (i < args->argc-1) {
+                       if (strcmp(args->argv[i], "-I") == 0 ||
+                           strcmp(args->argv[i], "-include") == 0 ||
+                           strcmp(args->argv[i], "-D") == 0 ||
+                           strcmp(args->argv[i], "-idirafter") == 0 ||
+                           strcmp(args->argv[i], "-isystem") == 0) {
+                               if (mode == FINDHASH_DIRECT_MODE) {
+                                       hash_string(&hash, args->argv[i]);
+                                       s = make_relative_path(x_strdup(args->argv[i+1]));
+                                       hash_string(&hash, s);
+                                       free(s);
+                               } /* else: skip from hash */
+                               i++;
                                continue;
                        }
                }
+               if (strncmp(args->argv[i], "-I", 2) == 0 ||
+                   strncmp(args->argv[i], "-D", 2) == 0) {
+                       if (mode == FINDHASH_DIRECT_MODE) {
+                               hash_buffer(&hash, args->argv[i], 2);
+                               s = make_relative_path(x_strdup(args->argv[i] + 2));
+                               hash_string(&hash, s);
+                               free(s);
+                       } /* else: skip from hash */
+                       continue;
+               }
 
                if (strncmp(args->argv[i], "--specs=", 8) == 0 &&
                    stat(args->argv[i]+8, &st) == 0) {
@@ -928,7 +961,7 @@ static void from_cache(enum fromcache_call_mode mode, int put_object_in_manifest
        close(fd_stderr);
 
        /* Create or update the manifest file. */
-       if (put_object_in_manifest) {
+       if (put_object_in_manifest && included_files) {
                if (manifest_put(manifest_path, object_hash, included_files)) {
                        cc_log("Added object file hash to manifest\n");
                        /* Update timestamp for LRU cleanup. */
@@ -1315,6 +1348,8 @@ static void ccache(int argc, char *argv[])
 
        cc_log("=== %s ===\n", now);
 
+       cc_log("Base directory: %s\n", base_dir);
+
        /* find the real compiler */
        find_compiler(argc, argv);
 
@@ -1549,6 +1584,8 @@ int main(int argc, char *argv[])
 {
        char *p;
 
+       current_working_dir = get_cwd();
+
        cache_dir = getenv("CCACHE_DIR");
        if (!cache_dir) {
                const char *home_directory = get_home_directory();
@@ -1564,8 +1601,16 @@ int main(int argc, char *argv[])
 
        cache_logfile = getenv("CCACHE_LOGFILE");
 
-       setup_uncached_err();
+       base_dir = getenv("CCACHE_BASEDIR");
+       if (base_dir) {
+               if (strcmp(base_dir, "") == 0) {
+                       base_dir = NULL;
+               }
+       } else {
+               base_dir = get_cwd();
+       }
 
+       setup_uncached_err();
 
        /* the user might have set CCACHE_UMASK */
        p = getenv("CCACHE_UMASK");
index 08c5fdc714d85c2626f67bd708dd17857bef1d49..0095dabdab5da12a796139d62a74438f5889b74e 100644 (file)
--- a/ccache.h
+++ b/ccache.h
@@ -129,6 +129,9 @@ char *x_realpath(const char *path);
 char *gnu_getcwd(void);
 int create_empty_file(const char *fname);
 const char *get_home_directory(void);
+char *get_cwd();
+size_t common_dir_prefix_length(const char *s1, const char *s2);
+char *get_relative_path(const char *from, const char *to);
 
 void stats_update(enum stats stat);
 void stats_zero(void);
diff --git a/test.sh b/test.sh
index f778a8665038b2877a5f0b53c70aa102801627a2..90f9b4bd3deeafe52af6bf07ff278118797ed05a 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -9,7 +9,7 @@ else
     COMPILER=cc
 fi
 
-CCACHE=../ccache
+CCACHE=$PWD/ccache
 TESTDIR=testdir.$$
 
 unset CCACHE_DISABLE
@@ -63,7 +63,7 @@ checkfile() {
 
 basetests() {
     echo "starting testsuite $testsuite"
-    rm -rf .ccache
+    rm -rf $CCACHE_DIR
     checkstat 'cache hit (preprocessed)' 0
     checkstat 'cache miss' 0
 
@@ -253,13 +253,12 @@ basetests() {
     $CCACHE -C > /dev/null
     checkstat 'files in cache' 0
 
-
     rm -f test1.c
 }
 
 direct_tests() {
     echo "starting testsuite $testsuite"
-    rm -rf .ccache
+    rm -rf $CCACHE_DIR
     unset CCACHE_NODIRECT
 
     ##################################################################
@@ -285,7 +284,7 @@ EOF
     ##################################################################
     # First compilation is a miss.
     testname="first compilation"
-    $CCACHE -C >/dev/null
+    $CCACHE -z >/dev/null
     $CCACHE $COMPILER -c test.c
     checkstat 'cache hit (direct)' 0
     checkstat 'cache hit (preprocessed)' 0
@@ -447,19 +446,118 @@ EOF
     $CCACHE -C >/dev/null
 }
 
+basedir_tests() {
+    echo "starting testsuite $testsuite"
+    rm -rf $CCACHE_DIR
+
+    ##################################################################
+    # Create some code to compile.
+    mkdir -p dir1/src dir1/include
+    cat <<EOF >dir1/src/test.c
+#include <test.h>
+EOF
+    cat <<EOF >dir1/include/test.h
+int test;
+EOF
+    cp -r dir1 dir2
+
+    sleep 1 # Sleep to make the include files trusted.
+
+    ##################################################################
+    # CCACHE_BASEDIR="" and using absolute include path will result in a cache
+    # miss.
+    testname="empty CCACHE_BASEDIR"
+    $CCACHE -z >/dev/null
+
+    cd dir1
+    CCACHE_BASEDIR="" $CCACHE $COMPILER -I$PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    cd ..
+
+    cd dir2
+    CCACHE_BASEDIR="" $CCACHE $COMPILER -I$PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 2
+    cd ..
+
+    ##################################################################
+    # Setting CCACHE_BASEDIR will result in a cache hit because include paths
+    # in the preprocessed output are rewritten.
+    testname="set CCACHE_BASEDIR"
+    $CCACHE -z >/dev/null
+    $CCACHE -C >/dev/null
+
+    cd dir1
+    CCACHE_BASEDIR="$PWD" $CCACHE $COMPILER -I$PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    cd ..
+
+    cd dir2
+    CCACHE_BASEDIR="$PWD" $CCACHE $COMPILER -I $PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 1
+    checkstat 'cache miss' 1
+    cd ..
+
+    ##################################################################
+    # Setting CCACHE_BASEDIR will result in a cache hit because -I arguments
+    # are rewritten, as are the paths stored in the manifest.
+    testname="set CCACHE_BASEDIR, direct lookup"
+    $CCACHE -z >/dev/null
+    $CCACHE -C >/dev/null
+    unset CCACHE_NODIRECT
+
+    cd dir1
+    CCACHE_BASEDIR="$PWD" $CCACHE $COMPILER -I$PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    cd ..
+
+    cd dir2
+    CCACHE_BASEDIR="$PWD" $CCACHE $COMPILER -I $PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 1
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    cd ..
+
+    CCACHE_NODIRECT=1
+    export CCACHE_NODIRECT
+
+    ##################################################################
+    # CCACHE_BASEDIR="$PWD" is the default.
+    testname="default CCACHE_BASEDIR"
+    cd dir1
+    $CCACHE -z >/dev/null
+    $CCACHE $COMPILER -I$PWD/include -c src/test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 1
+    checkstat 'cache miss' 0
+    cd ..
+}
+
 ######
 # main program
 rm -rf $TESTDIR
 mkdir $TESTDIR
 cd $TESTDIR || exit 1
-mkdir .ccache
-CCACHE_DIR=.ccache
+
+CCACHE_DIR=$PWD/.ccache
 export CCACHE_DIR
-CCACHE_LOGFILE=ccache.log
+CCACHE_LOGFILE=$PWD/ccache.log
 export CCACHE_LOGFILE
 CCACHE_NODIRECT=1
 export CCACHE_NODIRECT
 
+mkdir $CCACHE_DIR
+
+# ---------------------------------------
+
 testsuite="base"
 CCACHE_COMPILE="$CCACHE $COMPILER"
 basetests
@@ -500,6 +598,11 @@ unset CCACHE_NLEVELS
 testsuite="direct"
 direct_tests
 
+testsuite="basedir"
+basedir_tests
+
+# ---------------------------------------
+
 cd ..
 rm -rf $TESTDIR
 echo test done - OK
diff --git a/util.c b/util.c
index c5199d16eacd48e38d0da7643a2ca1d094da343a..fab119093ae8796e24f97c35c3fdd7e5f62b5f3c 100644 (file)
--- a/util.c
+++ b/util.c
@@ -472,6 +472,27 @@ void *x_realloc(void *ptr, size_t size)
 }
 
 
+/*
+ * This is like x_asprintf() but frees *ptr if *ptr != NULL.
+ */
+void x_asprintf2(char **ptr, const char *format, ...)
+{
+       char *saved = *ptr;
+       va_list ap;
+
+       *ptr = NULL;
+       va_start(ap, format);
+       if (vasprintf(ptr, format, ap) == -1) {
+               fatal("Out of memory in x_asprintf2\n");
+       }
+       va_end(ap);
+
+       if (!ptr) fatal("Out of memory in x_asprintf2\n");
+       if (saved) {
+               free(saved);
+       }
+}
+
 /*
    revsusive directory traversal - used for cleanup
    fn() is called on all files/dirs in the tree
@@ -734,3 +755,89 @@ const char *get_home_directory(void)
        cc_log("Unable to determine home directory");
        return NULL;
 }
+
+/*
+ * Get the current directory by reading $PWD. If $PWD isn't sane, gnu_getcwd()
+ * is used.
+ */
+char *get_cwd()
+{
+       char *pwd;
+       char *cwd;
+       struct stat st_pwd;
+       struct stat st_cwd;
+
+       cwd = gnu_getcwd();
+       pwd = getenv("PWD");
+       if (!pwd) {
+               return cwd;
+       }
+       if (stat(pwd, &st_pwd) != 0) {
+               return cwd;
+       }
+       if (stat(cwd, &st_cwd) != 0) {
+               return cwd;
+       }
+       if (st_pwd.st_dev == st_cwd.st_dev && st_pwd.st_ino == st_cwd.st_ino) {
+               return x_strdup(pwd);
+       } else {
+               return cwd;
+       }
+}
+
+/*
+ * Compute the length of the longest directory path that is common to two
+ * strings.
+ */
+size_t common_dir_prefix_length(const char *s1, const char *s2)
+{
+       const char *p1 = s1;
+       const char *p2 = s2;
+
+       while (*p1 && *p2 && *p1 == *p2) {
+               ++p1;
+               ++p2;
+       }
+       while (p1 > s1 && ((*p1 && *p1 != '/' ) || (*p2 && *p2 != '/'))) {
+               p1--;
+               p2--;
+       }
+       return p1 - s1;
+}
+
+/*
+ * Compute a relative path from from to to. Caller frees.
+ */
+char *get_relative_path(const char *from, const char *to)
+{
+       size_t common_prefix_len;
+       int i;
+       const char *p;
+       char *result;
+
+       if (!*to || *to != '/') {
+               return x_strdup(to);
+       }
+
+       result = x_strdup("");
+       common_prefix_len = common_dir_prefix_length(from, to);
+       for (p = from + common_prefix_len; *p; p++) {
+               if (*p == '/') {
+                       x_asprintf2(&result, "../%s", result);
+               }
+       }
+       if (strlen(to) > common_prefix_len) {
+               x_asprintf2(&result, "%s%s", result,
+                           to + common_prefix_len + 1);
+       }
+       i = strlen(result) - 1;
+       while (i >= 0 && result[i] == '/') {
+               result[i] = '\0';
+               i--;
+       }
+       if (strcmp(result, "") == 0) {
+               free(result);
+               result = x_strdup(".");
+       }
+       return result;
+}