Introduce the notion of "Relocatable" precompiled headers, which are built
with a particular system root directory and can be used with a different
system root directory when the headers it depends on have been installed.
Relocatable precompiled headers rewrite the file names of the headers used
when generating the PCH file into the corresponding file names of the 
headers available when using the PCH file.

Addresses <rdar://problem/7001604>.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@74885 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/GeneratePCH.cpp b/lib/Frontend/GeneratePCH.cpp
index 8be88ce..3b7715a 100644
--- a/lib/Frontend/GeneratePCH.cpp
+++ b/lib/Frontend/GeneratePCH.cpp
@@ -32,19 +32,24 @@
 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 @@
   if (PP.getDiagnostics().hasErrorOccurred())
     return;
 
- // Write the PCH contents into a buffer
+  // Write the PCH contents into a buffer
   std::vector<unsigned char> 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 @@
 }
 
 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 067cce9..bc0e720 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -336,7 +336,8 @@
 // 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 @@
     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 @@
 
 /// \brief Read the line table in the source manager block.
 /// \returns true if ther was an error.
-static bool ParseLineTable(SourceManager &SourceMgr, 
-                           llvm::SmallVectorImpl<uint64_t> &Record) {
+bool PCHReader::ParseLineTable(llvm::SmallVectorImpl<uint64_t> &Record) {
   unsigned Idx = 0;
   LineTableInfo &LineTable = SourceMgr.getLineTable();
 
@@ -664,6 +670,7 @@
     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 @@
       break;
 
     case pch::SM_LINE_TABLE:
-      if (ParseLineTable(SourceMgr, Record))
+      if (ParseLineTable(Record))
         return Failure;
       break;
 
@@ -912,10 +919,12 @@
     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 @@
   }
 }
 
+/// \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 @@
         return IgnorePCH;
       }
 
+      RelocatablePCH = Record[4];
       if (Listener) {
         std::string TargetTriple(BlobStart, BlobLen);
         if (Listener->ReadTargetTriple(TargetTriple))
@@ -1338,6 +1374,7 @@
 
     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 fee2137..566df35 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -476,11 +476,68 @@
   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 @@
       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 @@
 } // 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<PCHStatCacheTrait> 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<char, 4096> StatCacheData; 
@@ -753,7 +797,8 @@
 /// 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 @@
     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 @@
       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 @@
     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 @@
   // 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);