diff --git a/docs/UsersManual.html b/docs/UsersManual.html
index 65415eea48..ab341150e3 100644
--- a/docs/UsersManual.html
+++ b/docs/UsersManual.html
@@ -465,6 +465,50 @@ for headers that are directly included within a source file. For example:
test.h since test.h was included directly in the source file
and not specified on the command line using -include.
+Relocatable PCH Files
+It is sometimes necessary to build a precompiled header from headers that
+are not yet in their final, installed locations. For example, one might build a
+precompiled header within the build tree that is then meant to be installed
+alongside the headers. Clang permits the creation of "relocatable" precompiled
+headers, which are built with a given path (into the build directory) and can
+later be used from an installed location.
+
+To build a relocatable precompiled header, place your headers into a
+subdirectory whose structure mimics the installed location. For example, if you
+want to build a precompiled header for the header mylib.h
that
+will be installed into /usr/include
, create a subdirectory
+build/usr/include
and place the header mylib.h
into
+that subdirectory. If mylib.h
depends on other headers, then
+they can be stored within build/usr/include
in a way that mimics
+the installed location.
+
+Building a relocatable precompiled header requires two additional arguments.
+First, pass the --relocatable-pch
flag to indicate that the
+resulting PCH file should be relocatable. Second, pass
+-isysroot /path/to/build
, which makes all includes for your
+library relative to the build directory. For example:
+
+
+ # clang -x c-header --relocatable-pch -isysroot /path/to/build /path/to/build/mylib.h mylib.h.pch
+
+
+When loading the relocatable PCH file, the various headers used in the PCH
+file are found from the system header root. For example, mylib.h
+can be found in /usr/include/mylib.h
. If the headers are installed
+in some other system root, the -isysroot
option can be used provide
+a different system root from which the headers will be based. For example,
+-isysroot /Developer/SDKs/MacOSX10.4u.sdk
will look for
+mylib.h
in
+/Developer/SDKs/MacOSX10.4u.sdk/usr/include/mylib.h
.
+
+Relocatable precompiled headers are intended to be used in a limited number
+of cases where the compilation environment is tightly controlled and the
+precompiled header cannot be generated after headers have been installed.
+Relocatable precompiled headers also have some performance impact, because
+the difference in location between the header locations at PCH build time vs.
+at the time of PCH use requires one of the PCH optimizations,
+stat()
caching, to be disabled. However, this change is only
+likely to affect PCH files that reference a large number of headers.
C Language Features
diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td
index 815ae8d700..20087093d0 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -24,6 +24,9 @@ def warn_fixit_no_changes : Note<
"FIX-IT detected errors it could not fix; no output will be generated">;
// PCH reader
+def err_relocatable_without_without_isysroot : Error<
+ "must specify system root with -isysroot when building a relocatable "
+ "PCH file">;
def warn_pch_target_triple : Error<
"PCH file was compiled for the target '%0' but the current translation "
"unit is being compiled for target '%1'">;
diff --git a/include/clang/Driver/Options.def b/include/clang/Driver/Options.def
index af108a85eb..216f188522 100644
--- a/include/clang/Driver/Options.def
+++ b/include/clang/Driver/Options.def
@@ -223,6 +223,8 @@ OPTION("--print-prog-name", _print_prog_name, Separate, INVALID, print_prog_name
OPTION("--print-search-dirs", _print_search_dirs, Flag, INVALID, print_search_dirs, "", 0, 0, 0)
OPTION("--profile-blocks", _profile_blocks, Flag, INVALID, a, "", 0, 0, 0)
OPTION("--profile", _profile, Flag, INVALID, p, "", 0, 0, 0)
+OPTION("--relocatable-pch", _relocatable_pch, Flag, INVALID, INVALID, "", 0,
+ "Build a relocatable precompiled header", 0)
OPTION("--resource=", _resource_EQ, Joined, INVALID, fcompile_resource_EQ, "", 0, 0, 0)
OPTION("--resource", _resource, Separate, INVALID, fcompile_resource_EQ, "J", 0, 0, 0)
OPTION("--save-temps", _save_temps, Flag, INVALID, save_temps, "", 0, 0, 0)
diff --git a/include/clang/Frontend/ASTConsumers.h b/include/clang/Frontend/ASTConsumers.h
index be45202625..fe5a198880 100644
--- a/include/clang/Frontend/ASTConsumers.h
+++ b/include/clang/Frontend/ASTConsumers.h
@@ -92,7 +92,8 @@ ASTConsumer* CreateHTMLPrinter(llvm::raw_ostream *OS, Diagnostic &D,
// used later with the PCHReader (clang-cc option -include-pch)
// to speed up compile times.
ASTConsumer *CreatePCHGenerator(const Preprocessor &PP,
- llvm::raw_ostream *OS);
+ llvm::raw_ostream *OS,
+ const char *isysroot = 0);
// Block rewriter: rewrites code using the Apple blocks extension to pure
// C code. Output is always sent to stdout.
diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h
index d2849fe7c9..aa984175e0 100644
--- a/include/clang/Frontend/PCHReader.h
+++ b/include/clang/Frontend/PCHReader.h
@@ -310,6 +310,13 @@ private:
/// file.
std::string OriginalFileName;
+ /// \brief Whether this precompiled header is a relocatable PCH file.
+ bool RelocatablePCH;
+
+ /// \brief The system include root to be used when loading the
+ /// precompiled header.
+ const char *isysroot;
+
/// \brief Mapping from switch-case IDs in the PCH file to
/// switch-case statements.
std::map SwitchCaseStmts;
@@ -428,10 +435,13 @@ private:
/// predefines buffer may contain additional definitions.
std::string SuggestedPredefines;
+ void MaybeAddSystemRootToFilename(std::string &Filename);
+
PCHReadResult ReadPCHBlock();
bool CheckPredefinesBuffer(const char *PCHPredef,
unsigned PCHPredefLen,
FileID PCHBufferID);
+ bool ParseLineTable(llvm::SmallVectorImpl &Record);
PCHReadResult ReadSourceManagerBlock();
PCHReadResult ReadSLocEntryRecord(unsigned ID);
@@ -448,19 +458,42 @@ private:
PCHReader(const PCHReader&); // do not implement
PCHReader &operator=(const PCHReader &); // do not implement
-
public:
typedef llvm::SmallVector RecordData;
/// \brief Load the PCH file and validate its contents against the given
/// Preprocessor.
- PCHReader(Preprocessor &PP, ASTContext *Context);
+ ///
+ /// \param PP the preprocessor associated with the context in which this
+ /// precompiled header will be loaded.
+ ///
+ /// \param Context the AST context that this precompiled header will be
+ /// loaded into.
+ ///
+ /// \param isysroot If non-NULL, the system include path specified by the
+ /// user. This is only used with relocatable PCH files. If non-NULL,
+ /// a relocatable PCH file will use the default path "/".
+ PCHReader(Preprocessor &PP, ASTContext *Context, const char *isysroot = 0);
/// \brief Load the PCH file without using any pre-initialized Preprocessor.
///
/// The necessary information to initialize a Preprocessor later can be
/// obtained by setting a PCHReaderListener.
- PCHReader(SourceManager &SourceMgr, FileManager &FileMgr, Diagnostic &Diags);
+ ///
+ /// \param SourceMgr the source manager into which the precompiled header
+ /// will be loaded.
+ ///
+ /// \param FileMgr the file manager into which the precompiled header will
+ /// be loaded.
+ ///
+ /// \param Diags the diagnostics system to use for reporting errors and
+ /// warnings relevant to loading the precompiled header.
+ ///
+ /// \param isysroot If non-NULL, the system include path specified by the
+ /// user. This is only used with relocatable PCH files. If non-NULL,
+ /// a relocatable PCH file will use the default path "/".
+ PCHReader(SourceManager &SourceMgr, FileManager &FileMgr,
+ Diagnostic &Diags, const char *isysroot = 0);
~PCHReader();
/// \brief Load the precompiled header designated by the given file
diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h
index c663442e64..3bab9b998b 100644
--- a/include/clang/Frontend/PCHWriter.h
+++ b/include/clang/Frontend/PCHWriter.h
@@ -160,11 +160,12 @@ private:
unsigned NumVisibleDeclContexts;
void WriteBlockInfoBlock();
- void WriteMetadata(ASTContext &Context);
+ void WriteMetadata(ASTContext &Context, const char *isysroot);
void WriteLanguageOptions(const LangOptions &LangOpts);
- void WriteStatCache(MemorizeStatCalls &StatCalls);
+ void WriteStatCache(MemorizeStatCalls &StatCalls, const char* isysroot);
void WriteSourceManagerBlock(SourceManager &SourceMgr,
- const Preprocessor &PP);
+ const Preprocessor &PP,
+ const char* isysroot);
void WritePreprocessor(const Preprocessor &PP);
void WriteComments(ASTContext &Context);
void WriteType(const Type *T);
@@ -186,7 +187,17 @@ public:
PCHWriter(llvm::BitstreamWriter &Stream);
/// \brief Write a precompiled header for the given semantic analysis.
- void WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls);
+ ///
+ /// \param SemaRef a reference to the semantic analysis object that processed
+ /// the AST to be written into the precompiled header.
+ ///
+ /// \param StatCalls the object that cached all of the stat() calls made while
+ /// searching for source files and headers.
+ ///
+ /// \param isysroot if non-NULL, write a relocatable PCH file whose headers
+ /// are relative to the given system root.
+ void WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls,
+ const char* isysroot);
/// \brief Emit a source location.
void AddSourceLocation(SourceLocation Loc, RecordData &Record);
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index d198a54cf7..f7f2b190c8 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -474,6 +474,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(A->getValue(Args));
}
+ if (Args.hasArg(options::OPT__relocatable_pch, true))
+ CmdArgs.push_back("--relocatable-pch");
+
// Forward -f options which we can pass directly.
Args.AddLastArg(CmdArgs, options::OPT_femit_all_decls);
Args.AddLastArg(CmdArgs, options::OPT_fexceptions);
diff --git a/lib/Frontend/GeneratePCH.cpp b/lib/Frontend/GeneratePCH.cpp
index 8be88ce381..3b7715ad28 100644
--- a/lib/Frontend/GeneratePCH.cpp
+++ b/lib/Frontend/GeneratePCH.cpp
@@ -32,19 +32,24 @@ using namespace llvm;
namespace {
class VISIBILITY_HIDDEN PCHGenerator : public SemaConsumer {
const Preprocessor &PP;
+ const char *isysroot;
llvm::raw_ostream *Out;
Sema *SemaPtr;
MemorizeStatCalls *StatCalls; // owned by the FileManager
-
+
public:
- explicit PCHGenerator(const Preprocessor &PP, llvm::raw_ostream *Out);
+ explicit PCHGenerator(const Preprocessor &PP,
+ const char *isysroot,
+ llvm::raw_ostream *Out);
virtual void InitializeSema(Sema &S) { SemaPtr = &S; }
virtual void HandleTranslationUnit(ASTContext &Ctx);
};
}
-PCHGenerator::PCHGenerator(const Preprocessor &PP, llvm::raw_ostream *OS)
- : PP(PP), Out(OS), SemaPtr(0), StatCalls(0) {
+PCHGenerator::PCHGenerator(const Preprocessor &PP,
+ const char *isysroot,
+ llvm::raw_ostream *OS)
+ : PP(PP), isysroot(isysroot), Out(OS), SemaPtr(0), StatCalls(0) {
// Install a stat() listener to keep track of all of the stat()
// calls.
@@ -56,14 +61,14 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
if (PP.getDiagnostics().hasErrorOccurred())
return;
- // Write the PCH contents into a buffer
+ // Write the PCH contents into a buffer
std::vector Buffer;
BitstreamWriter Stream(Buffer);
PCHWriter Writer(Stream);
// Emit the PCH file
assert(SemaPtr && "No Sema?");
- Writer.WritePCH(*SemaPtr, StatCalls);
+ Writer.WritePCH(*SemaPtr, StatCalls, isysroot);
// Write the generated bitstream to "Out".
Out->write((char *)&Buffer.front(), Buffer.size());
@@ -73,6 +78,7 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
}
ASTConsumer *clang::CreatePCHGenerator(const Preprocessor &PP,
- llvm::raw_ostream *OS) {
- return new PCHGenerator(PP, OS);
+ llvm::raw_ostream *OS,
+ const char *isysroot) {
+ return new PCHGenerator(PP, isysroot, OS);
}
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 067cce9b30..bc0e7208d3 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -336,7 +336,8 @@ void PCHValidator::ReadCounter(unsigned Value) {
// PCH reader implementation
//===----------------------------------------------------------------------===//
-PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context)
+PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context,
+ const char *isysroot)
: Listener(new PCHValidator(PP, *this)), SourceMgr(PP.getSourceManager()),
FileMgr(PP.getFileManager()), Diags(PP.getDiagnostics()),
SemaObj(0), PP(&PP), Context(Context), Consumer(0),
@@ -344,25 +345,31 @@ PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context)
IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
TotalSelectorsInMethodPool(0), SelectorOffsets(0),
- TotalNumSelectors(0), Comments(0), NumComments(0),
+ TotalNumSelectors(0), Comments(0), NumComments(0), isysroot(isysroot),
NumStatHits(0), NumStatMisses(0),
NumSLocEntriesRead(0), NumStatementsRead(0),
NumMacrosRead(0), NumMethodPoolSelectorsRead(0), NumMethodPoolMisses(0),
- NumLexicalDeclContextsRead(0), NumVisibleDeclContextsRead(0) { }
+ NumLexicalDeclContextsRead(0), NumVisibleDeclContextsRead(0),
+ CurrentlyLoadingTypeOrDecl(0) {
+ RelocatablePCH = false;
+}
PCHReader::PCHReader(SourceManager &SourceMgr, FileManager &FileMgr,
- Diagnostic &Diags)
+ Diagnostic &Diags, const char *isysroot)
: SourceMgr(SourceMgr), FileMgr(FileMgr), Diags(Diags),
SemaObj(0), PP(0), Context(0), Consumer(0),
IdentifierTableData(0), IdentifierLookupTable(0),
IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
TotalSelectorsInMethodPool(0), SelectorOffsets(0),
- TotalNumSelectors(0), NumStatHits(0), NumStatMisses(0),
+ TotalNumSelectors(0), Comments(0), NumComments(0), isysroot(isysroot),
+ NumStatHits(0), NumStatMisses(0),
NumSLocEntriesRead(0), NumStatementsRead(0),
NumMacrosRead(0), NumMethodPoolSelectorsRead(0), NumMethodPoolMisses(0),
NumLexicalDeclContextsRead(0), NumVisibleDeclContextsRead(0),
- CurrentlyLoadingTypeOrDecl(0) { }
+ CurrentlyLoadingTypeOrDecl(0) {
+ RelocatablePCH = false;
+}
PCHReader::~PCHReader() {}
@@ -652,8 +659,7 @@ bool PCHReader::CheckPredefinesBuffer(const char *PCHPredef,
/// \brief Read the line table in the source manager block.
/// \returns true if ther was an error.
-static bool ParseLineTable(SourceManager &SourceMgr,
- llvm::SmallVectorImpl &Record) {
+bool PCHReader::ParseLineTable(llvm::SmallVectorImpl &Record) {
unsigned Idx = 0;
LineTableInfo &LineTable = SourceMgr.getLineTable();
@@ -664,6 +670,7 @@ static bool ParseLineTable(SourceManager &SourceMgr,
unsigned FilenameLen = Record[Idx++];
std::string Filename(&Record[Idx], &Record[Idx] + FilenameLen);
Idx += FilenameLen;
+ MaybeAddSystemRootToFilename(Filename);
FileIDs[I] = LineTable.getLineTableFilenameID(Filename.c_str(),
Filename.size());
}
@@ -859,7 +866,7 @@ PCHReader::PCHReadResult PCHReader::ReadSourceManagerBlock() {
break;
case pch::SM_LINE_TABLE:
- if (ParseLineTable(SourceMgr, Record))
+ if (ParseLineTable(Record))
return Failure;
break;
@@ -912,10 +919,12 @@ PCHReader::PCHReadResult PCHReader::ReadSLocEntryRecord(unsigned ID) {
return Failure;
case pch::SM_SLOC_FILE_ENTRY: {
- const FileEntry *File = FileMgr.getFile(BlobStart, BlobStart + BlobLen);
+ std::string Filename(BlobStart, BlobStart + BlobLen);
+ MaybeAddSystemRootToFilename(Filename);
+ const FileEntry *File = FileMgr.getFile(Filename);
if (File == 0) {
std::string ErrorStr = "could not find file '";
- ErrorStr.append(BlobStart, BlobLen);
+ ErrorStr += Filename;
ErrorStr += "' referenced by PCH file";
Error(ErrorStr.c_str());
return Failure;
@@ -1096,6 +1105,32 @@ void PCHReader::ReadMacroRecord(uint64_t Offset) {
}
}
+/// \brief If we are loading a relocatable PCH file, and the filename is
+/// not an absolute path, add the system root to the beginning of the file
+/// name.
+void PCHReader::MaybeAddSystemRootToFilename(std::string &Filename) {
+ // If this is not a relocatable PCH file, there's nothing to do.
+ if (!RelocatablePCH)
+ return;
+
+ if (Filename.empty() || Filename[0] == '/' || Filename[0] == '<')
+ return;
+
+ std::string FIXME = Filename;
+
+ if (isysroot == 0) {
+ // If no system root was given, default to '/'
+ Filename.insert(Filename.begin(), '/');
+ return;
+ }
+
+ unsigned Length = strlen(isysroot);
+ if (isysroot[Length - 1] != '/')
+ Filename.insert(Filename.begin(), '/');
+
+ Filename.insert(Filename.begin(), isysroot, isysroot + Length);
+}
+
PCHReader::PCHReadResult
PCHReader::ReadPCHBlock() {
if (Stream.EnterSubBlock(pch::PCH_BLOCK_ID)) {
@@ -1208,6 +1243,7 @@ PCHReader::ReadPCHBlock() {
return IgnorePCH;
}
+ RelocatablePCH = Record[4];
if (Listener) {
std::string TargetTriple(BlobStart, BlobLen);
if (Listener->ReadTargetTriple(TargetTriple))
@@ -1338,6 +1374,7 @@ PCHReader::ReadPCHBlock() {
case pch::ORIGINAL_FILE_NAME:
OriginalFileName.assign(BlobStart, BlobLen);
+ MaybeAddSystemRootToFilename(OriginalFileName);
break;
case pch::COMMENT_RANGES:
diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp
index fee2137314..566df350f6 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -476,11 +476,68 @@ void PCHWriter::WriteBlockInfoBlock() {
Stream.ExitBlock();
}
+/// \brief Adjusts the given filename to only write out the portion of the
+/// filename that is not part of the system root directory.
+///
+/// \param Filename the file name to adjust.
+///
+/// \param isysroot When non-NULL, the PCH file is a relocatable PCH file and
+/// the returned filename will be adjusted by this system root.
+///
+/// \returns either the original filename (if it needs no adjustment) or the
+/// adjusted filename (which points into the @p Filename parameter).
+static const char *
+adjustFilenameForRelocatablePCH(const char *Filename, const char *isysroot) {
+ assert(Filename && "No file name to adjust?");
+
+ if (!isysroot)
+ return Filename;
+
+ // Verify that the filename and the system root have the same prefix.
+ unsigned Pos = 0;
+ for (; Filename[Pos] && isysroot[Pos]; ++Pos)
+ if (Filename[Pos] != isysroot[Pos])
+ return Filename; // Prefixes don't match.
+
+ // We hit the end of the filename before we hit the end of the system root.
+ if (!Filename[Pos])
+ return Filename;
+
+ // If the file name has a '/' at the current position, skip over the '/'.
+ // We distinguish sysroot-based includes from absolute includes by the
+ // absence of '/' at the beginning of sysroot-based includes.
+ if (Filename[Pos] == '/')
+ ++Pos;
+
+ return Filename + Pos;
+}
/// \brief Write the PCH metadata (e.g., i686-apple-darwin9).
-void PCHWriter::WriteMetadata(ASTContext &Context) {
+void PCHWriter::WriteMetadata(ASTContext &Context, const char *isysroot) {
using namespace llvm;
+ // Metadata
+ const TargetInfo &Target = Context.Target;
+ BitCodeAbbrev *MetaAbbrev = new BitCodeAbbrev();
+ MetaAbbrev->Add(BitCodeAbbrevOp(pch::METADATA));
+ MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH major
+ MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH minor
+ MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang major
+ MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang minor
+ MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Relocatable
+ MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Target triple
+ unsigned MetaAbbrevCode = Stream.EmitAbbrev(MetaAbbrev);
+
+ RecordData Record;
+ Record.push_back(pch::METADATA);
+ Record.push_back(pch::VERSION_MAJOR);
+ Record.push_back(pch::VERSION_MINOR);
+ Record.push_back(CLANG_VERSION_MAJOR);
+ Record.push_back(CLANG_VERSION_MINOR);
+ Record.push_back(isysroot != 0);
+ const char *Triple = Target.getTargetTriple();
+ Stream.EmitRecordWithBlob(MetaAbbrevCode, Record, Triple, strlen(Triple));
+
// Original file name
SourceManager &SM = Context.getSourceManager();
if (const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID())) {
@@ -500,31 +557,14 @@ void PCHWriter::WriteMetadata(ASTContext &Context) {
MainFileName = MainFilePath.toString();
}
+ const char *MainFileNameStr = MainFileName.c_str();
+ MainFileNameStr = adjustFilenameForRelocatablePCH(MainFileNameStr,
+ isysroot);
RecordData Record;
Record.push_back(pch::ORIGINAL_FILE_NAME);
- Stream.EmitRecordWithBlob(FileAbbrevCode, Record, MainFileName.c_str(),
- MainFileName.size());
+ Stream.EmitRecordWithBlob(FileAbbrevCode, Record, MainFileNameStr,
+ strlen(MainFileNameStr));
}
-
- // Metadata
- const TargetInfo &Target = Context.Target;
- BitCodeAbbrev *MetaAbbrev = new BitCodeAbbrev();
- MetaAbbrev->Add(BitCodeAbbrevOp(pch::METADATA));
- MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH major
- MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH minor
- MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang major
- MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang minor
- MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Target triple
- unsigned MetaAbbrevCode = Stream.EmitAbbrev(MetaAbbrev);
-
- RecordData Record;
- Record.push_back(pch::METADATA);
- Record.push_back(pch::VERSION_MAJOR);
- Record.push_back(pch::VERSION_MINOR);
- Record.push_back(CLANG_VERSION_MAJOR);
- Record.push_back(CLANG_VERSION_MINOR);
- const char *Triple = Target.getTargetTriple();
- Stream.EmitRecordWithBlob(MetaAbbrevCode, Record, Triple, strlen(Triple));
}
/// \brief Write the LangOptions structure.
@@ -649,15 +689,19 @@ public:
} // end anonymous namespace
/// \brief Write the stat() system call cache to the PCH file.
-void PCHWriter::WriteStatCache(MemorizeStatCalls &StatCalls) {
+void PCHWriter::WriteStatCache(MemorizeStatCalls &StatCalls,
+ const char *isysroot) {
// Build the on-disk hash table containing information about every
// stat() call.
OnDiskChainedHashTableGenerator Generator;
unsigned NumStatEntries = 0;
for (MemorizeStatCalls::iterator Stat = StatCalls.begin(),
StatEnd = StatCalls.end();
- Stat != StatEnd; ++Stat, ++NumStatEntries)
- Generator.insert(Stat->first(), Stat->second);
+ Stat != StatEnd; ++Stat, ++NumStatEntries) {
+ const char *Filename = Stat->first();
+ Filename = adjustFilenameForRelocatablePCH(Filename, isysroot);
+ Generator.insert(Filename, Stat->second);
+ }
// Create the on-disk hash table in a buffer.
llvm::SmallVector StatCacheData;
@@ -753,7 +797,8 @@ static unsigned CreateSLocInstantiationAbbrev(llvm::BitstreamWriter &Stream) {
/// errors), we probably won't have to create file entries for any of
/// the files in the AST.
void PCHWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
- const Preprocessor &PP) {
+ const Preprocessor &PP,
+ const char *isysroot) {
RecordData Record;
// Enter the source manager block.
@@ -774,6 +819,7 @@ void PCHWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
for (unsigned I = 0, N = LineTable.getNumFilenames(); I != N; ++I) {
// Emit the file name
const char *Filename = LineTable.getFilename(I);
+ Filename = adjustFilenameForRelocatablePCH(Filename, isysroot);
unsigned FilenameLen = Filename? strlen(Filename) : 0;
Record.push_back(FilenameLen);
if (FilenameLen)
@@ -850,9 +896,21 @@ void PCHWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
if (Content->Entry) {
// The source location entry is a file. The blob associated
// with this entry is the file name.
- Stream.EmitRecordWithBlob(SLocFileAbbrv, Record,
- Content->Entry->getName(),
- strlen(Content->Entry->getName()));
+
+ // Turn the file name into an absolute path, if it isn't already.
+ const char *Filename = Content->Entry->getName();
+ llvm::sys::Path FilePath(Filename, strlen(Filename));
+ std::string FilenameStr;
+ if (!FilePath.isAbsolute()) {
+ llvm::sys::Path P = llvm::sys::Path::GetCurrentDirectory();
+ P.appendComponent(FilePath.toString());
+ FilenameStr = P.toString();
+ Filename = FilenameStr.c_str();
+ }
+
+ Filename = adjustFilenameForRelocatablePCH(Filename, isysroot);
+ Stream.EmitRecordWithBlob(SLocFileAbbrv, Record, Filename,
+ strlen(Filename));
// FIXME: For now, preload all file source locations, so that
// we get the appropriate File entries in the reader. This is
@@ -1716,7 +1774,8 @@ PCHWriter::PCHWriter(llvm::BitstreamWriter &Stream)
NumStatements(0), NumMacros(0), NumLexicalDeclContexts(0),
NumVisibleDeclContexts(0) { }
-void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls) {
+void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls,
+ const char *isysroot) {
using namespace llvm;
ASTContext &Context = SemaRef.Context;
@@ -1778,11 +1837,11 @@ void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls) {
// Write the remaining PCH contents.
RecordData Record;
Stream.EnterSubblock(pch::PCH_BLOCK_ID, 4);
- WriteMetadata(Context);
+ WriteMetadata(Context, isysroot);
WriteLanguageOptions(Context.getLangOptions());
- if (StatCalls)
- WriteStatCache(*StatCalls);
- WriteSourceManagerBlock(Context.getSourceManager(), PP);
+ if (StatCalls && !isysroot)
+ WriteStatCache(*StatCalls, isysroot);
+ WriteSourceManagerBlock(Context.getSourceManager(), PP, isysroot);
WritePreprocessor(PP);
WriteComments(Context);
diff --git a/test/PCH/libroot/usr/include/reloc.h b/test/PCH/libroot/usr/include/reloc.h
new file mode 100644
index 0000000000..04eeacba8f
--- /dev/null
+++ b/test/PCH/libroot/usr/include/reloc.h
@@ -0,0 +1,15 @@
+#ifndef RELOC_H
+#define RELOC_H
+
+#include
+
+
+
+
+
+
+
+// Line number 13 below is important
+int x = 2;
+
+#endif // RELOC_H
diff --git a/test/PCH/libroot/usr/include/reloc2.h b/test/PCH/libroot/usr/include/reloc2.h
new file mode 100644
index 0000000000..995415ce95
--- /dev/null
+++ b/test/PCH/libroot/usr/include/reloc2.h
@@ -0,0 +1,15 @@
+#ifndef RELOC2_H
+#define RELOC2_H
+#include
+
+
+
+
+
+
+
+
+
+// Line number below is important!
+int y = 2;
+#endif // RELOC2_H
diff --git a/test/PCH/reloc.c b/test/PCH/reloc.c
new file mode 100644
index 0000000000..ba25946149
--- /dev/null
+++ b/test/PCH/reloc.c
@@ -0,0 +1,14 @@
+// RUN: clang-cc -emit-pch -o %t --relocatable-pch -isysroot `pwd`/libroot `pwd`/libroot/usr/include/reloc.h &&
+// RUN: clang-cc -include-pch %t -isysroot `pwd`/libroot %s -verify
+// FIXME (test harness can't do this?): not clang-cc -include-pch %t %s
+
+#include
+
+int x = 2; // expected-error{{redefinition}}
+int y = 5; // expected-error{{redefinition}}
+
+
+
+
+// expected-note{{previous definition}}
+// expected-note{{previous definition}}
\ No newline at end of file
diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp
index 9433c1752e..18bd884f0d 100644
--- a/tools/clang-cc/clang-cc.cpp
+++ b/tools/clang-cc/clang-cc.cpp
@@ -1057,6 +1057,10 @@ static llvm::cl::opt
ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"),
llvm::cl::desc("Include file before parsing"));
+static llvm::cl::opt
+RelocatablePCH("relocatable-pch",
+ llvm::cl::desc("Whether to build a relocatable precompiled "
+ "header"));
//===----------------------------------------------------------------------===//
// Preprocessor include path information.
@@ -1820,8 +1824,16 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
}
case GeneratePCH:
+ if (RelocatablePCH.getValue() && !isysroot.getNumOccurrences()) {
+ PP.Diag(SourceLocation(), diag::err_relocatable_without_without_isysroot);
+ RelocatablePCH.setValue(false);
+ }
+
OS.reset(ComputeOutFile(InFile, 0, true, OutPath));
- Consumer.reset(CreatePCHGenerator(PP, OS.get()));
+ if (RelocatablePCH.getValue())
+ Consumer.reset(CreatePCHGenerator(PP, OS.get(), isysroot.c_str()));
+ else
+ Consumer.reset(CreatePCHGenerator(PP, OS.get()));
CompleteTranslationUnit = false;
break;
@@ -1978,7 +1990,13 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
llvm::OwningPtr Source;
if (!ImplicitIncludePCH.empty()) {
- Reader.reset(new PCHReader(PP, ContextOwner.get()));
+ // If the user specified -isysroot, it will be used for relocatable PCH
+ // files.
+ const char *isysrootPCH = 0;
+ if (isysroot.getNumOccurrences() != 0)
+ isysrootPCH = isysroot.c_str();
+
+ Reader.reset(new PCHReader(PP, ContextOwner.get(), isysrootPCH));
// The user has asked us to include a precompiled header. Load
// the precompiled header into the AST context.