mirror of https://github.com/microsoft/clang.git
[clang-cl, PCH] Implement support for MS-style PCH through headers
Implement support for MS-style PCH through headers. This enables support for /Yc and /Yu where the through header is either on the command line or included in the source. It replaces the current support the requires the header also be specified with /FI. This change adds a -cc1 option -pch-through-header that is used to either start or stop compilation during PCH create or use. When creating a PCH, the compilation ends after compilation of the through header. When using a PCH, tokens are skipped until after the through header is seen. Patch By: mikerice Differential Revision: https://reviews.llvm.org/D46652 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336379 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
feb79d981f
commit
ba7b10ace8
|
@ -404,6 +404,14 @@ def err_pp_invalid_directive : Error<"invalid preprocessing directive">;
|
|||
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 warn_pp_macro_def_mismatch_with_pch : Warning<
|
||||
"definition of macro %0 does not match definition in precompiled header">,
|
||||
InGroup<ClangClPch>;
|
||||
def err_pp_file_not_found_not_fatal : Error<
|
||||
"'%0' file not found with <angled> include; use \"quotes\" instead">;
|
||||
def err_pp_error_opening_file : Error<
|
||||
|
|
|
@ -607,10 +607,9 @@ def fixit_to_temp : Flag<["-"], "fixit-to-temporary">,
|
|||
|
||||
def foverride_record_layout_EQ : Joined<["-"], "foverride-record-layout=">,
|
||||
HelpText<"Override record layouts with those in the given file">;
|
||||
def find_pch_source_EQ : Joined<["-"], "find-pch-source=">,
|
||||
HelpText<"When building a pch, try to find the input file in include "
|
||||
"directories, as if it had been included by the argument passed "
|
||||
"to this flag.">;
|
||||
def pch_through_header_EQ : Joined<["-"], "pch-through-header=">,
|
||||
HelpText<"Stop PCH generation after including this file. When using a PCH, "
|
||||
"skip tokens until after this file is included.">;
|
||||
def fno_pch_timestamp : Flag<["-"], "fno-pch-timestamp">,
|
||||
HelpText<"Disable inclusion of timestamp in precompiled headers">;
|
||||
def building_pch_with_obj : Flag<["-"], "building-pch-with-obj">,
|
||||
|
|
|
@ -435,10 +435,6 @@ public:
|
|||
/// 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;
|
||||
|
||||
|
|
|
@ -720,6 +720,12 @@ private:
|
|||
/// 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 @@ public:
|
|||
/// 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,9 @@ private:
|
|||
PredefinesFileID = FID;
|
||||
}
|
||||
|
||||
/// Set the FileID for the PCH through header.
|
||||
void setPCHThroughHeaderFileID(FileID 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) {
|
||||
|
|
|
@ -54,6 +54,13 @@ public:
|
|||
/// definitions and expansions.
|
||||
bool DetailedRecord = false;
|
||||
|
||||
/// If non-empty, the filename used in an #include directive in the primary
|
||||
/// source file (or command-line preinclude) that is used to implement
|
||||
/// MSVC-style precompiled headers. When creating a PCH, after the #include
|
||||
/// of this header, the PCH generation stops. When using a PCH, tokens are
|
||||
/// skipped until after an #include of this header is seen.
|
||||
std::string PCHThroughHeader;
|
||||
|
||||
/// The implicit PCH included at the start of the translation unit, or empty.
|
||||
std::string ImplicitPCHInclude;
|
||||
|
||||
|
|
|
@ -2991,22 +2991,6 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
|
|||
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);
|
||||
|
@ -3076,11 +3060,9 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
|
|||
const types::ID HeaderType = lookupHeaderTypeForSourceType(InputType);
|
||||
llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PCHPL;
|
||||
types::getCompilationPhases(HeaderType, PCHPL);
|
||||
Arg *PchInputArg = MakeInputArg(Args, *Opts, YcArg->getValue());
|
||||
|
||||
// Build the pipeline for the pch file.
|
||||
Action *ClangClPch =
|
||||
C.MakeAction<InputAction>(*PchInputArg, HeaderType);
|
||||
C.MakeAction<InputAction>(*InputArg, HeaderType);
|
||||
for (phases::ID Phase : PCHPL)
|
||||
ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch);
|
||||
assert(ClangClPch);
|
||||
|
@ -4269,6 +4251,9 @@ std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const {
|
|||
// 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");
|
||||
|
|
|
@ -1060,77 +1060,28 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA,
|
|||
// wonky, but we include looking for .gch so we can support seamless
|
||||
// replacement into a build system already set up to be generating
|
||||
// .gch files.
|
||||
int YcIndex = -1, YuIndex = -1;
|
||||
{
|
||||
int AI = -1;
|
||||
|
||||
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 && JA.getKind() >= Action::PrecompileJobClass &&
|
||||
JA.getKind() <= Action::AssembleJobClass) {
|
||||
CmdArgs.push_back(Args.MakeArgString("-building-pch-with-obj"));
|
||||
}
|
||||
if (YcArg || YuArg) {
|
||||
StringRef ThroughHeader = YcArg ? YcArg->getValue() : YuArg->getValue();
|
||||
if (!isa<PrecompileJobAction>(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<PrecompileJobAction>(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()));
|
||||
}
|
||||
if (YcIndex != -1 && JA.getKind() >= Action::PrecompileJobClass &&
|
||||
JA.getKind() <= Action::AssembleJobClass) {
|
||||
CmdArgs.push_back(Args.MakeArgString("-building-pch-with-obj"));
|
||||
}
|
||||
|
||||
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<PrecompileJobAction>(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;
|
||||
|
|
|
@ -853,36 +853,7 @@ bool CompilerInstance::InitializeSourceManager(
|
|||
|
||||
// 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<std::pair<const FileEntry *, const DirectoryEntry *>, 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;
|
||||
|
|
|
@ -1546,7 +1546,6 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
|||
= Args.getLastArgValue(OPT_foverride_record_layout_EQ);
|
||||
Opts.AuxTriple =
|
||||
llvm::Triple::normalize(Args.getLastArgValue(OPT_aux_triple));
|
||||
Opts.FindPchSource = Args.getLastArgValue(OPT_find_pch_source_EQ);
|
||||
Opts.StatsFile = Args.getLastArgValue(OPT_stats_file);
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
|
||||
|
@ -2828,6 +2827,7 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,
|
|||
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
|
||||
|
|
|
@ -887,6 +887,22 @@ private:
|
|||
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 @@ void Preprocessor::HandleDirective(Token &Result) {
|
|||
// 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 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ void Preprocessor::HandleDefineDirective(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// 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::warn_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.
|
||||
|
|
|
@ -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"
|
||||
|
@ -425,6 +426,8 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
|
|||
PragmaAssumeNonNullLoc = SourceLocation();
|
||||
}
|
||||
|
||||
bool LeavingPCHThroughHeader = false;
|
||||
|
||||
// If this is a #include'd file, pop it off the include stack and continue
|
||||
// lexing the #includer file.
|
||||
if (!IncludeMacroStack.empty()) {
|
||||
|
@ -481,6 +484,12 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
|
|||
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 +509,16 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
|
|||
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 +539,12 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
|
|||
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();
|
||||
|
|
|
@ -149,6 +149,11 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
|
|||
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,72 @@ void Preprocessor::EnterMainSourceFile() {
|
|||
|
||||
// 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();
|
||||
}
|
||||
|
||||
void Preprocessor::setPCHThroughHeaderFileID(FileID FID) {
|
||||
assert(PCHThroughHeaderFileID.isInvalid() &&
|
||||
"PCHThroughHeaderFileID already set!");
|
||||
PCHThroughHeaderFileID = FID;
|
||||
}
|
||||
|
||||
bool Preprocessor::isPCHThroughHeader(const FileEntry *FE) {
|
||||
assert(PCHThroughHeaderFileID.isValid() &&
|
||||
"Invalid PCH through header FileID");
|
||||
return FE == SourceMgr.getFileEntryForID(PCHThroughHeaderFileID);
|
||||
}
|
||||
|
||||
bool Preprocessor::creatingPCHWithThroughHeader() {
|
||||
return TUKind == TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
|
||||
PCHThroughHeaderFileID.isValid();
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -141,6 +141,12 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
|
|||
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;
|
||||
|
|
|
@ -704,6 +704,17 @@ static bool checkPreprocessorOptions(const PreprocessorOptions &PPOpts,
|
|||
// 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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -157,26 +157,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
|
||||
|
@ -184,38 +195,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 \
|
||||
|
@ -232,20 +252,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
|
||||
|
|
Loading…
Reference in New Issue