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.