]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Don't use cache when building precompiled header with changed deps
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>
Thu, 15 Jun 2017 12:40:16 +0000 (14:40 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 19 Jul 2017 20:41:34 +0000 (22:41 +0200)
If one of the included files in a precompiled header is touched,
changing only its mtime, clang will produce a fatal error when the
precompiled header is then used:

    file 'foo.h' has been modified since the precompiled header 'foo.pch' was built

We need to take this into account when producing precompiled headers,
so that we don't pick out stale objects from the cache. This works
fine for direct mode, but in preprocess mode we don't have enough
information to decide if the object is still valid, so we skip the
cache entirely in that mode.

ccache.c
ccache.h
manifest.c
test.sh

index 42935e7a87ed49deb0a3835d4a4f616846af2be3..fd1b4ce1c8bdbdea8e23a910bd660a9f8421922f 100644 (file)
--- a/ccache.c
+++ b/ccache.c
@@ -197,7 +197,7 @@ static char *cpp_stderr;
 char *stats_file = NULL;
 
 // Whether the output is a precompiled header.
-static bool output_is_precompiled_header = false;
+bool output_is_precompiled_header = false;
 
 // Profile generation / usage information.
 static char *profile_dir = NULL;
@@ -1870,6 +1870,16 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
                return;
        }
 
+       // We can't trust the objects based on running the preprocessor
+       // when the output is precompiled headers, as the hash does not
+       // include the mtime of each included header, breaking compilation
+       // with clang when the precompiled header is used after touching
+       // one of the included files.
+       if (output_is_precompiled_header && mode == FROMCACHE_CPP_MODE) {
+               cc_log("Not using preprocessed cached object for precompiled header");
+               return;
+       }
+
        struct stat st;
        if (stat(cached_obj, &st) != 0) {
                cc_log("Object file %s not in cache", cached_obj);
index 6dfaa24265570e7945cc2e08bcc24969fa80bf10..2c5d64ddd199d578985dc44eb11c737c20513e05 100644 (file)
--- a/ccache.h
+++ b/ccache.h
@@ -238,6 +238,7 @@ void lockfile_release(const char *path);
 // ccache.c
 
 extern time_t time_of_compilation;
+extern bool output_is_precompiled_header;
 void block_signals(void);
 void unblock_signals(void);
 bool cc_process_args(struct args *args, struct args **preprocessor_args,
index f84bef44b5c603820c2aa0c61712155378f265be..5cd3ead2ad76c9cda88e68c8d7201580f083b601 100644 (file)
@@ -381,6 +381,13 @@ verify_object(struct conf *conf, struct manifest *mf, struct object *obj,
                        return 0;
                }
 
+               // Clang stores the mtime of the included files in the precompiled header,
+               // and will error out if that header is later used without rebuilding.
+               if (output_is_precompiled_header && fi->mtime != st->mtime) {
+                       cc_log("Precompiled header includes %s, which has a new mtime", path);
+                       return 0;
+               }
+
                if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) {
                        if (fi->mtime == st->mtime && fi->ctime == st->ctime) {
                                cc_log("mtime/ctime hit for %s", path);
diff --git a/test.sh b/test.sh
index cda538f6720ad368f6f7fe6f64438f19db1ebb66..ce4f9760d62fd3f896395ccce9c6912328595c5d 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -3115,6 +3115,42 @@ pch_suite_clang() {
         test_failed "pch.gch missing"
     fi
 
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, include file mtime changed"
+
+    backdate test.h
+    cat <<EOF >pch2.h
+    #include <stdlib.h>
+    #include "test.h"
+EOF
+
+    # Make sure time_of_compilation is at least one second larger than the ctime
+    # of the test.h include, otherwise we might not cache its ctime/mtime.
+    sleep 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c pch2.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    touch test.h
+    sleep 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c pch2.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    $UNCACHED_COMPILE $SYSROOT -c -include pch2.h pch2.c
+    if [ ! -f pch2.o ]; then
+        test_failed "pch.o missing"
+    fi
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c pch2.h
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
     # -------------------------------------------------------------------------
     TEST "Use .gch, no -fpch-preprocess, -include, no sloppiness"