]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Handle clang PCHs (#539) (#624)
authorahasselbring <arne.hasselbring@googlemail.com>
Sat, 25 Jul 2020 19:37:32 +0000 (21:37 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Sat, 25 Jul 2020 19:38:58 +0000 (21:38 +0200)
Co-authored-by: Cristian Adam <cristian.adam@gmail.com>
src/ArgsInfo.hpp
src/argprocessing.cpp
src/ccache.cpp
src/compopt.cpp
src/manifest.cpp
test/suites/pch.bash

index 54f7a31c51ba5b772f6c14d7c95c37baefd877d4..753dc8fa4950c4e511ac0eae4fc971cab238a289 100644 (file)
@@ -103,6 +103,10 @@ struct ArgsInfo
   // Clang's -include-pch or -include-pth).
   bool using_precompiled_header = false;
 
+  // Whether Clang is instructed not to include timestamps in the precompiled
+  // header it generates.
+  bool fno_pch_timestamp = false;
+
   // Files referenced by -fsanitize-blacklist options.
   std::vector<std::string> sanitize_blacklists;
 
index 5ac3391fa40cf34703dc3c0009198a94c8ee6216..84bcb442d2aaa5623089833617e5b1b9a3bd9881 100644 (file)
@@ -91,18 +91,22 @@ bool
 detect_pch(Context& ctx,
            const std::string& option,
            const std::string& arg,
+           bool is_cc1_option,
            bool* found_pch)
 {
   assert(found_pch);
 
   // Try to be smart about detecting precompiled headers.
+  // If the option is an option for Clang (is_cc1_option), don't accept
+  // anything just because it has a corresponding precompiled header,
+  // because Clang doesn't behave that way either.
   std::string pch_file;
   if (option == "-include-pch" || option == "-include-pth") {
     if (Stat::stat(arg)) {
       cc_log("Detected use of precompiled header: %s", arg.c_str());
       pch_file = arg;
     }
-  } else {
+  } else if (!is_cc1_option) {
     for (const auto& extension : {".gch", ".pch", ".pth"}) {
       std::string path = arg + extension;
       if (Stat::stat(path)) {
@@ -305,6 +309,23 @@ process_arg(Context& ctx,
     return nullopt;
   }
 
+  // Some arguments that clang passes directly to cc1 (related to precompiled
+  // headers) need the usual ccache handling. In those cases, the -Xclang
+  // prefix is skipped and the cc1 argument is handled instead.
+  if (args[i] == "-Xclang" && i < args.size() - 1
+      && (args[i + 1] == "-emit-pch" || args[i + 1] == "-emit-pth"
+          || args[i + 1] == "-include-pch" || args[i + 1] == "-include-pth"
+          || args[i + 1] == "-fno-pch-timestamp")) {
+    if (compopt_affects_comp(args[i + 1])) {
+      state.compiler_only_args.push_back(args[i]);
+    } else if (compopt_affects_cpp(args[i + 1])) {
+      state.cpp_args.push_back(args[i]);
+    } else {
+      state.common_args.push_back(args[i]);
+    }
+    ++i;
+  }
+
   // Handle options that should not be passed to the preprocessor.
   if (compopt_affects_comp(args[i])) {
     state.compiler_only_args.push_back(args[i]);
@@ -325,11 +346,6 @@ process_arg(Context& ctx,
     return nullopt;
   }
 
-  if (args[i] == "-fpch-preprocess" || args[i] == "-emit-pch"
-      || args[i] == "-emit-pth") {
-    state.found_fpch_preprocess = true;
-  }
-
   // Modules are handled on demand as necessary in the background, so there is
   // no need to cache them, they can in practice be ignored. All that is needed
   // is to correctly depend also on module.modulemap files, and those are
@@ -695,6 +711,18 @@ process_arg(Context& ctx,
     return nullopt;
   }
 
+  if (args[i] == "-fno-pch-timestamp") {
+    args_info.fno_pch_timestamp = true;
+    state.common_args.push_back(args[i]);
+    return nullopt;
+  }
+
+  if (args[i] == "-fpch-preprocess") {
+    state.found_fpch_preprocess = true;
+    state.common_args.push_back(args[i]);
+    return nullopt;
+  }
+
   if (config.sloppiness() & SLOPPY_CLANG_INDEX_STORE
       && args[i] == "-index-store-path") {
     // Xcode 9 or later calls Clang with this option. The given path includes a
@@ -716,20 +744,28 @@ process_arg(Context& ctx,
       return STATS_ARGS;
     }
 
-    if (!detect_pch(ctx, args[i], args[i + 1], &state.found_pch)) {
+    // In the -Xclang -include-(pch/pth) -Xclang <path> case, the path is one
+    // index further behind.
+    int next = 1;
+    if (args[i + 1] == "-Xclang" && i + 2 < args.size()) {
+      next = 2;
+    }
+
+    if (!detect_pch(
+          ctx, args[i], args[i + next], next == 2, &state.found_pch)) {
       return STATS_ARGS;
     }
 
-    std::string relpath = Util::make_relative_path(ctx, args[i + 1]);
-    if (compopt_affects_cpp(args[i])) {
-      state.cpp_args.push_back(args[i]);
-      state.cpp_args.push_back(relpath);
-    } else {
-      state.common_args.push_back(args[i]);
-      state.common_args.push_back(relpath);
+    std::string relpath = Util::make_relative_path(ctx, args[i + next]);
+    auto& dest_args =
+      compopt_affects_cpp(args[i]) ? state.cpp_args : state.common_args;
+    dest_args.push_back(args[i]);
+    if (next == 2) {
+      dest_args.push_back(args[i + 1]);
     }
+    dest_args.push_back(relpath);
 
-    i++;
+    i += next;
     return nullopt;
   }
 
@@ -959,7 +995,8 @@ process_args(Context& ctx,
   }
 
   args_info.output_is_precompiled_header =
-    args_info.actual_language.find("-header") != std::string::npos;
+    args_info.actual_language.find("-header") != std::string::npos
+    || is_precompiled_header(args_info.output_obj.c_str());
 
   if (args_info.output_is_precompiled_header
       && !(config.sloppiness() & SLOPPY_PCH_DEFINES)) {
index baf0da66c8c3a2b5aac813637fdc2392feef6abf..37591a96e89e1c1725965c3a9c03dedec9d13d3c 100644 (file)
@@ -1683,7 +1683,7 @@ from_cache(Context& ctx, enum fromcache_call_mode mode)
   if ((ctx.guessed_compiler == GuessedCompiler::clang
        || ctx.guessed_compiler == GuessedCompiler::unknown)
       && ctx.args_info.output_is_precompiled_header
-      && mode == FROMCACHE_CPP_MODE) {
+      && !ctx.args_info.fno_pch_timestamp && mode == FROMCACHE_CPP_MODE) {
     cc_log("Not considering cached precompiled header in preprocessor mode");
     return nullopt;
   }
index 67748c1ade46e4c8d510affb0b5a2931fa0f0491..74899830a6aae214cb4c35a1af37dadc74699e80 100644 (file)
@@ -93,6 +93,8 @@ static const struct compopt compopts[] = {
   {"-bind_at_load", AFFECTS_COMP},
   {"-bundle", AFFECTS_COMP},
   {"-ccbin", AFFECTS_CPP | TAKES_ARG}, // nvcc
+  {"-emit-pch", AFFECTS_COMP},         // Clang
+  {"-emit-pth", AFFECTS_COMP},         // Clang
   {"-fno-working-directory", AFFECTS_CPP},
   {"-fplugin=libcc1plugin", TOO_HARD}, // interaction with GDB
   {"-frepo", TOO_HARD},
@@ -105,6 +107,7 @@ static const struct compopt compopts[] = {
   {"-imultilib", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
   {"-include", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
   {"-include-pch", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+  {"-include-pth", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
   {"-install_name", TAKES_ARG}, // Darwin linker option
   {"-iprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
   {"-iquote", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
index 9bfa5121029f41afe579a170073a07b93e7cb791..68f24d0b47356e06c97cf2ef1799eaa30147fb66 100644 (file)
@@ -424,7 +424,8 @@ verify_result(const Context& ctx,
     // and will error out if that header is later used without rebuilding.
     if ((ctx.guessed_compiler == GuessedCompiler::clang
          || ctx.guessed_compiler == GuessedCompiler::unknown)
-        && ctx.args_info.output_is_precompiled_header && fi.mtime != fs.mtime) {
+        && ctx.args_info.output_is_precompiled_header
+        && !ctx.args_info.fno_pch_timestamp && fi.mtime != fs.mtime) {
       cc_log("Precompiled header includes %s, which has a new mtime",
              path.c_str());
       return false;
index f779a36f6a7a8a124f6e6be83dccf57a3517ca20..89cdf9baa0c82d5266fb9233e9559a741b359cfe 100644 (file)
@@ -613,7 +613,7 @@ pch_suite_gcc() {
 
 pch_suite_clang() {
     # -------------------------------------------------------------------------
-    TEST "Create .gch, include file mtime changed"
+    TEST "Create .pch, include file mtime changed"
 
     backdate test.h
     cat <<EOF >pch2.h
@@ -772,4 +772,59 @@ EOF
     expect_stat 'cache hit (direct)' 0
     expect_stat 'cache hit (preprocessed)' 2
     expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "Create .pch with -Xclang options"
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    rm pch.h.pch
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_file_exists pch.h.pch
+
+    echo '#include <string.h> /*change pch*/' >>pch.h
+    backdate pch.h
+    rm pch.h.pch
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+    expect_file_exists pch.h.pch
+
+    # -------------------------------------------------------------------------
+    TEST "Use .pch the with -Xclang options"
+
+    $REAL_COMPILER $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+    backdate pch.h.pch
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    echo '#include <string.h> /*change pch*/' >>pch.h
+    backdate pch.h
+    $REAL_COMPILER $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+    backdate pch.h.pch
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
 }