From 2ca4be97de0f8ff3fe51a4bf48a7d26922818482 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Tue, 1 Mar 2016 23:16:44 +0000 Subject: [PATCH] clang-cl: Implement initial limited support for precompiled headers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the gcc precompiled header model, one explicitly runs clang with `-x c++-header` on a .h file to produce a gch file, and then includes the header with `-include foo.h` and if a .gch file exists for that header it gets used. This is documented at http://clang.llvm.org/docs/UsersManual.html#precompiled-headers cl.exe's model is fairly different, and controlled by the two flags /Yc and /Yu. A pch file is generated as a side effect of a regular compilation when /Ycheader.h is passed. While the compilation is running, the compiler keeps track of #include lines in the main translation unit and writes everything up to an `#include "header.h"` line into a pch file. Conversely, /Yuheader.h tells the compiler to skip all code in the main TU up to and including `#include "header.h"` and instead load header.pch. (It's also possible to use /Yc and /Yu without an argument, in that case a `#pragma hrdstop` takes the role of controlling the point where pch ends and real code begins.) This patch implements limited support for this in that it requires the pch header to be passed as a /FI force include flag – with this restriction, it can be implemented almost completely in the driver with fairly small amounts of code. For /Yu, this is trivial, and for /Yc a separate pch action is added that runs before the actual compilation. After r261774, the first failing command makes a compilation stop – this means if the pch fails to build the main compilation won't run, which is what we want. However, in /fallback builds we need to run the main compilation even if the pch build fails so that the main compilation's fallback can run. To achieve this, add a ForceSuccessCommand that pretends that the pch build always succeeded in /fallback builds (the main compilation will then fail to open the pch and run the fallback cl.exe invocation). If /Yc /Yu are used in a setup that clang-cl doesn't implement yet, clang-cl will now emit a "not implemented yet; flag ignored" warning that can be disabled using -Wno-clang-cl-pch. Since clang-cl doesn't yet serialize some important things (most notably `pragma comment(lib, ...)`, this feature is disabled by default and only enabled by an internal driver flag. Once it's more stable, this internal flag will disappear. (The default stdafx.h setup passes stdafx.h as explicit argument to /Yc but not as /FI – instead every single TU has to `#include ` as first thing it does. Implementing support for this should be possible with the approach in this patch with minimal frontend changes by passing a --stop-at / --start-at flag from the driver to the frontend. This is left for a follow-up. I don't think we ever want to support `#pragma hdrstop`, and supporting it with this approach isn't easy: This approach relies on the driver knowing the pch filename in advance, and `#pragma hdrstop(out.pch)` can set the output filename, so the driver can't know about it in advance.) clang-cl now also honors /Fp and puts pch files in the same spot that cl.exe would put them, but the pch file format is of course incompatible. This has ramifications on /fallback, so /Yc /Yu aren't passed through to cl.exe in /fallback builds. http://reviews.llvm.org/D17695 llvm-svn: 262420 --- .../clang/Basic/DiagnosticDriverKinds.td | 14 + clang/include/clang/Basic/DiagnosticGroups.td | 2 + clang/include/clang/Driver/CC1Options.td | 4 + clang/include/clang/Driver/CLCompatOptions.td | 27 +- clang/include/clang/Driver/Driver.h | 3 + clang/include/clang/Driver/Job.h | 14 + .../include/clang/Frontend/CompilerInstance.h | 9 +- .../include/clang/Frontend/FrontendOptions.h | 4 + clang/lib/Driver/Driver.cpp | 103 +++++- clang/lib/Driver/Job.cpp | 23 ++ clang/lib/Driver/Tools.cpp | 70 +++- clang/lib/Frontend/CompilerInstance.cpp | 36 +- clang/lib/Frontend/CompilerInvocation.cpp | 1 + clang/test/Driver/Inputs/pchfile.cpp | 0 clang/test/Driver/Inputs/pchfile.h | 3 + clang/test/Driver/cl-pch-errorhandling.cpp | 15 + clang/test/Driver/cl-pch-search.cpp | 6 + clang/test/Driver/cl-pch.c | 45 +++ clang/test/Driver/cl-pch.cpp | 309 ++++++++++++++++++ 19 files changed, 674 insertions(+), 14 deletions(-) create mode 100644 clang/test/Driver/Inputs/pchfile.cpp create mode 100644 clang/test/Driver/Inputs/pchfile.h create mode 100644 clang/test/Driver/cl-pch-errorhandling.cpp create mode 100644 clang/test/Driver/cl-pch-search.cpp create mode 100644 clang/test/Driver/cl-pch.c create mode 100644 clang/test/Driver/cl-pch.cpp diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index c6e6eaf47cc9..ce455ae9768a 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -98,6 +98,20 @@ def err_drv_unknown_argument : Error<"unknown argument: '%0'">; def warn_drv_unknown_argument_clang_cl : Warning< "unknown argument ignored in clang-cl: '%0'">, InGroup; + +def warn_drv_ycyu_no_arg_clang_cl : Warning< + "support for '%0' without a filename not implemented yet; flag ignored">, + InGroup; +def warn_drv_ycyu_different_arg_clang_cl : Warning< + "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">, + InGroup; +def warn_drv_ycyu_no_fi_arg_clang_cl : Warning< + "support for '%0' without a corresponding /FI flag not implemented yet; flag ignored">, + InGroup; +def warn_drv_yc_multiple_inputs_clang_cl : Warning< + "support for '/Yc' with more than one source file not implemented yet; flag ignored">, + InGroup; + def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">; def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">; def err_drv_invalid_remap_file : Error< diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 969050df5963..e5d8794b555f 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -785,6 +785,8 @@ def Microsoft : DiagGroup<"microsoft", MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag, MicrosoftCommentPaste, MicrosoftEndOfFile]>; +def ClangClPch : DiagGroup<"clang-cl-pch">; + def ObjCNonUnifiedException : DiagGroup<"objc-nonunified-exceptions">; def ObjCProtocolMethodImpl : DiagGroup<"objc-protocol-method-implementation">; diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index d6905db96252..66a8acf2c688 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -495,6 +495,10 @@ def fixit_to_temp : Flag<["-"], "fixit-to-temporary">, def foverride_record_layout_EQ : Joined<["-"], "foverride-record-layout=">, HelpText<"Override record layouts with those in the given file">; +def find_pch_source_EQ : Joined<["-"], "find-pch-source=">, + HelpText<"When building a pch, try to find the input file in include " + "directories, as if it had been included by the argument passed " + "to this flag.">; //===----------------------------------------------------------------------===// // Language Options diff --git a/clang/include/clang/Driver/CLCompatOptions.td b/clang/include/clang/Driver/CLCompatOptions.td index dbd25f572331..d7c84134e246 100644 --- a/clang/include/clang/Driver/CLCompatOptions.td +++ b/clang/include/clang/Driver/CLCompatOptions.td @@ -20,6 +20,9 @@ def cl_compile_Group : OptionGroup<"">, def cl_ignored_Group : OptionGroup<"">, Group; +def cl_internal_Group : OptionGroup<"">, + Group; + class CLFlag : Option<["/", "-"], name, KIND_FLAG>, Group, Flags<[CLOption, DriverOption]>; @@ -29,6 +32,9 @@ class CLCompileFlag : Option<["/", "-"], name, KIND_FLAG>, class CLIgnoredFlag : Option<["/", "-"], name, KIND_FLAG>, Group, Flags<[CLOption, DriverOption, HelpHidden]>; +class CLInternalFlag : Option<["-"], name, KIND_FLAG>, + Group, Flags<[CLOption, DriverOption, HelpHidden]>; + class CLJoined : Option<["/", "-"], name, KIND_JOINED>, Group, Flags<[CLOption, DriverOption]>; @@ -252,6 +258,23 @@ def _SLASH_volatile_ms : Option<["/", "-"], "volatile:ms", KIND_FLAG>, def _SLASH_Zl : CLFlag<"Zl">, HelpText<"Don't mention any default libraries in the object file">; +def _SLASH_Yc : CLJoined<"Yc">, + HelpText<"Generate a pch file for all code up to and including ">, + MetaVarName<"">; +def _SLASH_Yu : CLJoined<"Yu">, + HelpText<"Load a pch file and use it instead of all code up to " + "and including ">, + MetaVarName<"">; +def _SLASH_Y_ : CLFlag<"Y-">, + HelpText<"Disable precompiled headers, overrides /Yc and /Yu">; +def _SLASH_Fp : CLJoined<"Fp">, + HelpText<"Set pch filename (with /Yc and /Yu)">, MetaVarName<"">; + +// Internal: +// FIXME: Once /Yc support is stable enough, turn it on by default (when /Yc +// is passed) and remove this flag. +def _SLASH_internal_enable_pch : CLInternalFlag<"internal-enable-pch">; + // Ignored: def _SLASH_analyze_ : CLIgnoredFlag<"analyze-">; @@ -294,7 +317,6 @@ def _SLASH_favor : CLJoined<"favor">; def _SLASH_FC : CLFlag<"FC">; def _SLASH_F : CLFlag<"F">; def _SLASH_Fm : CLJoined<"Fm">; -def _SLASH_Fp : CLJoined<"Fp">; def _SLASH_Fr : CLJoined<"Fr">; def _SLASH_FR : CLJoined<"FR">; def _SLASH_FU : CLJoinedOrSeparate<"FU">; @@ -332,11 +354,8 @@ def _SLASH_V : CLFlag<"V">; def _SLASH_WL : CLFlag<"WL">; def _SLASH_Wp64 : CLFlag<"Wp64">; def _SLASH_X : CLFlag<"X">; -def _SLASH_Yc : CLJoined<"Yc">; -def _SLASH_Y_ : CLFlag<"Y-">; def _SLASH_Yd : CLFlag<"Yd">; def _SLASH_Yl : CLJoined<"Yl">; -def _SLASH_Yu : CLJoined<"Yu">; def _SLASH_Za : CLFlag<"Za">; def _SLASH_Zc : CLJoined<"Zc:">; def _SLASH_Ze : CLFlag<"Ze">; diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index ccf23e046962..f0924946e849 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -423,6 +423,9 @@ public: /// GCC goes to extra lengths here to be a bit more robust. std::string GetTemporaryPath(StringRef Prefix, const char *Suffix) const; + /// Return the pathname of the pch file in clang-cl mode. + std::string GetClPchPath(Compilation &C, StringRef BaseName) const; + /// ShouldUseClangCompiler - Should the clang compiler be used to /// handle this action. bool ShouldUseClangCompiler(const JobAction &JA) const; diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h index 263356f396f3..3366fc48d711 100644 --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -138,6 +138,20 @@ private: std::unique_ptr Fallback; }; +/// Like Command, but always pretends that the wrapped command succeeded. +class ForceSuccessCommand : public Command { +public: + ForceSuccessCommand(const Action &Source_, const Tool &Creator_, + const char *Executable_, const ArgStringList &Arguments_, + ArrayRef Inputs); + + void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, + CrashReportInfo *CrashInfo = nullptr) const override; + + int Execute(const StringRef **Redirects, std::string *ErrMsg, + bool *ExecutionFailed) const override; +}; + /// JobList - A sequence of jobs to perform. class JobList { public: diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 83eed2cdc592..fe36eeee2e66 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -748,10 +748,11 @@ public: /// /// \return True on success. static bool InitializeSourceManager(const FrontendInputFile &Input, - DiagnosticsEngine &Diags, - FileManager &FileMgr, - SourceManager &SourceMgr, - const FrontendOptions &Opts); + DiagnosticsEngine &Diags, + FileManager &FileMgr, + SourceManager &SourceMgr, + HeaderSearch *HS, + const FrontendOptions &Opts); /// } diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index c800a5148e49..71c08ec9c63f 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -266,6 +266,10 @@ public: /// \brief Auxiliary triple for CUDA compilation. std::string AuxTriple; + /// \brief If non-empty, search the pch input file as it was a header + // included by this file. + std::string FindPchSource; + public: FrontendOptions() : DisableFree(false), RelocatablePCH(false), ShowHelp(false), diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index eaf29de2aaf1..8965d3d6c3c5 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1452,6 +1452,66 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, } } + // Diagnose unsupported forms of /Yc /Yu. Ignore /Yc/Yu for now if: + // * no filename after it + // * both /Yc and /Yu passed but with different filenames + // * corresponding file not also passed as /FI + Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc); + Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu); + if (YcArg && YcArg->getValue()[0] == '\0') { + Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YcArg->getSpelling(); + Args.eraseArg(options::OPT__SLASH_Yc); + YcArg = nullptr; + } + if (YuArg && YuArg->getValue()[0] == '\0') { + Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YuArg->getSpelling(); + Args.eraseArg(options::OPT__SLASH_Yu); + YuArg = nullptr; + } + if (YcArg && YuArg && strcmp(YcArg->getValue(), YuArg->getValue()) != 0) { + Diag(clang::diag::warn_drv_ycyu_different_arg_clang_cl); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + if (YcArg || YuArg) { + StringRef Val = YcArg ? YcArg->getValue() : YuArg->getValue(); + bool FoundMatchingInclude = false; + for (const Arg *Inc : Args.filtered(options::OPT_include)) { + // FIXME: Do case-insensitive matching and consider / and \ as equal. + if (Inc->getValue() == Val) + FoundMatchingInclude = true; + } + if (!FoundMatchingInclude) { + Diag(clang::diag::warn_drv_ycyu_no_fi_arg_clang_cl) + << (YcArg ? YcArg : YuArg)->getSpelling(); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + } + if (YcArg && Inputs.size() > 1) { + Diag(clang::diag::warn_drv_yc_multiple_inputs_clang_cl); + Args.eraseArg(options::OPT__SLASH_Yc); + YcArg = nullptr; + } + if (Args.hasArg(options::OPT__SLASH_Y_)) { + // /Y- disables all pch handling. Rather than check for it everywhere, + // just remove clang-cl pch-related flags here. + Args.eraseArg(options::OPT__SLASH_Fp); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + // FIXME: For now, only enable pch support if an internal flag is passed too. + // Remove this once pch support has stabilitzed. + if (!Args.hasArg(options::OPT__SLASH_internal_enable_pch)) { + Args.eraseArg(options::OPT__SLASH_Fp); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + // Construct the actions to perform. ActionList LinkerInputs; @@ -1463,6 +1523,25 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, PL.clear(); types::getCompilationPhases(InputType, PL); + if (YcArg) { + // Add a separate precompile phase for the compile phase. + if (FinalPhase >= phases::Compile) { + llvm::SmallVector PCHPL; + types::getCompilationPhases(types::TY_CXXHeader, PCHPL); + Arg *PchInputArg = MakeInputArg(Args, Opts, YcArg->getValue()); + + // Build the pipeline for the pch file. + Action *ClangClPch = C.MakeAction(*PchInputArg, InputType); + for (phases::ID Phase : PCHPL) + ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch); + assert(ClangClPch); + Actions.push_back(ClangClPch); + // The driver currently exits after the first failed command. This + // relies on that behavior, to make sure if the pch generation fails, + // the main compilation won't run. + } + } + // If the first step comes after the final phase we are doing as part of // this compilation, warn the user about it. phases::ID InitialPhase = PL[0]; @@ -2109,8 +2188,11 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, Output += "-"; Output.append(BoundArch); NamedOutput = C.getArgs().MakeArgString(Output.c_str()); - } else + } else { NamedOutput = getDefaultImageName(); + } + } else if (JA.getType() == types::TY_PCH && IsCLMode()) { + NamedOutput = C.getArgs().MakeArgString(GetClPchPath(C, BaseName).c_str()); } else { const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode()); assert(Suffix && "All types used for output should have a suffix."); @@ -2276,6 +2358,25 @@ std::string Driver::GetTemporaryPath(StringRef Prefix, return Path.str(); } +std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const { + SmallString<128> Output; + if (Arg *FpArg = C.getArgs().getLastArg(options::OPT__SLASH_Fp)) { + // FIXME: If anybody needs it, implement this obscure rule: + // "If you specify a directory without a file name, the default file name + // is VCx0.pch., where x is the major version of Visual C++ in use." + Output = FpArg->getValue(); + + // "If you do not specify an extension as part of the path name, an + // extension of .pch is assumed. " + if (!llvm::sys::path::has_extension(Output)) + Output += ".pch"; + } else { + Output = BaseName; + llvm::sys::path::replace_extension(Output, ".pch"); + } + return Output.str(); +} + const ToolChain &Driver::getToolChain(const ArgList &Args, const llvm::Triple &Target) const { diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 22904e5398a0..3eb23c9ee4c9 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -297,6 +297,29 @@ int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg, return SecondaryStatus; } +ForceSuccessCommand::ForceSuccessCommand(const Action &Source_, + const Tool &Creator_, + const char *Executable_, + const ArgStringList &Arguments_, + ArrayRef Inputs) + : Command(Source_, Creator_, Executable_, Arguments_, Inputs) {} + +void ForceSuccessCommand::Print(raw_ostream &OS, const char *Terminator, + bool Quote, CrashReportInfo *CrashInfo) const { + Command::Print(OS, "", Quote, CrashInfo); + OS << " || (exit 0)" << Terminator; +} + +int ForceSuccessCommand::Execute(const StringRef **Redirects, + std::string *ErrMsg, + bool *ExecutionFailed) const { + int Status = Command::Execute(Redirects, ErrMsg, ExecutionFailed); + (void)Status; + if (ExecutionFailed) + *ExecutionFailed = false; + return 0; +} + void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { for (const auto &Job : *this) diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index a0fceb9672af..0f5c700ce616 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -386,9 +386,71 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, // wonky, but we include looking for .gch so we can support seamless // replacement into a build system already set up to be generating // .gch files. + int YcIndex = -1, YuIndex = -1; + { + int AI = -1; + const Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc); + const Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu); + for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) { + // Walk the whole i_Group and skip non "-include" flags so that the index + // here matches the index in the next loop below. + ++AI; + if (!A->getOption().matches(options::OPT_include)) + continue; + if (YcArg && strcmp(A->getValue(), YcArg->getValue()) == 0) + YcIndex = AI; + if (YuArg && strcmp(A->getValue(), YuArg->getValue()) == 0) + YuIndex = AI; + } + } + if (isa(JA) && YcIndex != -1) { + Driver::InputList Inputs; + D.BuildInputs(getToolChain(), C.getArgs(), Inputs); + assert(Inputs.size() == 1 && "Need one input when building pch"); + CmdArgs.push_back(Args.MakeArgString(Twine("-find-pch-source=") + + Inputs[0].second->getValue())); + } + bool RenderedImplicitInclude = false; + int AI = -1; for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) { - if (A->getOption().matches(options::OPT_include)) { + ++AI; + + if (getToolChain().getDriver().IsCLMode()) { + // In clang-cl mode, /Ycfoo.h means that all code up to a foo.h + // include is compiled into foo.h, and everything after goes into + // the .obj file. /Yufoo.h means that all includes prior to and including + // foo.h are completely skipped and replaced with a use of the pch file + // for foo.h. (Each flag can have at most one value, multiple /Yc flags + // just mean that the last one wins.) If /Yc and /Yu are both present + // and refer to the same file, /Yc wins. + // Note that OPT__SLASH_FI gets mapped to OPT_include. + // FIXME: The code here assumes that /Yc and /Yu refer to the same file. + // cl.exe seems to support both flags with different values, but that + // seems strange (which flag does /Fp now refer to?), so don't implement + // that until someone needs that. + int PchIndex = YcIndex != -1 ? YcIndex : YuIndex; + if (PchIndex != -1) { + if (isa(JA)) { + // When building the pch, skip all includes after the pch. + assert(YcIndex != -1 && PchIndex == YcIndex); + if (AI >= YcIndex) + continue; + } else { + // When using the pch, skip all includes prior to the pch. + if (AI < PchIndex) + continue; + if (AI == PchIndex) { + A->claim(); + CmdArgs.push_back("-include-pch"); + CmdArgs.push_back( + Args.MakeArgString(D.GetClPchPath(C, A->getValue()))); + continue; + } + } + } + } else if (A->getOption().matches(options::OPT_include)) { + // Handling of gcc-style gch precompiled headers. bool IsFirstImplicitInclude = !RenderedImplicitInclude; RenderedImplicitInclude = true; @@ -5654,6 +5716,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, getCLFallback()->GetCommand(C, JA, Output, Inputs, Args, LinkingOutput); C.addCommand(llvm::make_unique( JA, *this, Exec, CmdArgs, Inputs, std::move(CLCommand))); + } else if (Args.hasArg(options::OPT__SLASH_fallback) && + isa(JA)) { + // In /fallback builds, run the main compilation even if the pch generation + // fails, so that the main compilation's fallback to cl.exe runs. + C.addCommand(llvm::make_unique(JA, *this, Exec, + CmdArgs, Inputs)); } else { C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); } diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 04a4c7dbe685..c02eb7610414 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -712,15 +712,18 @@ std::unique_ptr CompilerInstance::createOutputFile( // Initialization Utilities bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){ - return InitializeSourceManager(Input, getDiagnostics(), - getFileManager(), getSourceManager(), - getFrontendOpts()); + return InitializeSourceManager( + Input, getDiagnostics(), getFileManager(), getSourceManager(), + hasPreprocessor() ? &getPreprocessor().getHeaderSearchInfo() : nullptr, + getFrontendOpts()); } +// static bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, DiagnosticsEngine &Diags, FileManager &FileMgr, SourceManager &SourceMgr, + HeaderSearch *HS, const FrontendOptions &Opts) { SrcMgr::CharacteristicKind Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User; @@ -737,7 +740,32 @@ bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, // Figure out where to get and map in the main file. if (InputFile != "-") { - const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/true); + const FileEntry *File; + if (Opts.FindPchSource.empty()) { + File = FileMgr.getFile(InputFile, /*OpenFile=*/true); + } else { + // When building a pch file in clang-cl mode, the .h file is built as if + // it was included by a cc file. Since the driver doesn't know about + // all include search directories, the frontend must search the input + // file through HeaderSearch here, as if it had been included by the + // cc file at Opts.FindPchSource. + const FileEntry *FindFile = FileMgr.getFile(Opts.FindPchSource); + if (!FindFile) { + Diags.Report(diag::err_fe_error_reading) << Opts.FindPchSource; + return false; + } + const DirectoryLookup *UnusedCurDir; + SmallVector, 16> + Includers; + Includers.push_back(std::make_pair(FindFile, FindFile->getDir())); + File = HS->LookupFile(InputFile, SourceLocation(), /*isAngled=*/false, + /*FromDir=*/nullptr, + /*CurDir=*/UnusedCurDir, Includers, + /*SearchPath=*/nullptr, + /*RelativePath=*/nullptr, + /*RequestingModule=*/nullptr, + /*SuggestedModule=*/nullptr, /*SkipCache=*/true); + } if (!File) { Diags.Report(diag::err_fe_error_reading) << InputFile; return false; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index b23eb3543757..9bb33e7a7669 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1107,6 +1107,7 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, = Args.getLastArgValue(OPT_foverride_record_layout_EQ); Opts.AuxTriple = llvm::Triple::normalize(Args.getLastArgValue(OPT_aux_triple)); + Opts.FindPchSource = Args.getLastArgValue(OPT_find_pch_source_EQ); if (const Arg *A = Args.getLastArg(OPT_arcmt_check, OPT_arcmt_modify, diff --git a/clang/test/Driver/Inputs/pchfile.cpp b/clang/test/Driver/Inputs/pchfile.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/clang/test/Driver/Inputs/pchfile.h b/clang/test/Driver/Inputs/pchfile.h new file mode 100644 index 000000000000..1aafaeebe810 --- /dev/null +++ b/clang/test/Driver/Inputs/pchfile.h @@ -0,0 +1,3 @@ +#if defined(ERR_HEADER) +#error nope1 +#endif diff --git a/clang/test/Driver/cl-pch-errorhandling.cpp b/clang/test/Driver/cl-pch-errorhandling.cpp new file mode 100644 index 000000000000..1d239c1268dd --- /dev/null +++ b/clang/test/Driver/cl-pch-errorhandling.cpp @@ -0,0 +1,15 @@ +// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a +// command-line option, e.g. on Mac where %s is commonly under /Users. + +// /Yc but pch generation fails => main file not compiled +// This is a separate file since executing this failure path requires +// code generation, which makes this test require an x86 backend. +// REQUIRES: x86-registered-target + +// RUN: not %clang_cl -internal-enable-pch -Werror /Yc%S/Inputs/pchfile.h /FI%S/Inputs/pchfile.h /c -DERR_HEADER -- %s 2>&1 \ +// RUN: | FileCheck %s + +// CHECK: nope1 +// CHECK-NOT: nope2 + +#error nope2 diff --git a/clang/test/Driver/cl-pch-search.cpp b/clang/test/Driver/cl-pch-search.cpp new file mode 100644 index 000000000000..b215fd7e21f5 --- /dev/null +++ b/clang/test/Driver/cl-pch-search.cpp @@ -0,0 +1,6 @@ +// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a +// command-line option, e.g. on Mac where %s is commonly under /Users. + +// REQUIRES: x86-registered-target +// Check that pchfile.h next to to pchfile.cc is found correctly. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c /Fo%t.obj /Fp%t.pch -- %S/Inputs/pchfile.cpp diff --git a/clang/test/Driver/cl-pch.c b/clang/test/Driver/cl-pch.c new file mode 100644 index 000000000000..df00fff2c4df --- /dev/null +++ b/clang/test/Driver/cl-pch.c @@ -0,0 +1,45 @@ +// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a +// command-line option, e.g. on Mac where %s is commonly under /Users. + +// The main test for clang-cl pch handling is cl-pch.cpp. This file only checks +// a few things for .c inputs. + +// /Yc with a .c file should build a c pch file. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC %s +// CHECK-YC: cc1 +// CHECK-YC: -emit-pch +// CHECK-YC: -o +// CHECK-YC: pchfile.pch +// CHECK-YC: -x +// CHECK-YC: "c" + +// But not if /TP changes the input language to C++. +// RUN: %clang_cl /TP -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCTP %s +// CHECK-YCTP: cc1 +// CHECK-YCTP: -emit-pch +// CHECK-YCTP: -o +// CHECK-YCTP: pchfile.pch +// CHECK-YCTP: -x +// CHECK-YCTP: "c++" + +// Except if a later /TC changes it back. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCTPTC %s +// CHECK-YCTPTC: cc1 +// CHECK-YCTPTC: -emit-pch +// CHECK-YCTPTC: -o +// CHECK-YCTPTC: pchfile.pch +// CHECK-YCTPTC: -x +// CHECK-YCTPTC: "c" + +// Also check lower-case /Tp flag. +// RUN: %clang_cl -internal-enable-pch -Werror /Tp%s /Ycpchfile.h /FIpchfile.h /c -### 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCTp %s +// CHECK-YCTp: cc1 +// CHECK-YCTp: -emit-pch +// CHECK-YCTp: -o +// CHECK-YCTp: pchfile.pch +// CHECK-YCTp: -x +// CHECK-YCTp: "c++" diff --git a/clang/test/Driver/cl-pch.cpp b/clang/test/Driver/cl-pch.cpp new file mode 100644 index 000000000000..f415794064b3 --- /dev/null +++ b/clang/test/Driver/cl-pch.cpp @@ -0,0 +1,309 @@ +// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a +// command-line option, e.g. on Mac where %s is commonly under /Users. + +// /Yc +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC %s +// 1. Build .pch file. +// CHECK-YC: cc1 +// CHECK-YC: -emit-pch +// CHECK-YC: -o +// CHECK-YC: pchfile.pch +// CHECK-YC: -x +// CHECK-YC: "c++" +// 2. Use .pch file. +// CHECK-YC: cc1 +// CHECK-YC: -emit-obj +// CHECK-YC: -include-pch +// CHECK-YC: pchfile.pch + +// /Yc /Fo +// /Fo overrides the .obj output filename, but not the .pch filename +// RUN: %clang_cl -internal-enable-pch -Werror /Fomyobj.obj /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCO %s +// 1. Build .pch file. +// CHECK-YCO: cc1 +// CHECK-YCO: -emit-pch +// CHECK-YCO: -o +// CHECK-YCO: pchfile.pch +// 2. Use .pch file. +// CHECK-YCO: cc1 +// CHECK-YCO: -emit-obj +// CHECK-YCO: -include-pch +// CHECK-YCO: pchfile.pch +// CHECK-YCO: -o +// CHECK-YCO: myobj.obj + +// /Yc /Y- +// /Y- disables pch generation +// RUN: %clang_cl -internal-enable-pch -Werror /Y- /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-Y_ %s +// CHECK-YC-Y_-NOT: -emit-pch +// CHECK-YC-Y_-NOT: -include-pch + +// /Yu +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU %s +// Use .pch file, but don't build it. +// CHECK-YU-NOT: -emit-pch +// CHECK-YU: cc1 +// CHECK-YU: -emit-obj +// CHECK-YU: -include-pch +// CHECK-YU: pchfile.pch + +// /Yu /Y- +// RUN: %clang_cl -internal-enable-pch -Werror /Y- /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU-Y_ %s +// CHECK-YU-Y_-NOT: -emit-pch +// CHECK-YU-Y_-NOT: -include-pch + +// /Yc /Yu -- /Yc overrides /Yc if they both refer to the same file +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-YU %s +// 1. Build .pch file. +// CHECK-YC-YU: cc1 +// CHECK-YC-YU: -emit-pch +// CHECK-YC-YU: -o +// CHECK-YC-YU: pchfile.pch +// 2. Use .pch file. +// CHECK-YC-YU: cc1 +// CHECK-YC-YU: -emit-obj +// CHECK-YC-YU: -include-pch +// CHECK-YC-YU: pchfile.pch + +// If /Yc /Yu refer to different files, semantics are pretty wonky. Since this +// doesn't seem like something that's important in practice, just punt for now. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo1.h /Yufoo2.h /FIfoo1.h /FIfoo2.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-YU-MISMATCH %s +// CHECK-YC-YU-MISMATCH: error: support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored + +// Similarly, punt on /Yc with more than one input file. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo1.h /FIfoo1.h /c -### -- %s %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-MULTIINPUT %s +// CHECK-YC-MULTIINPUT: error: support for '/Yc' with more than one source file not implemented yet; flag ignored + +// /Yc /Yu /Y- +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /Yupchfile.h /FIpchfile.h /Y- /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-YU-Y_ %s +// CHECK-YC-YU-Y_-NOT: -emit-pch +// CHECK-YC-YU-Y_-NOT: -include-pch + +// Test computation of pch filename in various cases. + +// /Yu /Fpout.pch => out.pch is filename +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpout.pch /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YUFP1 %s +// Use .pch file, but don't build it. +// CHECK-YUFP1: -include-pch +// CHECK-YUFP1: out.pch + +// /Yu /Fpout => out.pch is filename (.pch gets added if no extension present) +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpout.pch /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YUFP2 %s +// Use .pch file, but don't build it. +// CHECK-YUFP2: -include-pch +// CHECK-YUFP2: out.pch + +// /Yu /Fpout.bmp => out.bmp is filename (.pch not added when extension present) +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpout.bmp /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YUFP3 %s +// Use .pch file, but don't build it. +// CHECK-YUFP3: -include-pch +// CHECK-YUFP3: out.bmp + +// /Yusub/dir.h => sub/dir.pch +// RUN: %clang_cl -internal-enable-pch -Werror /Yusub/pchfile.h /FIsub/pchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YUFP4 %s +// Use .pch file, but don't build it. +// CHECK-YUFP4: -include-pch +// CHECK-YUFP4: sub/pchfile.pch + +// /Yudir.h /Isub => dir.pch +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Isub /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YUFP5 %s +// Use .pch file, but don't build it. +// CHECK-YUFP5: -include-pch +// CHECK-YUFP5: pchfile.pch + +// FIXME: /Fpdir: use dir/VCx0.pch when dir is directory, where x is major MSVS +// version in use. + +// Spot-check one use of /Fp with /Yc too, else trust the /Yu test cases above +// also all assume to /Yc. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /Fpsub/file.pch /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCFP %s +// 1. Build .pch file. +// CHECK-YCFP: cc1 +// CHECK-YCFP: -emit-pch +// CHECK-YCFP: -o +// CHECK-YCFP: sub/file.pch +// 2. Use .pch file. +// CHECK-YCFP: cc1 +// CHECK-YCFP: -emit-obj +// CHECK-YCFP: -include-pch +// CHECK-YCFP: sub/file.pch + +// /Ycfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h +// => foo1 and foo2 go into pch, foo3 into main compilation +// /Yc +// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCFIFIFI %s +// 1. Build .pch file: Includes foo1.h (but NOT foo3.h) and compiles foo2.h +// CHECK-YCFIFIFI: cc1 +// CHECK-YCFIFIFI: -emit-pch +// CHECK-YCFIFIFI: -include +// CHECK-YCFIFIFI: foo1.h +// CHECK-YCFIFIFI-NOT: foo2.h +// CHECK-YCFIFIFI-NOT: foo3.h +// CHECK-YCFIFIFI: -o +// CHECK-YCFIFIFI: foo2.pch +// CHECK-YCFIFIFI: -x +// CHECK-YCFIFIFI: "c++" +// CHECK-YCFIFIFI: foo2.h +// 2. Use .pch file: Inlucdes foo2.pch and foo3.h +// CHECK-YCFIFIFI: cc1 +// CHECK-YCFIFIFI: -emit-obj +// CHECK-YCFIFIFI-NOT: foo1.h +// CHECK-YCFIFIFI-NOT: foo2.h +// CHECK-YCFIFIFI: -include-pch +// CHECK-YCFIFIFI: foo2.pch +// CHECK-YCFIFIFI: -include +// CHECK-YCFIFIFI: foo3.h + +// /Yucfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h +// => foo1 foo2 filtered out, foo3 into main compilation +// RUN: %clang_cl -internal-enable-pch -Werror /Yufoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YUFIFIFI %s +// Use .pch file, but don't build it. +// CHECK-YUFIFIFI-NOT: -emit-pch +// CHECK-YUFIFIFI: cc1 +// CHECK-YUFIFIFI: -emit-obj +// CHECK-YUFIFIFI-NOT: foo1.h +// CHECK-YUFIFIFI-NOT: foo2.h +// CHECK-YUFIFIFI: -include-pch +// CHECK-YUFIFIFI: foo2.pch +// CHECK-YUFIFIFI: -include +// CHECK-YUFIFIFI: foo3.h + +// FIXME: Implement support for /Ycfoo.h / /Yufoo.h without /FIfoo.h +// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-NOFI %s +// CHECK-YC-NOFI: error: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored +// RUN: %clang_cl -internal-enable-pch -Werror /Yufoo.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU-NOFI %s +// CHECK-YU-NOFI: error: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored + +// /Yc and /FI relative to /I paths... +// The rules are: +// Yu/Yc and FI parameter must match exactly, else it's not found +// Must match literally exactly: /FI./foo.h /Ycfoo.h does _not_ work. +// However, the path can be relative to /I paths. +// FIXME: Update the error messages below once /FI is no longer required, but +// these test cases all should stay failures as they fail with cl.exe. + +// Check that ./ isn't canonicalized away. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-I1 %s +// CHECK-YC-I1: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored + +// Check that ./ isn't canonicalized away. +// RUN: %clang_cl -internal-enable-pch -Werror /Yc./pchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-I2 %s +// CHECK-YC-I2: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored + +// With an actual /I argument. +// RUN: %clang_cl -internal-enable-pch -Werror /Ifoo /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-I3 %s +// 1. This writes pchfile.pch into the root dir, even if this will pick up +// foo/pchfile.h +// CHECK-YC-I3: cc1 +// CHECK-YC-I3: -emit-pch +// CHECK-YC-I3: -o +// CHECK-YC-I3: pchfile.pch +// 2. Use .pch file. +// CHECK-YC-I3: cc1 +// CHECK-YC-I3: -emit-obj +// CHECK-YC-I3: -include-pch +// CHECK-YC-I3: pchfile.pch + +// Check that ./ isn't canonicalized away for /Yu either. +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU-I1 %s +// CHECK-YU-I1: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored + +// But /FIfoo/bar.h /Ycfoo\bar.h does work, as does /FIfOo.h /Ycfoo.H +// FIXME: This part isn't implemented yet. The following two tests should not +// show an error but do regular /Yu handling. +// RUN: %clang_cl -internal-enable-pch -Werror /YupchFILE.h /FI./pchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU-CASE %s +// CHECK-YU-CASE: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored +// RUN: %clang_cl -internal-enable-pch -Werror /Yu./pchfile.h /FI.\pchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU-SLASH %s +// CHECK-YU-SLASH: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored + +// cl.exe warns on multiple /Yc, /Yu, /Fp arguments, but clang-cl silently just +// uses the last one. This is true for e.g. /Fo too, so not warning on this +// is self-consistent with clang-cl's flag handling. + +// Interaction with /fallback + +// /Yc /fallback => /Yc not passed on (but /FI is) +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /Fpfoo.pch /fallback /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YC-FALLBACK %s +// Note that in /fallback builds, if creation of the pch fails the main compile +// does still run so that /fallback can have an effect (this part is not tested) +// CHECK-YC-FALLBACK: cc1 +// CHECK-YC-FALLBACK: -emit-obj +// CHECK-YC-FALLBACK: -include-pch +// CHECK-YC-FALLBACK: foo.pch +// CHECK-YC-FALLBACK: || +// CHECK-YC-FALLBACK: cl.exe +// CHECK-YC-FALLBACK-NOT: -include-pch +// CHECK-YC-FALLBACK-NOT: /Ycpchfile.h +// CHECK-YC-FALLBACK: /FIpchfile.h +// CHECK-YC-FALLBACK-NOT: /Fpfoo.pch + +// /Yu /fallback => /Yu not passed on (but /FI is) +// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpfoo.pch /fallback /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YU-FALLBACK %s +// CHECK-YU-FALLBACK-NOT: -emit-pch +// CHECK-YU-FALLBACK: cc1 +// CHECK-YU-FALLBACK: -emit-obj +// CHECK-YU-FALLBACK: -include-pch +// CHECK-YU-FALLBACK: foo.pch +// CHECK-YU-FALLBACK: || +// CHECK-YU-FALLBACK: cl.exe +// CHECK-YU-FALLBACK-NOT: -include-pch +// CHECK-YU-FALLBACK-NOT: /Yupchfile.h +// CHECK-YU-FALLBACK: /FIpchfile.h +// CHECK-YU-FALLBACK-NOT: /Fpfoo.pch + +// /FI without /Yu => pch file not used, even if it exists (different from +// -include, which picks up .gch files if they exist). +// RUN: touch %t.pch +// RUN: %clang_cl -internal-enable-pch -Werror /FI%t.pch /Fp%t.pch /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-FI %s +// CHECK-FI-NOT: -include-pch +// CHECK-FI: -include + +// Test interaction of /Yc with language mode flags. + +// If /TC changes the input language to C, a c pch file should be produced. +// RUN: %clang_cl /TC -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCTC %s +// CHECK-YCTC: cc1 +// CHECK-YCTC: -emit-pch +// CHECK-YCTC: -o +// CHECK-YCTC: pchfile.pch +// CHECK-YCTC: -x +// CHECK-YCTP: "c" + +// Also check lower-case /Tc variant. +// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### /Tc%s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-YCTc %s +// CHECK-YCTc: cc1 +// CHECK-YCTc: -emit-pch +// CHECK-YCTc: -o +// CHECK-YCTc: pchfile.pch +// CHECK-YCTc: -x +// CHECK-YCTc: "c"