[cfe-commits] r74885 - in /cfe/trunk: docs/ include/clang/Basic/ include/clang/Driver/ include/clang/Frontend/ lib/Driver/ lib/Frontend/ test/PCH/ test/PCH/libroot/ test/PCH/libroot/usr/ test/PCH/libroot/usr/include/ tools/clang-cc/

Douglas Gregor dgregor at apple.com
Mon Jul 6 17:13:00 PDT 2009


Author: dgregor
Date: Mon Jul  6 19:12:59 2009
New Revision: 74885

URL: http://llvm.org/viewvc/llvm-project?rev=74885&view=rev
Log:
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>.


Added:
    cfe/trunk/test/PCH/libroot/
    cfe/trunk/test/PCH/libroot/usr/
    cfe/trunk/test/PCH/libroot/usr/include/
    cfe/trunk/test/PCH/libroot/usr/include/reloc.h
    cfe/trunk/test/PCH/libroot/usr/include/reloc2.h
    cfe/trunk/test/PCH/reloc.c
Modified:
    cfe/trunk/docs/UsersManual.html
    cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
    cfe/trunk/include/clang/Driver/Options.def
    cfe/trunk/include/clang/Frontend/ASTConsumers.h
    cfe/trunk/include/clang/Frontend/PCHReader.h
    cfe/trunk/include/clang/Frontend/PCHWriter.h
    cfe/trunk/lib/Driver/Tools.cpp
    cfe/trunk/lib/Frontend/GeneratePCH.cpp
    cfe/trunk/lib/Frontend/PCHReader.cpp
    cfe/trunk/lib/Frontend/PCHWriter.cpp
    cfe/trunk/tools/clang-cc/clang-cc.cpp

Modified: cfe/trunk/docs/UsersManual.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/UsersManual.html?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/docs/UsersManual.html (original)
+++ cfe/trunk/docs/UsersManual.html Mon Jul  6 19:12:59 2009
@@ -465,6 +465,50 @@
 <tt>test.h</tt> since <tt>test.h</tt> was included directly in the source file
 and not specified on the command line using <tt>-include</tt>.</p>
 
+<h4>Relocatable PCH Files</h4>
+<p>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.</p>
+
+<p>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 <code>mylib.h</code> that
+will be installed into <code>/usr/include</code>, create a subdirectory 
+<code>build/usr/include</code> and place the header <code>mylib.h</code> into
+that subdirectory. If <code>mylib.h</code> depends on other headers, then 
+they can be stored within <code>build/usr/include</code> in a way that mimics
+the installed location.</p>
+
+<p>Building a relocatable precompiled header requires two additional arguments.
+First, pass the <code>--relocatable-pch</code> flag to indicate that the
+resulting PCH file should be relocatable. Second, pass 
+<code>-isysroot /path/to/build</code>, which makes all includes for your
+library relative to the build directory. For example:</p>
+
+<pre>
+  # clang -x c-header --relocatable-pch -isysroot /path/to/build /path/to/build/mylib.h mylib.h.pch
+</pre>
+
+<p>When loading the relocatable PCH file, the various headers used in the PCH
+file are found from the system header root. For example, <code>mylib.h</code>
+can be found in <code>/usr/include/mylib.h</code>. If the headers are installed
+in some other system root, the <code>-isysroot</code> option can be used provide
+a different system root from which the headers will be based. For example,
+<code>-isysroot /Developer/SDKs/MacOSX10.4u.sdk</code> will look for 
+<code>mylib.h</code> in 
+<code>/Developer/SDKs/MacOSX10.4u.sdk/usr/include/mylib.h</code>.</p>
+
+<p>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,
+<code>stat()</code> caching, to be disabled. However, this change is only 
+likely to affect PCH files that reference a large number of headers.</p>
 
 <!-- ======================================================================= -->
 <h2 id="c">C Language Features</h2>

Modified: cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td Mon Jul  6 19:12:59 2009
@@ -24,6 +24,9 @@
     "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'">;

Modified: cfe/trunk/include/clang/Driver/Options.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.def?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/include/clang/Driver/Options.def (original)
+++ cfe/trunk/include/clang/Driver/Options.def Mon Jul  6 19:12:59 2009
@@ -223,6 +223,8 @@
 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)

Modified: cfe/trunk/include/clang/Frontend/ASTConsumers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/ASTConsumers.h?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/include/clang/Frontend/ASTConsumers.h (original)
+++ cfe/trunk/include/clang/Frontend/ASTConsumers.h Mon Jul  6 19:12:59 2009
@@ -92,7 +92,8 @@
 // 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.

Modified: cfe/trunk/include/clang/Frontend/PCHReader.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PCHReader.h?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/include/clang/Frontend/PCHReader.h (original)
+++ cfe/trunk/include/clang/Frontend/PCHReader.h Mon Jul  6 19:12:59 2009
@@ -310,6 +310,13 @@
   /// 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<unsigned, SwitchCase *> SwitchCaseStmts;
@@ -428,10 +435,13 @@
   /// 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<uint64_t> &Record);
   PCHReadResult ReadSourceManagerBlock();
   PCHReadResult ReadSLocEntryRecord(unsigned ID);
   
@@ -448,19 +458,42 @@
 
   PCHReader(const PCHReader&); // do not implement
   PCHReader &operator=(const PCHReader &); // do not implement
-
 public:
   typedef llvm::SmallVector<uint64_t, 64> 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

Modified: cfe/trunk/include/clang/Frontend/PCHWriter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PCHWriter.h?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/include/clang/Frontend/PCHWriter.h (original)
+++ cfe/trunk/include/clang/Frontend/PCHWriter.h Mon Jul  6 19:12:59 2009
@@ -160,11 +160,12 @@
   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 @@
   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);

Modified: cfe/trunk/lib/Driver/Tools.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Tools.cpp?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/lib/Driver/Tools.cpp (original)
+++ cfe/trunk/lib/Driver/Tools.cpp Mon Jul  6 19:12:59 2009
@@ -474,6 +474,9 @@
     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);

Modified: cfe/trunk/lib/Frontend/GeneratePCH.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/GeneratePCH.cpp?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/GeneratePCH.cpp (original)
+++ cfe/trunk/lib/Frontend/GeneratePCH.cpp Mon Jul  6 19:12:59 2009
@@ -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);
 }

Modified: cfe/trunk/lib/Frontend/PCHReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReader.cpp?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/PCHReader.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHReader.cpp Mon Jul  6 19:12:59 2009
@@ -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:

Modified: cfe/trunk/lib/Frontend/PCHWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriter.cpp?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/PCHWriter.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHWriter.cpp Mon Jul  6 19:12:59 2009
@@ -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);  
   

Added: cfe/trunk/test/PCH/libroot/usr/include/reloc.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/libroot/usr/include/reloc.h?rev=74885&view=auto

==============================================================================
--- cfe/trunk/test/PCH/libroot/usr/include/reloc.h (added)
+++ cfe/trunk/test/PCH/libroot/usr/include/reloc.h Mon Jul  6 19:12:59 2009
@@ -0,0 +1,15 @@
+#ifndef RELOC_H
+#define RELOC_H
+
+#include <reloc2.h>
+
+
+
+
+
+
+
+// Line number 13 below is important
+int x = 2;
+
+#endif // RELOC_H

Added: cfe/trunk/test/PCH/libroot/usr/include/reloc2.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/libroot/usr/include/reloc2.h?rev=74885&view=auto

==============================================================================
--- cfe/trunk/test/PCH/libroot/usr/include/reloc2.h (added)
+++ cfe/trunk/test/PCH/libroot/usr/include/reloc2.h Mon Jul  6 19:12:59 2009
@@ -0,0 +1,15 @@
+#ifndef RELOC2_H
+#define RELOC2_H
+#include <stddef.h>
+
+
+
+
+
+
+
+
+
+// Line number below is important!
+int y = 2;
+#endif // RELOC2_H

Added: cfe/trunk/test/PCH/reloc.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/reloc.c?rev=74885&view=auto

==============================================================================
--- cfe/trunk/test/PCH/reloc.c (added)
+++ cfe/trunk/test/PCH/reloc.c Mon Jul  6 19:12:59 2009
@@ -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 <reloc.h>
+
+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

Modified: cfe/trunk/tools/clang-cc/clang-cc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-cc/clang-cc.cpp?rev=74885&r1=74884&r2=74885&view=diff

==============================================================================
--- cfe/trunk/tools/clang-cc/clang-cc.cpp (original)
+++ cfe/trunk/tools/clang-cc/clang-cc.cpp Mon Jul  6 19:12:59 2009
@@ -1057,6 +1057,10 @@
 ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"),
                    llvm::cl::desc("Include file before parsing"));
 
+static llvm::cl::opt<bool>
+RelocatablePCH("relocatable-pch", 
+               llvm::cl::desc("Whether to build a relocatable precompiled "
+                              "header"));
 
 //===----------------------------------------------------------------------===//
 // Preprocessor include path information.
@@ -1820,8 +1824,16 @@
   }
 
   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 @@
   llvm::OwningPtr<ExternalASTSource> 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.





More information about the cfe-commits mailing list