Allow resolving headers from a PCH even after headers+PCH were moved to another path.

Store in PCH the directory that the PCH was originally created in.
If a header file is not found at the path that we expect it to be and the PCH file
was moved from its original location, try to resolve the file by assuming that
header+PCH were moved together and the header is in the same place relative to the PCH.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125576 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Argyrios Kyrtzidis 2011-02-15 17:54:22 +00:00
Родитель 5ac96d5f54
Коммит 8e3df4d086
10 изменённых файлов: 151 добавлений и 16 удалений

Просмотреть файл

@ -89,6 +89,7 @@ public:
static bool ComputeASTConsumerArguments(CompilerInstance &CI, static bool ComputeASTConsumerArguments(CompilerInstance &CI,
llvm::StringRef InFile, llvm::StringRef InFile,
std::string &Sysroot, std::string &Sysroot,
std::string &OutputFile,
llvm::raw_ostream *&OS, llvm::raw_ostream *&OS,
bool &Chaining); bool &Chaining);
}; };

Просмотреть файл

@ -353,7 +353,10 @@ namespace clang {
CUDA_SPECIAL_DECL_REFS = 39, CUDA_SPECIAL_DECL_REFS = 39,
/// \brief Record code for header search information. /// \brief Record code for header search information.
HEADER_SEARCH_TABLE = 40 HEADER_SEARCH_TABLE = 40,
/// \brief The directory that the PCH was originally created in.
ORIGINAL_PCH_DIR = 41
}; };
/// \brief Record types used within a source manager block. /// \brief Record types used within a source manager block.

Просмотреть файл

@ -613,6 +613,13 @@ private:
/// AST file. /// AST file.
std::string ActualOriginalFileName; std::string ActualOriginalFileName;
/// \brief The directory that the PCH was originally created in. Used to
/// allow resolving headers even after headers+PCH was moved to a new path.
std::string OriginalDir;
/// \brief The directory that the PCH we are reading is stored in.
std::string CurrentDir;
/// \brief Whether this precompiled header is a relocatable PCH file. /// \brief Whether this precompiled header is a relocatable PCH file.
bool RelocatablePCH; bool RelocatablePCH;

Просмотреть файл

@ -309,7 +309,8 @@ private:
void WriteSubStmt(Stmt *S); void WriteSubStmt(Stmt *S);
void WriteBlockInfoBlock(); void WriteBlockInfoBlock();
void WriteMetadata(ASTContext &Context, const char *isysroot); void WriteMetadata(ASTContext &Context, const char *isysroot,
const std::string &OutputFile);
void WriteLanguageOptions(const LangOptions &LangOpts); void WriteLanguageOptions(const LangOptions &LangOpts);
void WriteStatCache(MemorizeStatCalls &StatCalls); void WriteStatCache(MemorizeStatCalls &StatCalls);
void WriteSourceManagerBlock(SourceManager &SourceMgr, void WriteSourceManagerBlock(SourceManager &SourceMgr,
@ -339,7 +340,7 @@ private:
void WriteDecl(ASTContext &Context, Decl *D); void WriteDecl(ASTContext &Context, Decl *D);
void WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, void WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const char* isysroot); const char* isysroot, const std::string &OutputFile);
void WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls, void WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const char* isysroot); const char* isysroot);
@ -368,6 +369,7 @@ public:
/// \param PPRec Record of the preprocessing actions that occurred while /// \param PPRec Record of the preprocessing actions that occurred while
/// preprocessing this file, e.g., macro instantiations /// preprocessing this file, e.g., macro instantiations
void WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls, void WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const std::string &OutputFile,
const char* isysroot); const char* isysroot);
/// \brief Emit a source location. /// \brief Emit a source location.
@ -575,6 +577,7 @@ public:
/// precompiled header from the parsed source code. /// precompiled header from the parsed source code.
class PCHGenerator : public SemaConsumer { class PCHGenerator : public SemaConsumer {
const Preprocessor &PP; const Preprocessor &PP;
std::string OutputFile;
const char *isysroot; const char *isysroot;
llvm::raw_ostream *Out; llvm::raw_ostream *Out;
Sema *SemaPtr; Sema *SemaPtr;
@ -589,7 +592,7 @@ protected:
const ASTWriter &getWriter() const { return Writer; } const ASTWriter &getWriter() const { return Writer; }
public: public:
PCHGenerator(const Preprocessor &PP, bool Chaining, PCHGenerator(const Preprocessor &PP, const std::string &OutputFile, bool Chaining,
const char *isysroot, llvm::raw_ostream *Out); const char *isysroot, llvm::raw_ostream *Out);
virtual void InitializeSema(Sema &S) { SemaPtr = &S; } virtual void InitializeSema(Sema &S) { SemaPtr = &S; }
virtual void HandleTranslationUnit(ASTContext &Ctx); virtual void HandleTranslationUnit(ASTContext &Ctx);

Просмотреть файл

@ -653,7 +653,7 @@ public:
PrecompilePreambleConsumer(ASTUnit &Unit, PrecompilePreambleConsumer(ASTUnit &Unit,
const Preprocessor &PP, bool Chaining, const Preprocessor &PP, bool Chaining,
const char *isysroot, llvm::raw_ostream *Out) const char *isysroot, llvm::raw_ostream *Out)
: PCHGenerator(PP, Chaining, isysroot, Out), Unit(Unit) { } : PCHGenerator(PP, "", Chaining, isysroot, Out), Unit(Unit) { }
virtual void HandleTopLevelDecl(DeclGroupRef D) { virtual void HandleTopLevelDecl(DeclGroupRef D) {
for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) {
@ -700,9 +700,11 @@ public:
virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
llvm::StringRef InFile) { llvm::StringRef InFile) {
std::string Sysroot; std::string Sysroot;
std::string OutputFile;
llvm::raw_ostream *OS = 0; llvm::raw_ostream *OS = 0;
bool Chaining; bool Chaining;
if (GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, if (GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot,
OutputFile,
OS, Chaining)) OS, Chaining))
return 0; return 0;
@ -2008,7 +2010,7 @@ bool ASTUnit::Save(llvm::StringRef File) {
std::vector<unsigned char> Buffer; std::vector<unsigned char> Buffer;
llvm::BitstreamWriter Stream(Buffer); llvm::BitstreamWriter Stream(Buffer);
ASTWriter Writer(Stream); ASTWriter Writer(Stream);
Writer.WriteAST(getSema(), 0, 0); Writer.WriteAST(getSema(), 0, std::string(), 0);
// Write the generated bitstream to "Out". // Write the generated bitstream to "Out".
if (!Buffer.empty()) if (!Buffer.empty())

Просмотреть файл

@ -83,19 +83,21 @@ ASTConsumer *DeclContextPrintAction::CreateASTConsumer(CompilerInstance &CI,
ASTConsumer *GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, ASTConsumer *GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI,
llvm::StringRef InFile) { llvm::StringRef InFile) {
std::string Sysroot; std::string Sysroot;
std::string OutputFile;
llvm::raw_ostream *OS = 0; llvm::raw_ostream *OS = 0;
bool Chaining; bool Chaining;
if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OS, Chaining)) if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS, Chaining))
return 0; return 0;
const char *isysroot = CI.getFrontendOpts().RelocatablePCH ? const char *isysroot = CI.getFrontendOpts().RelocatablePCH ?
Sysroot.c_str() : 0; Sysroot.c_str() : 0;
return new PCHGenerator(CI.getPreprocessor(), Chaining, isysroot, OS); return new PCHGenerator(CI.getPreprocessor(), OutputFile, Chaining, isysroot, OS);
} }
bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI,
llvm::StringRef InFile, llvm::StringRef InFile,
std::string &Sysroot, std::string &Sysroot,
std::string &OutputFile,
llvm::raw_ostream *&OS, llvm::raw_ostream *&OS,
bool &Chaining) { bool &Chaining) {
Sysroot = CI.getHeaderSearchOpts().Sysroot; Sysroot = CI.getHeaderSearchOpts().Sysroot;
@ -111,6 +113,7 @@ bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI,
if (!OS) if (!OS)
return true; return true;
OutputFile = CI.getFrontendOpts().OutputFile;
Chaining = CI.getInvocation().getFrontendOpts().ChainedPCH && Chaining = CI.getInvocation().getFrontendOpts().ChainedPCH &&
!CI.getPreprocessorOpts().ImplicitPCHInclude.empty(); !CI.getPreprocessorOpts().ImplicitPCHInclude.empty();
return false; return false;

Просмотреть файл

@ -1181,6 +1181,39 @@ ASTReader::ASTReadResult ASTReader::ReadSourceManagerBlock(PerFileData &F) {
} }
} }
/// \brief If a header file is not found at the path that we expect it to be
/// and the PCH file was moved from its original location, try to resolve the
/// file by assuming that header+PCH were moved together and the header is in
/// the same place relative to the PCH.
static std::string
resolveFileRelativeToOriginalDir(const std::string &Filename,
const std::string &OriginalDir,
const std::string &CurrDir) {
assert(OriginalDir != CurrDir &&
"No point trying to resolve the file if the PCH dir didn't change");
using namespace llvm::sys;
llvm::SmallString<128> filePath(Filename);
fs::make_absolute(filePath);
assert(path::is_absolute(OriginalDir));
llvm::SmallString<128> currPCHPath(CurrDir);
path::const_iterator fileDirI = path::begin(path::parent_path(filePath)),
fileDirE = path::end(path::parent_path(filePath));
path::const_iterator origDirI = path::begin(OriginalDir),
origDirE = path::end(OriginalDir);
// Skip the common path components from filePath and OriginalDir.
while (fileDirI != fileDirE && origDirI != origDirE &&
*fileDirI == *origDirI) {
++fileDirI;
++origDirI;
}
for (; origDirI != origDirE; ++origDirI)
path::append(currPCHPath, "..");
path::append(currPCHPath, fileDirI, fileDirE);
path::append(currPCHPath, path::filename(Filename));
return currPCHPath.str();
}
/// \brief Get a cursor that's correctly positioned for reading the source /// \brief Get a cursor that's correctly positioned for reading the source
/// location entry with the given ID. /// location entry with the given ID.
ASTReader::PerFileData *ASTReader::SLocCursorForID(unsigned ID) { ASTReader::PerFileData *ASTReader::SLocCursorForID(unsigned ID) {
@ -1235,6 +1268,14 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(unsigned ID) {
std::string Filename(BlobStart, BlobStart + BlobLen); std::string Filename(BlobStart, BlobStart + BlobLen);
MaybeAddSystemRootToFilename(Filename); MaybeAddSystemRootToFilename(Filename);
const FileEntry *File = FileMgr.getFile(Filename); const FileEntry *File = FileMgr.getFile(Filename);
if (File == 0 && !OriginalDir.empty() && !CurrentDir.empty() &&
OriginalDir != CurrentDir) {
std::string resolved = resolveFileRelativeToOriginalDir(Filename,
OriginalDir,
CurrentDir);
if (!resolved.empty())
File = FileMgr.getFile(resolved);
}
if (File == 0) if (File == 0)
File = FileMgr.getVirtualFile(Filename, (off_t)Record[4], File = FileMgr.getVirtualFile(Filename, (off_t)Record[4],
(time_t)Record[5]); (time_t)Record[5]);
@ -2179,6 +2220,12 @@ ASTReader::ReadASTBlock(PerFileData &F) {
MaybeAddSystemRootToFilename(OriginalFileName); MaybeAddSystemRootToFilename(OriginalFileName);
break; break;
case ORIGINAL_PCH_DIR:
// The primary AST will be the last to get here, so it will be the one
// that's used.
OriginalDir.assign(BlobStart, BlobLen);
break;
case VERSION_CONTROL_BRANCH_REVISION: { case VERSION_CONTROL_BRANCH_REVISION: {
const std::string &CurBranch = getClangFullRepositoryVersion(); const std::string &CurBranch = getClangFullRepositoryVersion();
llvm::StringRef ASTBranch(BlobStart, BlobLen); llvm::StringRef ASTBranch(BlobStart, BlobLen);
@ -2393,6 +2440,11 @@ ASTReader::ASTReadResult ASTReader::ReadASTCore(llvm::StringRef FileName,
// Set the AST file name. // Set the AST file name.
F.FileName = FileName; F.FileName = FileName;
if (FileName != "-") {
CurrentDir = llvm::sys::path::parent_path(FileName);
if (CurrentDir.empty()) CurrentDir = ".";
}
// Open the AST file. // Open the AST file.
// //
// FIXME: This shouldn't be here, we should just take a raw_ostream. // FIXME: This shouldn't be here, we should just take a raw_ostream.

Просмотреть файл

@ -899,7 +899,8 @@ adjustFilenameForRelocatablePCH(const char *Filename, const char *isysroot) {
} }
/// \brief Write the AST metadata (e.g., i686-apple-darwin9). /// \brief Write the AST metadata (e.g., i686-apple-darwin9).
void ASTWriter::WriteMetadata(ASTContext &Context, const char *isysroot) { void ASTWriter::WriteMetadata(ASTContext &Context, const char *isysroot,
const std::string &OutputFile) {
using namespace llvm; using namespace llvm;
// Metadata // Metadata
@ -947,6 +948,23 @@ void ASTWriter::WriteMetadata(ASTContext &Context, const char *isysroot) {
Stream.EmitRecordWithBlob(FileAbbrevCode, Record, MainFileNameStr); Stream.EmitRecordWithBlob(FileAbbrevCode, Record, MainFileNameStr);
} }
// Original PCH directory
if (!OutputFile.empty() && OutputFile != "-") {
BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
Abbrev->Add(BitCodeAbbrevOp(ORIGINAL_PCH_DIR));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name
unsigned AbbrevCode = Stream.EmitAbbrev(Abbrev);
llvm::SmallString<128> OutputPath(OutputFile);
llvm::sys::fs::make_absolute(OutputPath);
StringRef origDir = llvm::sys::path::parent_path(OutputPath);
RecordData Record;
Record.push_back(ORIGINAL_PCH_DIR);
Stream.EmitRecordWithBlob(AbbrevCode, Record, origDir);
}
// Repository branch/version information. // Repository branch/version information.
BitCodeAbbrev *RepoAbbrev = new BitCodeAbbrev(); BitCodeAbbrev *RepoAbbrev = new BitCodeAbbrev();
RepoAbbrev->Add(BitCodeAbbrevOp(VERSION_CONTROL_BRANCH_REVISION)); RepoAbbrev->Add(BitCodeAbbrevOp(VERSION_CONTROL_BRANCH_REVISION));
@ -2568,6 +2586,7 @@ ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream)
} }
void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls, void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const std::string &OutputFile,
const char *isysroot) { const char *isysroot) {
// Emit the file header. // Emit the file header.
Stream.Emit((unsigned)'C', 8); Stream.Emit((unsigned)'C', 8);
@ -2580,11 +2599,12 @@ void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
if (Chain) if (Chain)
WriteASTChain(SemaRef, StatCalls, isysroot); WriteASTChain(SemaRef, StatCalls, isysroot);
else else
WriteASTCore(SemaRef, StatCalls, isysroot); WriteASTCore(SemaRef, StatCalls, isysroot, OutputFile);
} }
void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const char *isysroot) { const char *isysroot,
const std::string &OutputFile) {
using namespace llvm; using namespace llvm;
ASTContext &Context = SemaRef.Context; ASTContext &Context = SemaRef.Context;
@ -2692,7 +2712,7 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
// Write the remaining AST contents. // Write the remaining AST contents.
RecordData Record; RecordData Record;
Stream.EnterSubblock(AST_BLOCK_ID, 5); Stream.EnterSubblock(AST_BLOCK_ID, 5);
WriteMetadata(Context, isysroot); WriteMetadata(Context, isysroot, OutputFile);
WriteLanguageOptions(Context.getLangOptions()); WriteLanguageOptions(Context.getLangOptions());
if (StatCalls && !isysroot) if (StatCalls && !isysroot)
WriteStatCache(*StatCalls); WriteStatCache(*StatCalls);
@ -2827,7 +2847,7 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
RecordData Record; RecordData Record;
Stream.EnterSubblock(AST_BLOCK_ID, 5); Stream.EnterSubblock(AST_BLOCK_ID, 5);
WriteMetadata(Context, isysroot); WriteMetadata(Context, isysroot, "");
if (StatCalls && !isysroot) if (StatCalls && !isysroot)
WriteStatCache(*StatCalls); WriteStatCache(*StatCalls);
// FIXME: Source manager block should only write new stuff, which could be // FIXME: Source manager block should only write new stuff, which could be

Просмотреть файл

@ -27,10 +27,11 @@
using namespace clang; using namespace clang;
PCHGenerator::PCHGenerator(const Preprocessor &PP, PCHGenerator::PCHGenerator(const Preprocessor &PP,
const std::string &OutputFile,
bool Chaining, bool Chaining,
const char *isysroot, const char *isysroot,
llvm::raw_ostream *OS) llvm::raw_ostream *OS)
: PP(PP), isysroot(isysroot), Out(OS), SemaPtr(0), : PP(PP), OutputFile(OutputFile), isysroot(isysroot), Out(OS), SemaPtr(0),
StatCalls(0), Stream(Buffer), Writer(Stream), Chaining(Chaining) { StatCalls(0), Stream(Buffer), Writer(Stream), Chaining(Chaining) {
// Install a stat() listener to keep track of all of the stat() // Install a stat() listener to keep track of all of the stat()
// calls. // calls.
@ -50,7 +51,7 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
// Emit the PCH file // Emit the PCH file
assert(SemaPtr && "No Sema?"); assert(SemaPtr && "No Sema?");
Writer.WriteAST(*SemaPtr, StatCalls, isysroot); Writer.WriteAST(*SemaPtr, StatCalls, OutputFile, isysroot);
// Write the generated bitstream to "Out". // Write the generated bitstream to "Out".
Out->write((char *)&Buffer.front(), Buffer.size()); Out->write((char *)&Buffer.front(), Buffer.size());

43
test/PCH/headersearch.cpp Normal file
Просмотреть файл

@ -0,0 +1,43 @@
// Test reading of PCH with changed location of original input files,
// i.e. invoking header search.
// Generate the original files:
// RUN: mkdir -p %t_orig/sub %t_orig/sub2
// RUN: echo 'struct orig_sub{char c; int i; };' > %t_orig/sub/orig_sub.h
// RUN: echo 'void orig_sub2_1();' > %t_orig/sub2/orig_sub2_1.h
// RUN: echo '#include "orig_sub2_1.h"' > %t_orig/sub2/orig_sub2.h
// RUN: echo 'template <typename T> void tf() { orig_sub2_1(); T::foo(); }' >> %t_orig/sub2/orig_sub2.h
// RUN: echo 'void foo() {}' > %t_orig/tmp2.h
// RUN: echo '#include "tmp2.h"' > %t_orig/all.h
// RUN: echo '#include "sub/orig_sub.h"' >> %t_orig/all.h
// RUN: echo '#include "orig_sub2.h"' >> %t_orig/all.h
// RUN: echo 'int all();' >> %t_orig/all.h
// Generate the PCH:
// RUN: cd %t_orig && %clang_cc1 -x c++ -emit-pch -o all.h.pch -Isub2 all.h
// RUN: rm -rf %t_moved
// RUN: mv %t_orig %t_moved
// Check diagnostic with location in original source:
// RUN: %clang_cc1 -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -Wpadded -emit-obj -o %t.o %s 2> %t.stderr
// RUN: grep 'struct orig_sub' %t.stderr
// Check diagnostic with 2nd location in original source:
// RUN: not %clang_cc1 -DREDECL -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-obj -o %t.o %s 2> %t.stderr
// RUN: grep 'void foo' %t.stderr
// Check diagnostic with instantiation location in original source:
// RUN: not %clang_cc1 -DINSTANTIATION -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-obj -o %t.o %s 2> %t.stderr
// RUN: grep 'orig_sub2_1' %t.stderr
void qq(orig_sub*) {all();}
#ifdef REDECL
float foo() {return 0;}
#endif
#ifdef INSTANTIATION
void f() {
tf<int>();
}
#endif