From: ahasselbring Date: Sat, 25 Jul 2020 19:37:32 +0000 (+0200) Subject: Handle clang PCHs (#539) (#624) X-Git-Tag: v4.0~296 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3ec58dbcaa1e7903d59dce4c77baff7bc3783269;p=thirdparty%2Fccache.git Handle clang PCHs (#539) (#624) Co-authored-by: Cristian Adam --- diff --git a/src/ArgsInfo.hpp b/src/ArgsInfo.hpp index 54f7a31c5..753dc8fa4 100644 --- a/src/ArgsInfo.hpp +++ b/src/ArgsInfo.hpp @@ -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 sanitize_blacklists; diff --git a/src/argprocessing.cpp b/src/argprocessing.cpp index 5ac3391fa..84bcb442d 100644 --- a/src/argprocessing.cpp +++ b/src/argprocessing.cpp @@ -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 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)) { diff --git a/src/ccache.cpp b/src/ccache.cpp index baf0da66c..37591a96e 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -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; } diff --git a/src/compopt.cpp b/src/compopt.cpp index 67748c1ad..74899830a 100644 --- a/src/compopt.cpp +++ b/src/compopt.cpp @@ -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}, diff --git a/src/manifest.cpp b/src/manifest.cpp index 9bfa51210..68f24d0b4 100644 --- a/src/manifest.cpp +++ b/src/manifest.cpp @@ -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; diff --git a/test/suites/pch.bash b/test/suites/pch.bash index f779a36f6..89cdf9baa 100644 --- a/test/suites/pch.bash +++ b/test/suites/pch.bash @@ -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 <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 /*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 /*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 }