Index: include/clang/Basic/DiagnosticLexKinds.td =================================================================== --- include/clang/Basic/DiagnosticLexKinds.td +++ include/clang/Basic/DiagnosticLexKinds.td @@ -404,6 +404,14 @@ def err_pp_directive_required : Error< "%0 must be used within a preprocessing directive">; def err_pp_file_not_found : Error<"'%0' file not found">, DefaultFatal; +def err_pp_through_header_not_found : Error< + "'%0' required for precompiled header not found">, DefaultFatal; +def err_pp_through_header_not_seen : Error< + "#include of '%0' not seen while attempting to " + "%select{create|use}1 precompiled header">, DefaultFatal; +def err_pp_macro_def_mismatch_with_pch : Warning< + "definition of macro %0 does not match definition in precompiled header">, + InGroup; def err_pp_file_not_found_not_fatal : Error< "'%0' file not found with include; use \"quotes\" instead">; def err_pp_error_opening_file : Error< Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -599,10 +599,9 @@ 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.">; +def pch_through_header_EQ : Joined<["-"], "pch-through-header=">, + HelpText<"When creating a pch stop at this file. When using a pch start " + "after this file.">; def fno_pch_timestamp : Flag<["-"], "fno-pch-timestamp">, HelpText<"Disable inclusion of timestamp in precompiled headers">; Index: include/clang/Frontend/FrontendOptions.h =================================================================== --- include/clang/Frontend/FrontendOptions.h +++ include/clang/Frontend/FrontendOptions.h @@ -432,10 +432,6 @@ /// Auxiliary triple for CUDA compilation. std::string AuxTriple; - /// If non-empty, search the pch input file as if it was a header - /// included by this file. - std::string FindPchSource; - /// Filename to write statistics to. std::string StatsFile; Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -720,6 +720,12 @@ /// The file ID for the preprocessor predefines. FileID PredefinesFileID; + /// The file ID for the PCH through header. + FileID PCHThroughHeaderFileID; + + /// Whether tokens are being skipped until the through header is seen. + bool SkippingUntilPCHThroughHeader = false; + /// \{ /// Cache of macro expanders to reduce malloc traffic. enum { TokenLexerCacheSize = 8 }; @@ -1140,6 +1146,22 @@ /// all macro expansions, macro definitions, etc. void createPreprocessingRecord(); + /// Returns true if the FileEntry is the PCH through header. + bool isPCHThroughHeader(const FileEntry *File); + + /// True if creating a PCH with a through header. + bool creatingPCHWithThroughHeader(); + + /// True if using a PCH with a through header. + bool usingPCHWithThroughHeader(); + + /// Skip tokens until after the #include of the through header. + void SkipTokensUntilPCHThroughHeader(); + + /// Process directives while skipping until the through header is found. + void HandleSkippedThroughHeaderDirective(Token &Result, + SourceLocation HashLoc); + /// Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. void EnterMainSourceFile(); @@ -2020,6 +2042,13 @@ PredefinesFileID = FID; } + /// Set the FileID for the PCH through header. + void setPCHThroughHeaderFileID(FileID FID) { + assert(PCHThroughHeaderFileID.isInvalid() && + "PCHThroughHeaderFileID already set!"); + PCHThroughHeaderFileID = FID; + } + /// Returns true if we are lexing from a file and not a /// pragma or a macro. static bool IsFileLexer(const Lexer* L, const PreprocessorLexer* P) { Index: include/clang/Lex/PreprocessorOptions.h =================================================================== --- include/clang/Lex/PreprocessorOptions.h +++ include/clang/Lex/PreprocessorOptions.h @@ -54,6 +54,9 @@ /// definitions and expansions. bool DetailedRecord = false; + /// If non-empty, is the start/stop point for a precompiled header. + std::string PCHThroughHeader; + /// The implicit PCH included at the start of the translation unit, or empty. std::string ImplicitPCHInclude; Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -2859,22 +2859,6 @@ 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); @@ -2944,11 +2928,9 @@ const types::ID HeaderType = lookupHeaderTypeForSourceType(InputType); llvm::SmallVector PCHPL; types::getCompilationPhases(HeaderType, PCHPL); - Arg *PchInputArg = MakeInputArg(Args, *Opts, YcArg->getValue()); - // Build the pipeline for the pch file. Action *ClangClPch = - C.MakeAction(*PchInputArg, HeaderType); + C.MakeAction(*InputArg, HeaderType); for (phases::ID Phase : PCHPL) ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch); assert(ClangClPch); @@ -4124,6 +4106,9 @@ // extension of .pch is assumed. " if (!llvm::sys::path::has_extension(Output)) Output += ".pch"; + } else if (Arg *YcArg = C.getArgs().getLastArg(options::OPT__SLASH_Yc)) { + Output = YcArg->getValue(); + llvm::sys::path::replace_extension(Output, ".pch"); } else { Output = BaseName; llvm::sys::path::replace_extension(Output, ".pch"); Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -1076,73 +1076,26 @@ // 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; + + if (getToolChain().getDriver().IsCLMode()) { 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 (YcArg || YuArg) { + StringRef ThroughHeader = YcArg ? YcArg->getValue() : YuArg->getValue(); + if (!isa(JA)) { + CmdArgs.push_back("-include-pch"); + CmdArgs.push_back(Args.MakeArgString(D.GetClPchPath(C, ThroughHeader))); + } + CmdArgs.push_back( + Args.MakeArgString(Twine("-pch-through-header=") + ThroughHeader)); } } - 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)) { ++AI; - - if (getToolChain().getDriver().IsCLMode() && - A->getOption().matches(options::OPT_include)) { - // 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 it. - 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) { - A->claim(); - 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)) { + if (A->getOption().matches(options::OPT_include)) { // Handling of gcc-style gch precompiled headers. bool IsFirstImplicitInclude = !RenderedImplicitInclude; RenderedImplicitInclude = true; Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -852,36 +852,7 @@ // Figure out where to get and map in the main file. if (InputFile != "-") { - 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, /*IsMapped=*/nullptr, - /*SkipCache=*/true); - // Also add the header to /showIncludes output. - if (File) - DepOpts.ShowIncludesPretendHeader = File->getName(); - } + const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/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 @@ -1541,7 +1541,6 @@ = 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); Opts.StatsFile = Args.getLastArgValue(OPT_stats_file); if (const Arg *A = Args.getLastArg(OPT_arcmt_check, @@ -2800,6 +2799,7 @@ frontend::ActionKind Action) { Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch); Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth); + Opts.PCHThroughHeader = Args.getLastArgValue(OPT_pch_through_header_EQ); if (const Arg *A = Args.getLastArg(OPT_token_cache)) Opts.TokenCache = A->getValue(); else Index: lib/Lex/PPDirectives.cpp =================================================================== --- lib/Lex/PPDirectives.cpp +++ lib/Lex/PPDirectives.cpp @@ -887,6 +887,22 @@ bool save; }; +/// Process a directive while looking for the through header. +/// Only #include (to check if it is the through header) and #define (to warn +/// about macros that don't match the PCH) are handled. All other directives +/// are completely discarded. +void Preprocessor::HandleSkippedThroughHeaderDirective(Token &Result, + SourceLocation HashLoc) { + if (const IdentifierInfo *II = Result.getIdentifierInfo()) { + if (II->getPPKeywordID() == tok::pp_include) + return HandleIncludeDirective(HashLoc, Result); + if (II->getPPKeywordID() == tok::pp_define) + return HandleDefineDirective(Result, + /*ImmediatelyAfterHeaderGuard=*/false); + } + DiscardUntilEndOfDirective(); +} + /// HandleDirective - This callback is invoked when the lexer sees a # token /// at the start of a line. This consumes the directive, modifies the /// lexer/preprocessor state, and advances the lexer(s) so that the next token @@ -948,6 +964,9 @@ // and reset to previous state when returning from this function. ResetMacroExpansionHelper helper(this); + if (SkippingUntilPCHThroughHeader) + return HandleSkippedThroughHeaderDirective(Result, SavedHash.getLocation()); + switch (Result.getKind()) { case tok::eod: return; // null directive. @@ -1862,6 +1881,12 @@ } } + if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) { + if (isPCHThroughHeader(File)) + SkippingUntilPCHThroughHeader = false; + return; + } + // Should we enter the source file? Set to false if either the source file is // known to have no effect beyond its effect on module visibility -- that is, // if it's got an include guard that is already defined or is a modular header @@ -2587,7 +2612,15 @@ } } - + // When skipping just warn about macros that do not match. + if (SkippingUntilPCHThroughHeader) { + const MacroInfo *OtherMI = getMacroInfo(MacroNameTok.getIdentifierInfo()); + if (!OtherMI || !MI->isIdenticalTo(*OtherMI, *this, + /*Syntactic=*/LangOpts.MicrosoftExt)) + Diag(MI->getDefinitionLoc(), diag::err_pp_macro_def_mismatch_with_pch) + << MacroNameTok.getIdentifierInfo(); + return; + } // Finally, if this identifier already had a macro defined for it, verify that // the macro bodies are identical, and issue diagnostics if they are not. Index: lib/Lex/PPLexerChange.cpp =================================================================== --- lib/Lex/PPLexerChange.cpp +++ lib/Lex/PPLexerChange.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/HeaderSearch.h" @@ -340,6 +341,7 @@ // module, complain and close it now. // FIXME: This is not correct if we are building a module from PTH. const bool LeavingSubmodule = CurLexer && CurLexerSubmodule; + bool LeavingPCHThroughHeader = false; if ((LeavingSubmodule || IncludeMacroStack.empty()) && !BuildingSubmoduleStack.empty() && BuildingSubmoduleStack.back().IsPragma) { @@ -481,6 +483,12 @@ Result.setAnnotationValue(M); } + bool FoundPCHThroughHeader = false; + if (CurPPLexer && creatingPCHWithThroughHeader() && + isPCHThroughHeader( + SourceMgr.getFileEntryForID(CurPPLexer->getFileID()))) + FoundPCHThroughHeader = true; + // We're done with the #included file. RemoveTopOfLexerStack(); @@ -500,8 +508,16 @@ if (ExitedFromPredefinesFile) replayPreambleConditionalStack(); - // Client should lex another token unless we generated an EOM. - return LeavingSubmodule; + if (!isEndOfMacro && CurPPLexer && FoundPCHThroughHeader && + (isInPrimaryFile() || + CurPPLexer->getFileID() == getPredefinesFileID())) { + // Leaving the through header. Continue directly to end of main file + // processing. + LeavingPCHThroughHeader = true; + } else { + // Client should lex another token unless we generated an EOM. + return LeavingSubmodule; + } } // If this is the end of the main file, form an EOF token. @@ -522,6 +538,12 @@ Result.setLocation(Result.getLocation().getLocWithOffset(-1)); } + if (creatingPCHWithThroughHeader() && !LeavingPCHThroughHeader) { + // Reached the end of the compilation without finding the through header. + Diag(CurLexer->getFileLoc(), diag::err_pp_through_header_not_seen) + << PPOpts->PCHThroughHeader << 0; + } + if (!isIncrementalProcessingEnabled()) // We're done with lexing. CurLexer.reset(); Index: lib/Lex/Preprocessor.cpp =================================================================== --- lib/Lex/Preprocessor.cpp +++ lib/Lex/Preprocessor.cpp @@ -149,6 +149,11 @@ Ident_AbnormalTermination = nullptr; } + // If using a PCH with a through header, start skipping tokens. + if (!this->PPOpts->PCHThroughHeader.empty() && + !this->PPOpts->ImplicitPCHInclude.empty()) + SkippingUntilPCHThroughHeader = true; + if (this->PPOpts->GeneratePreamble) PreambleConditionalStack.startRecording(); } @@ -551,6 +556,70 @@ // Start parsing the predefines. EnterSourceFile(FID, nullptr, SourceLocation()); + + if (!PPOpts->PCHThroughHeader.empty()) { + // Lookup and save the FileID for the through header. If it isn't found + // in the search path, it's a fatal error. + const DirectoryLookup *CurDir; + const FileEntry *File = LookupFile( + SourceLocation(), PPOpts->PCHThroughHeader, + /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir, + /*SearchPath=*/nullptr, /*RelativePath=*/nullptr, + /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr); + if (!File) { + Diag(SourceLocation(), diag::err_pp_through_header_not_found) + << PPOpts->PCHThroughHeader; + return; + } + setPCHThroughHeaderFileID( + SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User)); + } + + // Skip tokens from the Predefines and if needed the main file. + if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) + SkipTokensUntilPCHThroughHeader(); +} + +/// Return true if the FileEntry is the PCH through header. +bool Preprocessor::isPCHThroughHeader(const FileEntry *FE) { + assert(PCHThroughHeaderFileID.isValid() && + "Invalid PCH through header FileID"); + return FE == SourceMgr.getFileEntryForID(PCHThroughHeaderFileID); +} + +/// Return true if creating a PCH with a through header. +bool Preprocessor::creatingPCHWithThroughHeader() { + return TUKind == TU_Prefix && !PPOpts->PCHThroughHeader.empty() && + PCHThroughHeaderFileID.isValid(); +} + +/// Return true if using a PCH with a through header. +bool Preprocessor::usingPCHWithThroughHeader() { + return TUKind != TU_Prefix && !PPOpts->PCHThroughHeader.empty() && + PCHThroughHeaderFileID.isValid(); +} + +/// Skip tokens until after the #include of the through header. +/// Tokens in the predefines file and the main file may be skipped. If the end +/// of the predefines file is reached, skipping continues into the main file. +/// If the end of the main file is reached, it's a fatal error. +void Preprocessor::SkipTokensUntilPCHThroughHeader() +{ + bool ReachedMainFileEOF = false; + Token Tok; + while (true) { + bool InPredefines = (CurLexer->getFileID() == getPredefinesFileID()); + CurLexer->Lex(Tok); + if (Tok.is(tok::eof) && !InPredefines) { + ReachedMainFileEOF = true; + break; + } + if (!SkippingUntilPCHThroughHeader) + break; + } + if (ReachedMainFileEOF) + Diag(SourceLocation(), diag::err_pp_through_header_not_seen) + << PPOpts->PCHThroughHeader << 1; } void Preprocessor::replayPreambleConditionalStack() { Index: lib/Parse/ParseAST.cpp =================================================================== --- lib/Parse/ParseAST.cpp +++ lib/Parse/ParseAST.cpp @@ -141,6 +141,12 @@ CleanupParser(ParseOP.get()); S.getPreprocessor().EnterMainSourceFile(); + if (!S.getPreprocessor().getCurrentLexer()) { + // If a PCH through header is specified that does not have an include in + // the source, there won't be any tokens or a Lexer. + return; + } + P.Initialize(); Parser::DeclGroupPtrTy ADecl; Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -704,6 +704,17 @@ // Compute the #include and #include_macros lines we need. for (unsigned I = 0, N = ExistingPPOpts.Includes.size(); I != N; ++I) { StringRef File = ExistingPPOpts.Includes[I]; + + if (!ExistingPPOpts.ImplicitPCHInclude.empty() && + !ExistingPPOpts.PCHThroughHeader.empty()) { + // In case the through header is an include, we must add all the includes + // to the predefines so the start point can be determined. + SuggestedPredefines += "#include \""; + SuggestedPredefines += File; + SuggestedPredefines += "\"\n"; + continue; + } + if (File == ExistingPPOpts.ImplicitPCHInclude) continue; Index: test/Driver/cl-pch-search.cpp =================================================================== --- test/Driver/cl-pch-search.cpp +++ test/Driver/cl-pch-search.cpp @@ -3,4 +3,4 @@ // REQUIRES: x86-registered-target // Check that pchfile.h next to pchfile.cc is found correctly. -// RUN: %clang_cl -Werror --target=x86_64-windows /Ycpchfile.h /FIpchfile.h /c /Fo%t.obj /Fp%t.pch -- %S/Inputs/pchfile.cpp +// RUN: %clang_cl -Werror -Wno-microsoft-include --target=x86_64-windows /Ycpchfile.h /FIpchfile.h /c /Fo%t.obj /Fp%t.pch -- %S/Inputs/pchfile.cpp Index: test/Driver/cl-pch.cpp =================================================================== --- test/Driver/cl-pch.cpp +++ test/Driver/cl-pch.cpp @@ -151,26 +151,37 @@ // 1. Build .pch file: Includes foo1.h (but NOT foo3.h) and compiles foo2.h // CHECK-YCFIFIFI: cc1 // CHECK-YCFIFIFI: -emit-pch +// CHECK-YCFIFIFI: -pch-through-header=foo2.h // CHECK-YCFIFIFI: -include // CHECK-YCFIFIFI: foo1.h -// CHECK-YCFIFIFI-NOT: foo2.h -// CHECK-YCFIFIFI-NOT: foo3.h +// CHECK-YCFIFIFI: -include +// CHECK-YCFIFIFI: foo2.h +// CHECK-YCFIFIFI: -include +// CHECK-YCFIFIFI: foo3.h // CHECK-YCFIFIFI: -o // CHECK-YCFIFIFI: foo2.pch // CHECK-YCFIFIFI: -x // CHECK-YCFIFIFI: "c++-header" -// CHECK-YCFIFIFI: foo2.h +// CHECK-YCFIFIFI: cl-pch.cpp // 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: -pch-through-header=foo2.h +// CHECK-YCFIFIFI: -include +// CHECK-YCFIFIFI: foo1.h +// CHECK-YCFIFIFI: -include +// CHECK-YCFIFIFI: foo2.h // CHECK-YCFIFIFI: -include // CHECK-YCFIFIFI: foo3.h +// CHECK-YCFIFIFI: -o +// CHECK-YCFIFIFI: cl-pch.obj +// CHECK-YCFIFIFI: -x +// CHECK-YCFIFIFI: "c++" +// CHECK-YCFIFIFI: cl-pch.cpp -// /Yucfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h +// /Yufoo2.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 @@ -178,38 +189,47 @@ // 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: -pch-through-header=foo2.h +// CHECK-YUFIFIFI: -include +// CHECK-YUFIFIFI: foo1.h +// CHECK-YUFIFIFI: -include +// CHECK-YUFIFIFI: foo2.h // CHECK-YUFIFIFI: -include // CHECK-YUFIFIFI: foo3.h -// FIXME: Implement support for /Ycfoo.h / /Yufoo.h without /FIfoo.h +// Test /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; flag ignored +// 1. Precompile +// CHECK-YC-NOFI: cc1 +// CHECK-YC-NOFI: -emit-pch +// CHECK-YC-NOFI: -pch-through-header=foo.h +// CHECK-YC-NOFI: -o +// CHECK-YC-NOFI: foo.pch +// CHECK-YC-NOFI: -x +// CHECK-YC-NOFI: c++-header +// CHECK-YC-NOFI: cl-pch.cpp +// 2. Build PCH object +// CHECK-YC-NOFI: cc1 +// CHECK-YC-NOFI: -emit-obj +// CHECK-YC-NOFI: -include-pch +// CHECK-YC-NOFI: foo.pch +// CHECK-YC-NOFI: -pch-through-header=foo.h +// CHECK-YC-NOFI: -x +// CHECK-YC-NOFI: c++ +// CHECK-YC-NOFI: cl-pch.cpp // 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; 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 -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 -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 +// CHECK-YU-NOFI: cc1 +// CHECK-YU-NOFI: -emit-obj +// CHECK-YU-NOFI: -include-pch +// CHECK-YU-NOFI: foo.pch +// CHECK-YU-NOFI: -pch-through-header=foo.h +// CHECK-YU-NOFI: -x +// CHECK-YU-NOFI: c++ +// CHECK-YU-NOFI: cl-pch.cpp // With an actual /I argument. // RUN: %clang_cl -Werror /Ifoo /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \ @@ -226,20 +246,17 @@ // 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; 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 -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 -Werror /Yu./pchfile.h /FI.\pchfile.h /c -### -- %s 2>&1 \ +// CHECK-YU-CASE: -pch-through-header=pchFILE.h +// CHECK-YU-CASE: -include +// CHECK-YU-CASE: "./pchfile.h" +// 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; flag ignored +// CHECK-YU-SLASH: -pch-through-header=./pchfile.h +// CHECK-YU-SLASH: -include +// CHECK-YU-SLASH: ".{{[/\\]+}}pchfile.h" // 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 Index: test/PCH/Inputs/pch-through-use0.cpp =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through-use0.cpp @@ -0,0 +1,2 @@ +void foo() { +} Index: test/PCH/Inputs/pch-through-use1.cpp =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through-use1.cpp @@ -0,0 +1,5 @@ +#include "Inputs/pch-through1.h" +#include "Inputs/pch-through3.h" +void foo() { + through2(0); +} Index: test/PCH/Inputs/pch-through-use2.cpp =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through-use2.cpp @@ -0,0 +1,3 @@ +void foo() { + through4(0); +} Index: test/PCH/Inputs/pch-through-use3a.cpp =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through-use3a.cpp @@ -0,0 +1,2 @@ +#define AFOO 0 +#include "Inputs/pch-through1.h" Index: test/PCH/Inputs/pch-through-use3b.cpp =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through-use3b.cpp @@ -0,0 +1,3 @@ +#define AFOO 1 +#define BFOO 2 +#include "Inputs/pch-through1.h" Index: test/PCH/Inputs/pch-through1.h =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through1.h @@ -0,0 +1,2 @@ +#define THROUGH1 +int through1(int); Index: test/PCH/Inputs/pch-through2.h =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through2.h @@ -0,0 +1,2 @@ +#define THROUGH2 +int through2(int); Index: test/PCH/Inputs/pch-through3.h =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through3.h @@ -0,0 +1,2 @@ +#define THROUGH3 +int through3(int); Index: test/PCH/Inputs/pch-through4.h =================================================================== --- /dev/null +++ test/PCH/Inputs/pch-through4.h @@ -0,0 +1,2 @@ +#define THROUGH4 +int through4(int); Index: test/PCH/pch-through1.cpp =================================================================== --- /dev/null +++ test/PCH/pch-through1.cpp @@ -0,0 +1,34 @@ +// Through header not found (anywhere) +// RUN: not %clang_cc1 -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-does-not-exist.h -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-TEST0A %s +// CHECK-TEST0A: fatal error:{{.*}} 'Inputs/pch-does-not-exist.h' +// CHECK-TEST0A-SAME: required for precompiled header not found + +// Through header not found in search path +// RUN: not %clang_cc1 -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-through2.h -o %t \ +// RUN: %S/Inputs/pch-through-use0.cpp 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-TEST0B %s +// CHECK-TEST0B: fatal error:{{.*}}'Inputs/pch-through2.h' +// CHECK-TEST0B-SAME: required for precompiled header not found + +// No #include of through header during pch create +// RUN: not %clang_cc1 -I %S -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-through2.h -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-TEST1A %s +// CHECK-TEST1A: fatal error:{{.*}} #include of +// CHECK-TEST1A-SAME: 'Inputs/pch-through2.h' not seen while attempting to +// CHECK-TEST1A-SAME: create precompiled header + +// checks for through headers that are also -includes +// RUN: %clang_cc1 -I %S -include Inputs/pch-through1.h \ +// RUN: -pch-through-header=Inputs/pch-through1.h -emit-pch -o %t.s3t1 %s +// RUN: %clang_cc1 -I %S -include Inputs/pch-through1.h \ +// RUN: -include Inputs/pch-through2.h -include Inputs/pch-through3.h \ +// RUN: -pch-through-header=Inputs/pch-through2.h -emit-pch -o %t.s3t2 %s +// Use through header from -includes +// RUN: %clang_cc1 -I %S -include Inputs/pch-through1.h \ +// RUN: -include Inputs/pch-through2.h -include Inputs/pch-through4.h \ +// RUN: -pch-through-header=Inputs/pch-through2.h -include-pch %t.s3t2 \ +// RUN: %S/Inputs/pch-through-use2.cpp -o %t.out Index: test/PCH/pch-through2.cpp =================================================================== --- /dev/null +++ test/PCH/pch-through2.cpp @@ -0,0 +1,28 @@ +// Create +// RUN: %clang_cc1 -I %S -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-through2.h -o %t.s2t2 %s + +// Use +// RUN: %clang_cc1 -I %S -include-pch %t.s2t2 \ +// RUN: -pch-through-header=Inputs/pch-through2.h %s + +// No #include of through header during pch use +// RUN: not %clang_cc1 -I %S -include-pch %t.s2t2 \ +// RUN: -pch-through-header=Inputs/pch-through2.h \ +// RUN: %S/Inputs/pch-through-use1.cpp 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-TEST2A %s +// CHECK-TEST2A: fatal error:{{.*}} #include of +// CHECK-TEST2A-SAME: 'Inputs/pch-through2.h' not seen while attempting to +// CHECK-TEST2A-SAME: use precompiled header + +// check that pch only contains code before the through header. +// RUN: %clang_cc1 -I %S -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-through1.h -o %t.s2t1 %s +// RUN: not %clang_cc1 -I %S -include-pch %t.s2t1 \ +// RUN: -pch-through-header=Inputs/pch-through1.h \ +// RUN: %S/Inputs/pch-through-use1.cpp 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-TEST3 %s +// CHECK-TEST3: error: use of undeclared identifier 'through2' + +#include "Inputs/pch-through1.h" +#include "Inputs/pch-through2.h" Index: test/PCH/pch-through3a.cpp =================================================================== --- /dev/null +++ test/PCH/pch-through3a.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -verify -I %S -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-through1.h -o %t.s3at1 %s + +// RUN: %clang_cc1 -I %S -include-pch %t.s3at1 \ +// RUN: -pch-through-header=Inputs/pch-through1.h \ +// RUN: %S/Inputs/pch-through-use3a.cpp +//expected-no-diagnostics + +#define AFOO 0 +#include "Inputs/pch-through1.h" Index: test/PCH/pch-through3b.cpp =================================================================== --- /dev/null +++ test/PCH/pch-through3b.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -I %S -emit-pch \ +// RUN: -pch-through-header=Inputs/pch-through1.h -o %t.s3bt1 %s + +// RUN: %clang_cc1 -I %S -include-pch %t.s3bt1 \ +// RUN: -pch-through-header=Inputs/pch-through1.h \ +// RUN: %S/Inputs/pch-through-use3b.cpp 2>&1 | FileCheck %s + +//CHECK: warning: definition of macro 'AFOO' does not match definition in +//CHECK-SAME: precompiled header +//CHECK: warning: definition of macro 'BFOO' does not match definition in +//CHECK-SAME: precompiled header + +#define AFOO 0 +#include "Inputs/pch-through1.h"