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, ignoring flag">, + InGroup; +def warn_drv_ycyu_different_arg_clang_cl : Warning< + "support for '/Yc' and '/Yu' with different filenames not implemented yet, ignoring flags">, + InGroup; +def warn_drv_ycyu_no_fi_arg_clang_cl : Warning< + "support for '%0' without a corresponding /FI flag not implemented yet, ignoring flag">, + InGroup; +def warn_drv_yc_multiple_inputs_clang_cl : Warning< + "support for '/Yc' with more than one input source file not implemented yet, ignoring flag">, + 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/Action.h =================================================================== --- include/clang/Driver/Action.h +++ include/clang/Driver/Action.h @@ -78,10 +78,23 @@ ActionList Inputs; + /// Inputs that this Action depends on, but that aren't explicitly passed + /// as arguments to the action. + /// + /// For example, `clang-cl /Yc /Fifoo.h foo.cc` will first compile foo.h into + /// a pch file, and then load the pch file when compiling foo.cc. If building + /// the pch file fails, foo.cc should not be compiled, so the action building + /// the pch file is an implicit input of the compile action. + ActionList ImplicitInputs; + protected: Action(ActionClass Kind, types::ID Type) : Action(Kind, ActionList(), Type) {} Action(ActionClass Kind, Action *Input, types::ID Type) : Action(Kind, ActionList({Input}), Type) {} + Action(ActionClass Kind, Action *Input, const ActionList &ImplicitInputs, + types::ID Type) + : Kind(Kind), Type(Type), Inputs({Input}), + ImplicitInputs(ImplicitInputs) {} Action(ActionClass Kind, Action *Input) : Action(Kind, ActionList({Input}), Input->getType()) {} Action(ActionClass Kind, const ActionList &Inputs, types::ID Type) @@ -108,6 +121,16 @@ input_const_range inputs() const { return input_const_range(input_begin(), input_end()); } + + input_const_iterator implicit_input_begin() const { + return ImplicitInputs.begin(); + } + input_const_iterator implicit_input_end() const { + return ImplicitInputs.end(); + } + input_const_range implicit_inputs() const { + return input_const_range(implicit_input_begin(), implicit_input_end()); + } }; class InputAction : public Action { @@ -183,6 +206,8 @@ virtual void anchor(); protected: JobAction(ActionClass Kind, Action *Input, types::ID Type); + JobAction(ActionClass Kind, Action *Input, const ActionList &ImplicitInputs, + types::ID Type); JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type); public: @@ -235,7 +260,8 @@ class CompileJobAction : public JobAction { void anchor() override; public: - CompileJobAction(Action *Input, types::ID OutputType); + CompileJobAction(Action *Input, const ActionList &ImplicitInputs, + types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == CompileJobClass; Index: include/clang/Driver/CLCompatOptions.td =================================================================== --- include/clang/Driver/CLCompatOptions.td +++ include/clang/Driver/CLCompatOptions.td @@ -252,6 +252,11 @@ def _SLASH_Zl : CLFlag<"Zl">, HelpText<"Don't mention any default libraries in the object file">; +def _SLASH_Yc : CLJoined<"Yc">; +def _SLASH_Y_ : CLFlag<"Y-">; +def _SLASH_Yu : CLJoined<"Yu">; +def _SLASH_Fp : CLJoined<"Fp">; + // Ignored: def _SLASH_analyze_ : CLIgnoredFlag<"analyze-">; @@ -294,7 +299,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 +336,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 @@ -375,7 +375,8 @@ /// \p Phase on the \p Input, taking in to account arguments /// like -fsyntax-only or --analyze. Action *ConstructPhaseAction(Compilation &C, const llvm::opt::ArgList &Args, - phases::ID Phase, Action *Input) const; + phases::ID Phase, Action *Input, + Action *ClangClPchInput = nullptr) const; /// BuildJobsForAction - Construct the jobs to perform for the action \p A and /// return an InputInfo for the result of running \p A. Will only construct @@ -414,6 +415,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: lib/Driver/Action.cpp =================================================================== --- lib/Driver/Action.cpp +++ lib/Driver/Action.cpp @@ -96,6 +96,10 @@ JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type) : Action(Kind, Input, Type) {} +JobAction::JobAction(ActionClass Kind, Action *Input, + const ActionList &ImplicitInputs, types::ID Type) + : Action(Kind, Input, ImplicitInputs, Type) {} + JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type) : Action(Kind, Inputs, Type) { } @@ -122,8 +126,10 @@ void CompileJobAction::anchor() {} -CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType) - : JobAction(CompileJobClass, Input, OutputType) {} +CompileJobAction::CompileJobAction(Action *Input, + const ActionList &ImplicitInputs, + types::ID OutputType) + : JobAction(CompileJobClass, Input, ImplicitInputs, OutputType) {} void BackendJobAction::anchor() {} Index: lib/Driver/Compilation.cpp =================================================================== --- lib/Driver/Compilation.cpp +++ lib/Driver/Compilation.cpp @@ -176,9 +176,13 @@ if (A == &(CI->second->getSource())) return true; + // Check if any of the actions that A depends on failed. for (const Action *AI : A->inputs()) if (ActionFailed(AI, FailingCommands)) return true; + for (const Action *AI : A->implicit_inputs()) + if (ActionFailed(AI, FailingCommands)) + return true; return false; } Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -1439,6 +1439,58 @@ } } + // 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 Found = false; + for (const Arg *Inc : Args.filtered(options::OPT_include)) { + // FIXME: Do case-insensitive matching and consider / and \ as equal. + if (Inc->getValue() == Val) + Found = true; + } + if (!Found) { + 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; + } + // Construct the actions to perform. ActionList LinkerInputs; @@ -1450,6 +1502,23 @@ PL.clear(); types::getCompilationPhases(InputType, PL); + Action* ClangClPch = nullptr; + 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 *InputArg = MakeInputArg(Args, Opts, YcArg->getValue()); + + // Build the pipeline for the pch file. + ClangClPch = C.MakeAction(*InputArg, InputType); + for (phases::ID Phase : PCHPL) + ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch); + assert(ClangClPch); + Actions.push_back(ClangClPch); + } + } + // 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]; @@ -1513,7 +1582,10 @@ continue; // Otherwise construct the appropriate action. - Current = ConstructPhaseAction(C, Args, Phase, Current); + if (Phase == phases::Compile) + Current = ConstructPhaseAction(C, Args, Phase, Current, ClangClPch); + else + Current = ConstructPhaseAction(C, Args, Phase, Current); if (InputType == types::TY_CUDA && Phase == CudaInjectionPhase) { Current = buildCudaActions(C, Args, InputArg, Current, Actions); @@ -1551,7 +1623,8 @@ } Action *Driver::ConstructPhaseAction(Compilation &C, const ArgList &Args, - phases::ID Phase, Action *Input) const { + phases::ID Phase, Action *Input, + Action *ClangClPchInput) const { llvm::PrettyStackTraceString CrashInfo("Constructing phase actions"); // Build the appropriate action. switch (Phase) { @@ -1582,24 +1655,45 @@ return C.MakeAction(Input, OutputTy); } case phases::Compile: { + ActionList ImplicitInputs; + + // Don't make the pch action an implicit input in /fallback builds, so that + // if the pch compilation fails, the main compilation with the fallback + // still runs. + if (ClangClPchInput && !Args.hasArg(options::OPT__SLASH_fallback)) + ImplicitInputs.push_back(ClangClPchInput); + if (Args.hasArg(options::OPT_fsyntax_only)) - return C.MakeAction(Input, types::TY_Nothing); + return C.MakeAction(Input, ImplicitInputs, + types::TY_Nothing); if (Args.hasArg(options::OPT_rewrite_objc)) - return C.MakeAction(Input, types::TY_RewrittenObjC); + return C.MakeAction(Input, ImplicitInputs, + types::TY_RewrittenObjC); if (Args.hasArg(options::OPT_rewrite_legacy_objc)) - return C.MakeAction(Input, + return C.MakeAction(Input, ImplicitInputs, types::TY_RewrittenLegacyObjC); - if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) + if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) { + assert(!ClangClPchInput); return C.MakeAction(Input, types::TY_Plist); - if (Args.hasArg(options::OPT__migrate)) + } + if (Args.hasArg(options::OPT__migrate)) { + assert(!ClangClPchInput); return C.MakeAction(Input, types::TY_Remap); + } if (Args.hasArg(options::OPT_emit_ast)) - return C.MakeAction(Input, types::TY_AST); + return C.MakeAction(Input, ImplicitInputs, + types::TY_AST); if (Args.hasArg(options::OPT_module_file_info)) - return C.MakeAction(Input, types::TY_ModuleFile); - if (Args.hasArg(options::OPT_verify_pch)) + return C.MakeAction(Input, ImplicitInputs, + types::TY_ModuleFile); + if (Args.hasArg(options::OPT_verify_pch)) { + assert(!ClangClPchInput); + if (Args.hasArg(options::OPT_verify_pch)) + return C.MakeAction(Input, types::TY_Nothing); return C.MakeAction(Input, types::TY_Nothing); - return C.MakeAction(Input, types::TY_LLVM_BC); + } + return C.MakeAction(Input, ImplicitInputs, + types::TY_LLVM_BC); } case phases::Backend: { if (isUsingLTO()) { @@ -2085,6 +2179,8 @@ NamedOutput = C.getArgs().MakeArgString(Output.c_str()); } 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."); @@ -2250,6 +2346,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/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -386,9 +386,63 @@ // 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 the 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; + } + } + 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)) { bool IsFirstImplicitInclude = !RenderedImplicitInclude; RenderedImplicitInclude = true; 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.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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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, ignoring flags + +// Similarly, punt on /Yc with more than one input file. +// RUN: %clang_cl -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 input source file not implemented yet, ignoring flag + +// /Yc /Yu /Y- +// RUN: %clang_cl -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 -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 -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 -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 -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 -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 -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 -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 -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 -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, ignoring flag +// RUN: %clang_cl -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, ignoring flag + +// /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 -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, ignoring flag + +// Check that ./ isn't canonicalized away. +// RUN: %clang_cl -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, ignoring flag + +// With an actual /I argument. +// RUN: %clang_cl -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 -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, ignoring flag + +// 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 -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, ignoring flag +// RUN: %clang_cl -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, ignoring flag + +// 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 -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 -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 -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 -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 -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"