diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -97,6 +97,7 @@ CIOpts.RecoverOnError = true; CIOpts.Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false); + CIOpts.ProbePrecompiled = false; std::unique_ptr CI = createInvocation(ArgStrs, CIOpts); if (!CI) return nullptr; diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -271,6 +271,9 @@ /// Whether to check that input files exist when constructing compilation /// jobs. unsigned CheckInputsExist : 1; + /// Whether to probe for PCH files on disk, in order to upgrade + /// -include foo.h to -include-pch foo.h.pch. + unsigned ProbePrecompiled : 1; public: /// Force clang to emit reproducer for driver invocation. This is enabled @@ -357,6 +360,9 @@ void setCheckInputsExist(bool Value) { CheckInputsExist = Value; } + bool getProbePrecompiled() const { return ProbePrecompiled; } + void setProbePrecompiled(bool Value) { ProbePrecompiled = Value; } + void setTargetAndMode(const ParsedClangName &TM) { ClangNameParts = TM; } const std::string &getTitle() { return DriverTitle; } diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -202,6 +202,11 @@ /// if any errors were encountered. /// By default, always return null on errors. bool RecoverOnError = false; + /// Allow the driver to probe the filesystem for PCH files. + /// This is used to replace -include with -include-pch in the cc1 args. + /// FIXME: ProbePrecompiled=true is a poor, historical default. + /// It misbehaves if the PCH file is from GCC, has the wrong version, etc. + bool ProbePrecompiled = true; /// If set, the target is populated with the cc1 args produced by the driver. /// This may be populated even if createInvocation returns nullptr. std::vector *CC1Args = nullptr; @@ -236,7 +241,7 @@ IntrusiveRefCntPtr(), IntrusiveRefCntPtr VFS = nullptr, bool ShouldRecoverOnErrors = false, - std::vector *CC1Args = nullptr); + std::vector *CC1Args = nullptr, bool ProbePrecompiled = true); } // namespace clang diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -198,7 +198,8 @@ CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false), CCGenDiagnostics(false), CCPrintProcessStats(false), TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true), - GenReproducer(false), SuppressMissingInputWarning(false) { + ProbePrecompiled(true), GenReproducer(false), + SuppressMissingInputWarning(false) { // Provide a sane fallback if no VFS is specified. if (!this->VFS) this->VFS = llvm::vfs::getRealFileSystem(); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1364,7 +1364,8 @@ bool RenderedImplicitInclude = false; for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) { - if (A->getOption().matches(options::OPT_include)) { + if (A->getOption().matches(options::OPT_include) && + D.getProbePrecompiled()) { // Handling of gcc-style gch precompiled headers. bool IsFirstImplicitInclude = !RenderedImplicitInclude; RenderedImplicitInclude = true; @@ -1375,12 +1376,12 @@ // so that replace_extension does the right thing. P += ".dummy"; llvm::sys::path::replace_extension(P, "pch"); - if (llvm::sys::fs::exists(P)) + if (D.getVFS().exists(P)) FoundPCH = true; if (!FoundPCH) { llvm::sys::path::replace_extension(P, "gch"); - if (llvm::sys::fs::exists(P)) { + if (D.getVFS().exists(P)) { FoundPCH = true; } } diff --git a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp --- a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -48,6 +48,7 @@ // Don't check that inputs exist, they may have been remapped. TheDriver.setCheckInputsExist(false); + TheDriver.setProbePrecompiled(Opts.ProbePrecompiled); std::unique_ptr C(TheDriver.BuildCompilation(Args)); if (!C) @@ -107,8 +108,8 @@ std::unique_ptr clang::createInvocationFromCommandLine( ArrayRef Args, IntrusiveRefCntPtr Diags, IntrusiveRefCntPtr VFS, bool ShouldRecoverOnErrors, - std::vector *CC1Args) { + std::vector *CC1Args, bool ProbePrecompiled) { return createInvocation( - Args, - CreateInvocationOptions{Diags, VFS, ShouldRecoverOnErrors, CC1Args}); + Args, CreateInvocationOptions{Diags, VFS, ShouldRecoverOnErrors, + ProbePrecompiled, CC1Args}); } diff --git a/clang/unittests/Frontend/UtilsTest.cpp b/clang/unittests/Frontend/UtilsTest.cpp --- a/clang/unittests/Frontend/UtilsTest.cpp +++ b/clang/unittests/Frontend/UtilsTest.cpp @@ -11,12 +11,15 @@ #include "clang/Basic/TargetOptions.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace clang { namespace { +using testing::ElementsAre; TEST(BuildCompilerInvocationTest, RecoverMultipleJobs) { // This generates multiple jobs and we recover by using the first. @@ -33,5 +36,31 @@ EXPECT_THAT(CI->TargetOpts->Triple, testing::StartsWith("i386-")); } +// buildInvocationFromCommandLine should not translate -include to -include-pch, +// even if the PCH file exists. +TEST(BuildCompilerInvocationTest, ProbePrecompiled) { + std::vector Args = {"clang", "-include", "foo.h", "foo.cpp"}; + auto FS = llvm::makeIntrusiveRefCnt(); + FS->addFile("foo.h", 0, llvm::MemoryBuffer::getMemBuffer("")); + FS->addFile("foo.h.pch", 0, llvm::MemoryBuffer::getMemBuffer("")); + + clang::IgnoringDiagConsumer D; + llvm::IntrusiveRefCntPtr CommandLineDiagsEngine = + clang::CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, + false); + // Default: ProbePrecompiled is true. + std::unique_ptr CI = createInvocationFromCommandLine( + Args, CommandLineDiagsEngine, FS, false, nullptr); + ASSERT_TRUE(CI); + EXPECT_THAT(CI->getPreprocessorOpts().Includes, ElementsAre()); + EXPECT_EQ(CI->getPreprocessorOpts().ImplicitPCHInclude, "foo.h.pch"); + + CI = createInvocationFromCommandLine(Args, CommandLineDiagsEngine, FS, false, + nullptr, /*ProbePrecompiled=*/false); + ASSERT_TRUE(CI); + EXPECT_THAT(CI->getPreprocessorOpts().Includes, ElementsAre("foo.h")); + EXPECT_EQ(CI->getPreprocessorOpts().ImplicitPCHInclude, ""); +} + } // namespace } // namespace clang