cameron314 updated this revision to Diff 114016.
cameron314 added a comment.
Here's an updated patch that allows only the PCH to be accessed from the real
FS when a VSF is present. Tests still pass.
https://reviews.llvm.org/D37474
Files:
lib/Frontend/ASTUnit.cpp
unittests/Frontend/CMakeLists.txt
unittests/Frontend/PchPreambleTest.cpp
Index: unittests/Frontend/PchPreambleTest.cpp
===================================================================
--- /dev/null
+++ unittests/Frontend/PchPreambleTest.cpp
@@ -0,0 +1,156 @@
+//====-- unittests/Frontend/PchPreambleTest.cpp - FrontendAction tests ---====//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem
+{
+ std::map<std::string, unsigned> ReadCounts;
+
+public:
+ ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override
+ {
+ SmallVector<char, 128> PathVec;
+ Path.toVector(PathVec);
+ llvm::sys::path::remove_dots(PathVec, true);
+ ++ReadCounts[std::string(PathVec.begin(), PathVec.end())];
+ return InMemoryFileSystem::openFileForRead(Path);
+ }
+
+ unsigned GetReadCount(const Twine &Path) const
+ {
+ auto it = ReadCounts.find(Path.str());
+ return it == ReadCounts.end() ? 0 : it->second;
+ }
+};
+
+class PchPreambleTest : public ::testing::Test {
+ IntrusiveRefCntPtr<ReadCountingInMemoryFileSystem> VFS;
+ StringMap<std::string> RemappedFiles;
+ std::shared_ptr<PCHContainerOperations> PCHContainerOpts;
+ FileSystemOptions FSOpts;
+
+public:
+ void SetUp() override {
+ VFS = new ReadCountingInMemoryFileSystem();
+ // We need the working directory to be set to something absolute,
+ // otherwise it ends up being inadvertently set to the current
+ // working directory in the real file system due to a series of
+ // unfortunate conditions interacting badly.
+ // What's more, this path *must* be absolute on all (real)
+ // filesystems, so just '/' won't work (e.g. on Win32).
+ VFS->setCurrentWorkingDirectory("//./");
+ }
+
+ void TearDown() override {
+ }
+
+ void AddFile(const std::string &Filename, const std::string &Contents) {
+ ::time_t now;
+ ::time(&now);
+ VFS->addFile(Filename, now, MemoryBuffer::getMemBufferCopy(Contents, Filename));
+ }
+
+ void RemapFile(const std::string &Filename, const std::string &Contents) {
+ RemappedFiles[Filename] = Contents;
+ }
+
+ std::unique_ptr<ASTUnit> ParseAST(const std::string &EntryFile) {
+ PCHContainerOpts = std::make_shared<PCHContainerOperations>();
+ std::shared_ptr<CompilerInvocation> CI(new CompilerInvocation);
+ CI->getFrontendOpts().Inputs.push_back(
+ FrontendInputFile(EntryFile, FrontendOptions::getInputKindForExtension(
+ llvm::sys::path::extension(EntryFile).substr(1))));
+
+ CI->getTargetOpts().Triple = "i386-unknown-linux-gnu";
+
+ CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles();
+
+ PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
+ PPOpts.RemappedFilesKeepOriginalName = true;
+
+ IntrusiveRefCntPtr<DiagnosticsEngine>
+ Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, new DiagnosticConsumer));
+
+ FileManager *FileMgr = new FileManager(FSOpts, VFS);
+
+ std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
+ CI, PCHContainerOpts, Diags, FileMgr, false, false,
+ /*PrecompilePreambleAfterNParses=*/1);
+ return AST;
+ }
+
+ bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) {
+ bool reparseFailed = AST->Reparse(PCHContainerOpts, GetRemappedFiles(), VFS);
+ return !reparseFailed;
+ }
+
+ unsigned GetFileReadCount(const std::string &Filename) const {
+ return VFS->GetReadCount(Filename);
+ }
+
+private:
+ std::vector<std::pair<std::string, llvm::MemoryBuffer *>>
+ GetRemappedFiles() const {
+ std::vector<std::pair<std::string, llvm::MemoryBuffer *>> Remapped;
+ for (const auto &RemappedFile : RemappedFiles) {
+ std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy(
+ RemappedFile.second, RemappedFile.first());
+ Remapped.emplace_back(RemappedFile.first(), buf.release());
+ }
+ return Remapped;
+ }
+};
+
+TEST_F(PchPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) {
+ std::string Header1 = "//./header1.h";
+ std::string Header2 = "//./header2.h";
+ std::string MainName = "//./main.cpp";
+ AddFile(Header1, "");
+ AddFile(Header2, "#pragma once");
+ AddFile(MainName,
+ "#include \"//./foo/../header1.h\"\n"
+ "#include \"//./foo/../header2.h\"\n"
+ "int main() { return ZERO; }");
+ RemapFile(Header1, "static const int ZERO = 0;\n");
+
+ std::unique_ptr<ASTUnit> AST(ParseAST(MainName));
+ ASSERT_TRUE(AST.get());
+ ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+ unsigned initialCounts[] = {
+ GetFileReadCount(MainName),
+ GetFileReadCount(Header1),
+ GetFileReadCount(Header2)
+ };
+
+ ASSERT_TRUE(ReparseAST(AST));
+
+ ASSERT_NE(initialCounts[0], GetFileReadCount(MainName));
+ ASSERT_EQ(initialCounts[1], GetFileReadCount(Header1));
+ ASSERT_EQ(initialCounts[2], GetFileReadCount(Header2));
+}
+
+} // anonymous namespace
Index: unittests/Frontend/CMakeLists.txt
===================================================================
--- unittests/Frontend/CMakeLists.txt
+++ unittests/Frontend/CMakeLists.txt
@@ -6,6 +6,7 @@
ASTUnitTest.cpp
FrontendActionTest.cpp
CodeGenActionTest.cpp
+ PchPreambleTest.cpp
)
target_link_libraries(FrontendTests
clangAST
Index: lib/Frontend/ASTUnit.cpp
===================================================================
--- lib/Frontend/ASTUnit.cpp
+++ lib/Frontend/ASTUnit.cpp
@@ -38,6 +38,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mutex.h"
@@ -1008,6 +1009,59 @@
}
}
+/// Overlays the PCH file system (restricted to only access the PCH)
+/// with another virtual file system.
+class PCHOverlayFileSystem : public vfs::FileSystem
+{
+public:
+ PCHOverlayFileSystem(const std::string &PCH,
+ IntrusiveRefCntPtr<vfs::FileSystem> PCHFS,
+ IntrusiveRefCntPtr<vfs::FileSystem> BaseFS)
+ : PCH(PCH), PCHFS(PCHFS), BaseFS(BaseFS) { }
+
+ PCHOverlayFileSystem(PCHOverlayFileSystem const&) = delete;
+ PCHOverlayFileSystem& operator=(PCHOverlayFileSystem const&) = delete;
+
+ llvm::ErrorOr<vfs::Status> status(const Twine &Path) override {
+ if (isWithinPCH(Path))
+ return PCHFS->status(Path);
+ return BaseFS->status(Path);
+ }
+
+ llvm::ErrorOr<std::unique_ptr<vfs::File>>
+ openFileForRead(const Twine &Path) override {
+ if (isWithinPCH(Path))
+ return PCHFS->openFileForRead(Path);
+ return BaseFS->openFileForRead(Path);
+ }
+
+ vfs::directory_iterator
+ dir_begin(const Twine &Dir, std::error_code &EC) override {
+ if (isWithinPCH(Dir))
+ return PCHFS->dir_begin(Dir, EC);
+ return BaseFS->dir_begin(Dir, EC);
+ }
+
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+ return BaseFS->setCurrentWorkingDirectory(Path);
+ }
+
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+ return BaseFS->getCurrentWorkingDirectory();
+ }
+
+private:
+ bool isWithinPCH(const Twine &Path) const {
+ SmallString<128> PathVec;
+ return Path.toStringRef(PathVec).startswith(PCH);
+ }
+
+private:
+ std::string PCH;
+ IntrusiveRefCntPtr<vfs::FileSystem> PCHFS;
+ IntrusiveRefCntPtr<vfs::FileSystem> BaseFS;
+};
+
/// Parse the source file into a translation unit using the given compiler
/// invocation, replacing the current translation unit.
///
@@ -1029,6 +1083,26 @@
Clang->setVirtualFileSystem(VFS);
}
+ // Make sure we can access the PCH file even if we're using a VFS
+ if (!VFS && FileMgr)
+ VFS = FileMgr->getVirtualFileSystem();
+ IntrusiveRefCntPtr<vfs::FileSystem> RealFS = vfs::getRealFileSystem();
+ if (OverrideMainBuffer && VFS && RealFS && VFS != RealFS) {
+ // We have a slight inconsistency here -- we're using the VFS to
+ // read files, but the PCH was generated in the real file system.
+ // No choice but to overlay the two.
+ VFS = new PCHOverlayFileSystem(
+ Invocation->PreprocessorOpts->ImplicitPCHInclude,
+ RealFS, VFS);
+ if (FileMgr) {
+ FileMgr = new FileManager(FileMgr->getFileSystemOpts(), VFS);
+ Clang->setFileManager(FileMgr.get());
+ }
+ else {
+ Clang->setVirtualFileSystem(VFS);
+ }
+ }
+
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance>
CICleanup(Clang.get());
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits