Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -98,6 +98,20 @@ 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< Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -785,6 +785,8 @@ MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag, MicrosoftCommentPaste, MicrosoftEndOfFile]>; +def ClangClPch : DiagGroup<"clang-cl-pch">; + def ObjCNonUnifiedException : DiagGroup<"objc-nonunified-exceptions">; def ObjCProtocolMethodImpl : DiagGroup<"objc-protocol-method-implementation">; Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -495,6 +495,10 @@ 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 Index: include/clang/Driver/CLCompatOptions.td =================================================================== --- include/clang/Driver/CLCompatOptions.td +++ include/clang/Driver/CLCompatOptions.td @@ -20,6 +20,9 @@ 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 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_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_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_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">; Index: include/clang/Driver/Driver.h =================================================================== --- include/clang/Driver/Driver.h +++ include/clang/Driver/Driver.h @@ -423,6 +423,9 @@ /// 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; Index: include/clang/Driver/Job.h =================================================================== --- include/clang/Driver/Job.h +++ include/clang/Driver/Job.h @@ -138,6 +138,21 @@ std::unique_ptr Fallback; }; +/// Like Command, but always pretends that the wrapped command succeeded. +/// the primary command crashes. +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: Index: include/clang/Frontend/CompilerInstance.h =================================================================== --- include/clang/Frontend/CompilerInstance.h +++ include/clang/Frontend/CompilerInstance.h @@ -748,10 +748,11 @@ /// /// \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); /// } Index: include/clang/Frontend/FrontendOptions.h =================================================================== --- include/clang/Frontend/FrontendOptions.h +++ include/clang/Frontend/FrontendOptions.h @@ -266,6 +266,10 @@ /// \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), Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -1452,6 +1452,66 @@ } } + // 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 @@ 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 @@ 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 @@ 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(); + + // Add pch if needed: "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 { Index: lib/Driver/Job.cpp =================================================================== --- lib/Driver/Job.cpp +++ lib/Driver/Job.cpp @@ -297,6 +297,29 @@ 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) Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -386,9 +386,71 @@ // 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 @@ 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)); } Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -712,15 +712,18 @@ // 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,27 @@ // 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 { + 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; Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1107,6 +1107,7 @@ = 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, Index: test/Driver/Inputs/pchfile.h =================================================================== --- test/Driver/Inputs/pchfile.h +++ test/Driver/Inputs/pchfile.h @@ -0,0 +1,3 @@ +#if defined(ERR_HEADER) +#error nope1 +#endif Index: test/Driver/cl-pch-errorhandling.cpp =================================================================== --- test/Driver/cl-pch-errorhandling.cpp +++ 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 Index: test/Driver/cl-pch-search.cpp =================================================================== --- test/Driver/cl-pch-search.cpp +++ test/Driver/cl-pch-search.cpp @@ -0,0 +1,5 @@ +// 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. + +// 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 Index: test/Driver/cl-pch.c =================================================================== --- test/Driver/cl-pch.c +++ 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++" Index: test/Driver/cl-pch.cpp =================================================================== --- test/Driver/cl-pch.cpp +++ 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"