Index: cfe/trunk/include/clang/Frontend/ASTUnit.h =================================================================== --- cfe/trunk/include/clang/Frontend/ASTUnit.h +++ cfe/trunk/include/clang/Frontend/ASTUnit.h @@ -59,6 +59,10 @@ class FrontendAction; class ASTDeserializationListener; +namespace vfs { +class FileSystem; +} + /// \brief Utility class for loading a ASTContext from an AST file. /// class ASTUnit : public ModuleLoader { @@ -420,7 +424,8 @@ explicit ASTUnit(bool MainFileIsAST); bool Parse(std::shared_ptr PCHContainerOps, - std::unique_ptr OverrideMainBuffer); + std::unique_ptr OverrideMainBuffer, + IntrusiveRefCntPtr VFS); struct ComputedPreamble { llvm::MemoryBuffer *Buffer; @@ -434,11 +439,13 @@ PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {} }; ComputedPreamble ComputePreamble(CompilerInvocation &Invocation, - unsigned MaxLines); + unsigned MaxLines, + IntrusiveRefCntPtr VFS); std::unique_ptr getMainBufferWithPrecompiledPreamble( std::shared_ptr PCHContainerOps, - const CompilerInvocation &PreambleInvocationIn, bool AllowRebuild = true, + const CompilerInvocation &PreambleInvocationIn, + IntrusiveRefCntPtr VFS, bool AllowRebuild = true, unsigned MaxLines = 0); void RealizeTopLevelDeclsFromPreamble(); @@ -731,11 +738,17 @@ /// of this translation unit should be precompiled, to improve the performance /// of reparsing. Set to zero to disable preambles. /// + /// \param VFS - A vfs::FileSystem to be used for all file accesses. Note that + /// preamble is saved to a temporary directory on a RealFileSystem, so in order + /// for it to be loaded correctly, VFS should have access to it(i.e., be an + /// overlay over RealFileSystem). + /// /// \returns \c true if a catastrophic failure occurred (which means that the /// \c ASTUnit itself is invalid), or \c false otherwise. bool LoadFromCompilerInvocation( std::shared_ptr PCHContainerOps, - unsigned PrecompilePreambleAfterNParses); + unsigned PrecompilePreambleAfterNParses, + IntrusiveRefCntPtr VFS); public: @@ -826,6 +839,11 @@ /// (e.g. because the PCH could not be loaded), this accepts the ASTUnit /// mainly to allow the caller to see the diagnostics. /// + /// \param VFS - A vfs::FileSystem to be used for all file accesses. Note that + /// preamble is saved to a temporary directory on a RealFileSystem, so in order + /// for it to be loaded correctly, VFS should have access to it(i.e., be an + /// overlay over RealFileSystem). RealFileSystem will be used if \p VFS is nullptr. + /// // FIXME: Move OnlyLocalDecls, UseBumpAllocator to setters on the ASTUnit, we // shouldn't need to specify them at construction time. static ASTUnit *LoadFromCommandLine( @@ -842,15 +860,23 @@ bool AllowPCHWithCompilerErrors = false, bool SkipFunctionBodies = false, bool UserFilesAreVolatile = false, bool ForSerialization = false, llvm::Optional ModuleFormat = llvm::None, - std::unique_ptr *ErrAST = nullptr); + std::unique_ptr *ErrAST = nullptr, + IntrusiveRefCntPtr VFS = nullptr); /// \brief Reparse the source files using the same command-line options that /// were originally used to produce this translation unit. /// + /// \param VFS - A vfs::FileSystem to be used for all file accesses. Note that + /// preamble is saved to a temporary directory on a RealFileSystem, so in order + /// for it to be loaded correctly, VFS should give an access to this(i.e. be an + /// overlay over RealFileSystem). FileMgr->getVirtualFileSystem() will be used if + /// \p VFS is nullptr. + /// /// \returns True if a failure occurred that causes the ASTUnit not to /// contain any translation-unit information, false otherwise. bool Reparse(std::shared_ptr PCHContainerOps, - ArrayRef RemappedFiles = None); + ArrayRef RemappedFiles = None, + IntrusiveRefCntPtr VFS = nullptr); /// \brief Perform code completion at the given file, line, and /// column within this translation unit. Index: cfe/trunk/include/clang/Frontend/CompilerInvocation.h =================================================================== --- cfe/trunk/include/clang/Frontend/CompilerInvocation.h +++ cfe/trunk/include/clang/Frontend/CompilerInvocation.h @@ -225,6 +225,11 @@ createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags); +IntrusiveRefCntPtr +createVFSFromCompilerInvocation(const CompilerInvocation &CI, + DiagnosticsEngine &Diags, + IntrusiveRefCntPtr BaseFS); + } // end namespace clang #endif Index: cfe/trunk/lib/Frontend/ASTUnit.cpp =================================================================== --- cfe/trunk/lib/Frontend/ASTUnit.cpp +++ cfe/trunk/lib/Frontend/ASTUnit.cpp @@ -90,6 +90,21 @@ /// \brief Erase temporary files and the preamble file. void Cleanup(); }; + + template + std::unique_ptr valueOrNull(llvm::ErrorOr> Val) { + if (!Val) + return nullptr; + return std::move(*Val); + } + + template + bool moveOnNoError(llvm::ErrorOr Val, T &Output) { + if (!Val) + return false; + Output = std::move(*Val); + return true; + } } static llvm::sys::SmartMutex &getOnDiskMutex() { @@ -1019,7 +1034,8 @@ /// \returns True if a failure occurred that causes the ASTUnit not to /// contain any translation-unit information, false otherwise. bool ASTUnit::Parse(std::shared_ptr PCHContainerOps, - std::unique_ptr OverrideMainBuffer) { + std::unique_ptr OverrideMainBuffer, + IntrusiveRefCntPtr VFS) { SavedMainFileBuffer.reset(); if (!Invocation) @@ -1028,6 +1044,12 @@ // Create the compiler instance to use for building the AST. std::unique_ptr Clang( new CompilerInstance(std::move(PCHContainerOps))); + if (FileMgr && VFS) { + assert(VFS == FileMgr->getVirtualFileSystem() && + "VFS passed to Parse and VFS in FileMgr are different"); + } else if (VFS) { + Clang->setVirtualFileSystem(VFS); + } // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar @@ -1170,7 +1192,8 @@ /// that corresponds to the main file along with a pair (bytes, start-of-line) /// that describes the preamble. ASTUnit::ComputedPreamble -ASTUnit::ComputePreamble(CompilerInvocation &Invocation, unsigned MaxLines) { +ASTUnit::ComputePreamble(CompilerInvocation &Invocation, unsigned MaxLines, + IntrusiveRefCntPtr VFS) { FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); PreprocessorOptions &PreprocessorOpts = Invocation.getPreprocessorOpts(); @@ -1180,28 +1203,32 @@ llvm::MemoryBuffer *Buffer = nullptr; std::unique_ptr BufferOwner; std::string MainFilePath(FrontendOpts.Inputs[0].getFile()); - llvm::sys::fs::UniqueID MainFileID; - if (!llvm::sys::fs::getUniqueID(MainFilePath, MainFileID)) { + auto MainFileStatus = VFS->status(MainFilePath); + if (MainFileStatus) { + llvm::sys::fs::UniqueID MainFileID = MainFileStatus->getUniqueID(); + // Check whether there is a file-file remapping of the main file for (const auto &RF : PreprocessorOpts.RemappedFiles) { std::string MPath(RF.first); - llvm::sys::fs::UniqueID MID; - if (!llvm::sys::fs::getUniqueID(MPath, MID)) { + auto MPathStatus = VFS->status(MPath); + if (MPathStatus) { + llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID(); if (MainFileID == MID) { // We found a remapping. Try to load the resulting, remapped source. - BufferOwner = getBufferForFile(RF.second); + BufferOwner = valueOrNull(VFS->getBufferForFile(RF.second)); if (!BufferOwner) return ComputedPreamble(nullptr, nullptr, 0, true); } } } - + // Check whether there is a file-buffer remapping. It supercedes the // file-file remapping. for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { std::string MPath(RB.first); - llvm::sys::fs::UniqueID MID; - if (!llvm::sys::fs::getUniqueID(MPath, MID)) { + auto MPathStatus = VFS->status(MPath); + if (MPathStatus) { + llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID(); if (MainFileID == MID) { // We found a remapping. BufferOwner.reset(); @@ -1213,7 +1240,7 @@ // If the main source file was not remapped, load it now. if (!Buffer && !BufferOwner) { - BufferOwner = getBufferForFile(FrontendOpts.Inputs[0].getFile()); + BufferOwner = valueOrNull(VFS->getBufferForFile(FrontendOpts.Inputs[0].getFile())); if (!BufferOwner) return ComputedPreamble(nullptr, nullptr, 0, true); } @@ -1324,8 +1351,10 @@ std::unique_ptr ASTUnit::getMainBufferWithPrecompiledPreamble( std::shared_ptr PCHContainerOps, - const CompilerInvocation &PreambleInvocationIn, bool AllowRebuild, + const CompilerInvocation &PreambleInvocationIn, + IntrusiveRefCntPtr VFS, bool AllowRebuild, unsigned MaxLines) { + assert(VFS && "VFS is null"); auto PreambleInvocation = std::make_shared(PreambleInvocationIn); @@ -1333,7 +1362,8 @@ PreprocessorOptions &PreprocessorOpts = PreambleInvocation->getPreprocessorOpts(); - ComputedPreamble NewPreamble = ComputePreamble(*PreambleInvocation, MaxLines); + ComputedPreamble NewPreamble = + ComputePreamble(*PreambleInvocation, MaxLines, VFS); if (!NewPreamble.Size) { // We couldn't find a preamble in the main source. Clear out the current @@ -1369,7 +1399,7 @@ break; vfs::Status Status; - if (FileMgr->getNoncachedStatValue(R.second, Status)) { + if (!moveOnNoError(VFS->status(R.second), Status)) { // If we can't stat the file we're remapping to, assume that something // horrible happened. AnyFileChanged = true; @@ -1386,7 +1416,7 @@ break; vfs::Status Status; - if (FileMgr->getNoncachedStatValue(RB.first, Status)) { + if (!moveOnNoError(VFS->status(RB.first), Status)) { AnyFileChanged = true; break; } @@ -1401,7 +1431,7 @@ !AnyFileChanged && F != FEnd; ++F) { vfs::Status Status; - if (FileMgr->getNoncachedStatValue(F->first(), Status)) { + if (!moveOnNoError(VFS->status(F->first()), Status)) { // If we can't stat the file, assume that something horrible happened. AnyFileChanged = true; break; @@ -1546,14 +1576,14 @@ TopLevelDeclsInPreamble.clear(); PreambleDiagnostics.clear(); - IntrusiveRefCntPtr VFS = - createVFSFromCompilerInvocation(Clang->getInvocation(), getDiagnostics()); + VFS = createVFSFromCompilerInvocation(Clang->getInvocation(), + getDiagnostics(), VFS); if (!VFS) return nullptr; // Create a file manager object to provide access to and cache the filesystem. Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS)); - + // Create the source manager. Clang->setSourceManager(new SourceManager(getDiagnostics(), Clang->getFileManager())); @@ -1863,10 +1893,13 @@ bool ASTUnit::LoadFromCompilerInvocation( std::shared_ptr PCHContainerOps, - unsigned PrecompilePreambleAfterNParses) { + unsigned PrecompilePreambleAfterNParses, + IntrusiveRefCntPtr VFS) { if (!Invocation) return true; - + + assert(VFS && "VFS is null"); + // We'll manage file buffers ourselves. Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true; Invocation->getFrontendOpts().DisableFree = false; @@ -1877,19 +1910,19 @@ if (PrecompilePreambleAfterNParses > 0) { PreambleRebuildCounter = PrecompilePreambleAfterNParses; OverrideMainBuffer = - getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation); + getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); } - + SimpleTimer ParsingTimer(WantTiming); ParsingTimer.setOutput("Parsing " + getMainFileName()); - + // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar MemBufferCleanup(OverrideMainBuffer.get()); - return Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer)); + return Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); } std::unique_ptr ASTUnit::LoadFromCompilerInvocation( @@ -1923,7 +1956,8 @@ DiagCleanup(Diags.get()); if (AST->LoadFromCompilerInvocation(std::move(PCHContainerOps), - PrecompilePreambleAfterNParses)) + PrecompilePreambleAfterNParses, + AST->FileMgr->getVirtualFileSystem())) return nullptr; return AST; } @@ -1938,7 +1972,8 @@ bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors, bool SkipFunctionBodies, bool UserFilesAreVolatile, bool ForSerialization, - llvm::Optional ModuleFormat, std::unique_ptr *ErrAST) { + llvm::Optional ModuleFormat, std::unique_ptr *ErrAST, + IntrusiveRefCntPtr VFS) { assert(Diags.get() && "no DiagnosticsEngine was provided"); SmallVector StoredDiagnostics; @@ -1979,8 +2014,9 @@ ConfigureDiags(Diags, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; AST->FileSystemOpts = CI->getFileSystemOpts(); - IntrusiveRefCntPtr VFS = - createVFSFromCompilerInvocation(*CI, *Diags); + if (!VFS) + VFS = vfs::getRealFileSystem(); + VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS); if (!VFS) return nullptr; AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); @@ -2006,7 +2042,8 @@ ASTUnitCleanup(AST.get()); if (AST->LoadFromCompilerInvocation(std::move(PCHContainerOps), - PrecompilePreambleAfterNParses)) { + PrecompilePreambleAfterNParses, + VFS)) { // Some error occurred, if caller wants to examine diagnostics, pass it the // ASTUnit. if (ErrAST) { @@ -2020,10 +2057,16 @@ } bool ASTUnit::Reparse(std::shared_ptr PCHContainerOps, - ArrayRef RemappedFiles) { + ArrayRef RemappedFiles, + IntrusiveRefCntPtr VFS) { if (!Invocation) return true; + if (!VFS) { + assert(FileMgr && "FileMgr is null on Reparse call"); + VFS = FileMgr->getVirtualFileSystem(); + } + clearFileLevelDecls(); SimpleTimer ParsingTimer(WantTiming); @@ -2045,7 +2088,8 @@ std::unique_ptr OverrideMainBuffer; if (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0) OverrideMainBuffer = - getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation); + getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); + // Clear out the diagnostics state. FileMgr.reset(); @@ -2056,7 +2100,7 @@ // Parse the sources bool Result = - Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer)); + Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); // If we're caching global code-completion results, and the top-level // declarations have changed, clear out the code-completion cache. @@ -2414,15 +2458,19 @@ std::unique_ptr OverrideMainBuffer; if (!getPreambleFile(this).empty()) { std::string CompleteFilePath(File); - llvm::sys::fs::UniqueID CompleteFileID; - if (!llvm::sys::fs::getUniqueID(CompleteFilePath, CompleteFileID)) { + auto VFS = FileMgr.getVirtualFileSystem(); + auto CompleteFileStatus = VFS->status(CompleteFilePath); + if (CompleteFileStatus) { + llvm::sys::fs::UniqueID CompleteFileID = CompleteFileStatus->getUniqueID(); + std::string MainPath(OriginalSourceFile); - llvm::sys::fs::UniqueID MainID; - if (!llvm::sys::fs::getUniqueID(MainPath, MainID)) { + auto MainStatus = VFS->status(MainPath); + if (MainStatus) { + llvm::sys::fs::UniqueID MainID = MainStatus->getUniqueID(); if (CompleteFileID == MainID && Line > 1) OverrideMainBuffer = getMainBufferWithPrecompiledPreamble( - PCHContainerOps, Inv, false, Line - 1); + PCHContainerOps, Inv, VFS, false, Line - 1); } } } Index: cfe/trunk/lib/Frontend/CompilerInvocation.cpp =================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp @@ -2750,15 +2750,22 @@ IntrusiveRefCntPtr createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags) { + return createVFSFromCompilerInvocation(CI, Diags, vfs::getRealFileSystem()); +} + +IntrusiveRefCntPtr +createVFSFromCompilerInvocation(const CompilerInvocation &CI, + DiagnosticsEngine &Diags, + IntrusiveRefCntPtr BaseFS) { if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty()) - return vfs::getRealFileSystem(); + return BaseFS; - IntrusiveRefCntPtr - Overlay(new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + IntrusiveRefCntPtr Overlay( + new vfs::OverlayFileSystem(BaseFS)); // earlier vfs files are on the bottom for (const std::string &File : CI.getHeaderSearchOpts().VFSOverlayFiles) { llvm::ErrorOr> Buffer = - llvm::MemoryBuffer::getFile(File); + BaseFS->getBufferForFile(File); if (!Buffer) { Diags.Report(diag::err_missing_vfs_overlay_file) << File; return IntrusiveRefCntPtr();