]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Convert absolute paths to relative paths inside the dependency file
authorMihai Serban <mihai.serban@intel.com>
Tue, 15 Mar 2016 21:55:32 +0000 (23:55 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 30 Mar 2016 19:41:09 +0000 (21:41 +0200)
When using DIRECT MODE and the -MD flag to generate a dependency file
the toolchain header files are used with their absolute paths as dependencies.
If the same toolchain is used but from a different location the previously
cached .d file is copied to the current build environment. Because it contains
absolute paths that could disappear or where the current user does not have
access the compilation will fail complaining that some header files cannot
be found.
This mostly happens when the cache is shared among multiple users or multiple
clones of a source tree and when the same toolchain is used but from different
locations.
This patch adds support for this kind of usage by replacing the absolute paths
with relative ones based on the BASEDIR value.

https://lists.samba.org/archive/ccache/2014q2/001183.html
https://lists.samba.org/archive/ccache/2014q2/001184.html

ccache.c
test.sh

index 4a99e899df7aab3aa22782563cee399510293571..af766ab3e663f5f8aa2f8f17ffebbe34a0f620ab 100644 (file)
--- a/ccache.c
+++ b/ccache.c
@@ -177,6 +177,9 @@ time_t time_of_compilation;
  */
 static struct hashtable *included_files;
 
+/* uses absolute path for some include files */
+static bool has_absolute_include_headers = false;
+
 /* List of headers to ignore */
 static char **ignore_headers;
 
@@ -854,6 +857,9 @@ process_preprocessed_file(struct mdfour *hash, const char *path)
                        }
                        /* p and q span the include file path */
                        path = x_strndup(p, q - p);
+                       if (!has_absolute_include_headers) {
+                               has_absolute_include_headers = is_absolute_path(path);
+                       }
                        path = make_relative_path(path);
                        hash_string(hash, path);
                        remember_include_file(path, hash, system);
@@ -878,6 +884,96 @@ process_preprocessed_file(struct mdfour *hash, const char *path)
        return true;
 }
 
+/*
+ * Replace absolute paths with relative paths in the
+ * profided dependency file.
+ */
+static void
+use_relative_paths_in_depfile(const char *depfile)
+{
+       FILE *f, *tmpf;
+       char buf[10000];
+       char *tmp_file;
+       char *relpath;
+       bool result = false;
+       char *token, *saveptr;
+
+       if (str_eq(conf->base_dir, "")) {
+               cc_log("Base dir not set, skip using relative paths");
+               return; /* nothing to do */
+       }
+       if (!has_absolute_include_headers) {
+               cc_log("No absolute path for included files found, skip using relative paths");
+               return; /* nothing to do */
+       }
+
+       f = fopen(depfile, "r");
+       if (!f) {
+               cc_log("Cannot open dependency file: %s (%s)", depfile, strerror(errno));
+               return;
+       }
+       tmp_file = format("%s.tmp", depfile);
+       tmpf = create_tmp_file(&tmp_file, "w");
+       if (!tmpf) {
+               cc_log("Cannot create temporary dependency file: %s (%s)", tmp_file,
+                               strerror(errno));
+               free(tmp_file);
+               fclose(f);
+               return;
+       }
+
+       while (fgets(buf, sizeof(buf), f) && !ferror(tmpf)) {
+               token = strtok_r(buf, " \t", &saveptr);
+               while (token) {
+                       if (is_absolute_path(token) && str_startswith(token, conf->base_dir)) {
+                               relpath = make_relative_path(x_strdup(token));
+                               result = true;
+                       } else {
+                               relpath = token;
+                       }
+                       if (token != buf) { /* this is a dependency file */
+                               fputc(' ', tmpf);
+                       }
+                       fputs(relpath, tmpf);
+                       if (relpath != token) {
+                               free(relpath);
+                       }
+                       token = strtok_r(NULL, " \t", &saveptr);
+               }
+       }
+
+       if (ferror(f)) {
+               cc_log("Error reading dependency file: %s, skip relative path usage",
+                               depfile);
+               result = false;
+         goto out;
+       }
+       if (ferror(tmpf)) {
+               cc_log("Error writing temporary dependency file: %s, skip relative path usage",
+                               tmp_file);
+               result = false;
+               goto out;
+       }
+
+out:
+       fclose(tmpf);
+       fclose(f);
+       if (result) {
+               if (x_rename(tmp_file, depfile) != 0) {
+                       cc_log("Error renaming dependency file: %s -> %s (%s), skip relative path usage",
+                           tmp_file, depfile, strerror(errno));
+                       result = false;
+         } else {
+                       cc_log("Rename dependency file: %s -> %s", tmp_file, depfile);
+         }
+       }
+       if (!result) {
+               cc_log("Removing temporary dependency file: %s", tmp_file);
+               x_unlink(tmp_file);
+       }
+       free(tmp_file);
+}
+
 /* Copy or link a file to the cache. */
 static void
 put_file_in_cache(const char *source, const char *dest)
@@ -1232,6 +1328,7 @@ to_cache(struct args *args)
        }
 
        if (generating_dependencies) {
+               use_relative_paths_in_depfile(output_dep);
                put_file_in_cache(output_dep, cached_dep);
        }
        stats_update(STATS_TOCACHE);
@@ -3082,6 +3179,7 @@ cc_reset(void)
        if (included_files) {
                hashtable_destroy(included_files, 1); included_files = NULL;
        }
+       has_absolute_include_headers = false;
        generating_debuginfo = false;
        generating_dependencies = false;
        generating_coverage = false;
diff --git a/test.sh b/test.sh
index d19642973e49cce064b26818e6362c7ce9e89e74..35cfdbd399be5e5d0ea0928c0959c9fcabbd7a0c 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -1525,6 +1525,7 @@ basedir_suite() {
     # Create some code to compile.
     mkdir -p dir1/src dir1/include
     cat <<EOF >dir1/src/test.c
+#include <stdarg.h>
 #include <test.h>
 EOF
     cat <<EOF >dir1/include/test.h
@@ -1673,6 +1674,35 @@ EOF
         cd ..
     done
 
+    ##################################################################
+    # When BASEDIR is set to / check that -MF, -MQ and -MT arguments with absolute paths
+    # are rewritten to relative and the dependency file contains only relative# paths.
+    testname="-MF/-MQ/-MT with absolute paths and BASEDIR set to /"
+    for option in MF "MF " MQ "MQ " MT "MT "; do
+        $CCACHE -Cz >/dev/null
+        cd dir1
+        CCACHE_BASEDIR="/" $CCACHE $COMPILER -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
+        checkstat 'cache hit (direct)' 0
+        checkstat 'cache hit (preprocessed)' 0
+        checkstat 'cache miss' 1
+        # Check that there is no absolute path in the dependency file
+        while read line; do
+            for file in $line; do
+                case $file in /*)
+                    test_failed "Absolute file path '$file' found in dependency file '`pwd`/test.d'"
+                esac
+            done
+        done < `pwd`/test.d
+        cd ..
+
+        cd dir2
+        CCACHE_BASEDIR="/" $CCACHE $COMPILER -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
+        checkstat 'cache hit (direct)' 1
+        checkstat 'cache hit (preprocessed)' 0
+        checkstat 'cache miss' 1
+        cd ..
+    done
+
     ##################################################################
     # Check that clang's --serialize-diagnostics arguments with absolute paths
     # are rewritten to relative.