Index: include/clang/Tooling/Tooling.h =================================================================== --- include/clang/Tooling/Tooling.h +++ include/clang/Tooling/Tooling.h @@ -243,6 +243,7 @@ /// /// \param FilePath The path at which the content will be mapped. /// \param Content A null terminated buffer of the file's content. + // FIXME: remove this when all users have migrated! void mapVirtualFile(StringRef FilePath, StringRef Content); /// \brief Run the clang invocation. @@ -331,6 +332,7 @@ std::vector SourcePaths; std::shared_ptr PCHContainerOps; + llvm::IntrusiveRefCntPtr OverlayFileSystem; llvm::IntrusiveRefCntPtr Files; // Contains a list of pairs (, ). std::vector< std::pair > MappedFileContents; Index: lib/Tooling/Tooling.cpp =================================================================== --- lib/Tooling/Tooling.cpp +++ lib/Tooling/Tooling.cpp @@ -32,13 +32,6 @@ #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" -// For chdir, see the comment in ClangTool::run for more information. -#ifdef LLVM_ON_WIN32 -# include -#else -# include -#endif - #define DEBUG_TYPE "clang-tooling" namespace clang { @@ -131,18 +124,25 @@ SmallString<16> FileNameStorage; StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); + llvm::IntrusiveRefCntPtr OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + OverlayFileSystem->pushOverlay(InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( - new FileManager(FileSystemOptions())); + new FileManager(FileSystemOptions(), OverlayFileSystem)); ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), ToolAction, Files.get(), PCHContainerOps); SmallString<1024> CodeStorage; - Invocation.mapVirtualFile(FileNameRef, - Code.toNullTerminatedStringRef(CodeStorage)); + InMemoryFileSystem->addFile(FileNameRef, 0, + llvm::MemoryBuffer::getMemBuffer( + Code.toNullTerminatedStringRef(CodeStorage))); for (auto &FilenameWithContent : VirtualMappedFiles) { - Invocation.mapVirtualFile(FilenameWithContent.first, - FilenameWithContent.second); + InMemoryFileSystem->addFile( + FilenameWithContent.first, 0, + llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second)); } return Invocation.run(); @@ -250,6 +250,7 @@ } std::unique_ptr Invocation( newInvocation(&Diagnostics, *CC1Args)); + // FIXME: remove this when all users have migrated! for (const auto &It : MappedFileContents) { // Inject the code as the given file name into the preprocessor options. std::unique_ptr Input = @@ -308,7 +309,11 @@ std::shared_ptr PCHContainerOps) : Compilations(Compilations), SourcePaths(SourcePaths), PCHContainerOps(PCHContainerOps), - Files(new FileManager(FileSystemOptions())), DiagConsumer(nullptr) { + OverlayFileSystem(new vfs::OverlayFileSystem(vfs::getRealFileSystem())), + Files(new FileManager(FileSystemOptions(), OverlayFileSystem)), + DiagConsumer(nullptr) { + // Insert a dummy file system that is overwritten later. + OverlayFileSystem->pushOverlay(new vfs::InMemoryFileSystem); appendArgumentsAdjuster(getClangStripOutputAdjuster()); appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); } @@ -369,6 +374,12 @@ continue; } for (CompileCommand &CompileCommand : CompileCommandsForFile) { + // Create a new in-memory file system for virtual files. + // FIXME: It would be nice if we could avoid recreating this every time. + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + *OverlayFileSystem->overlays_begin() = InMemoryFileSystem; + // FIXME: chdir is thread hostile; on the other hand, creating the same // behavior as chdir is complex: chdir resolves the path once, thus // guaranteeing that all subsequent relative path operations work @@ -376,9 +387,18 @@ // difference for example on network filesystems, where symlinks might be // switched during runtime of the tool. Fixing this depends on having a // file system abstraction that allows openat() style interactions. - if (chdir(CompileCommand.Directory.c_str())) + if (OverlayFileSystem->setCurrentWorkingDirectory( + CompileCommand.Directory)) llvm::report_fatal_error("Cannot chdir into \"" + Twine(CompileCommand.Directory) + "\n!"); + + // Now fill the in-memory VFS with the file mappings so it will have the + // correct relative paths. + for (const auto &MappedFile : MappedFileContents) + InMemoryFileSystem->addFile( + MappedFile.first, 0, + llvm::MemoryBuffer::getMemBuffer(MappedFile.second)); + std::vector CommandLine = CompileCommand.CommandLine; if (ArgsAdjuster) CommandLine = ArgsAdjuster(CommandLine); @@ -390,8 +410,7 @@ ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(), PCHContainerOps); Invocation.setDiagnosticConsumer(DiagConsumer); - for (const auto &MappedFile : MappedFileContents) - Invocation.mapVirtualFile(MappedFile.first, MappedFile.second); + if (!Invocation.run()) { // FIXME: Diagnostics should be used instead. llvm::errs() << "Error while processing " << File << ".\n"; @@ -399,7 +418,7 @@ } // Return to the initial directory to correctly resolve next file by // relative path. - if (chdir(InitialDirectory.c_str())) + if (OverlayFileSystem->setCurrentWorkingDirectory(InitialDirectory.c_str())) llvm::report_fatal_error("Cannot chdir into \"" + Twine(InitialDirectory) + "\n!"); } @@ -455,12 +474,20 @@ std::vector> ASTs; ASTBuilderAction Action(ASTs); + llvm::IntrusiveRefCntPtr OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + OverlayFileSystem->pushOverlay(InMemoryFileSystem); + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), OverlayFileSystem)); ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), &Action, - nullptr, PCHContainerOps); + Files.get(), PCHContainerOps); SmallString<1024> CodeStorage; - Invocation.mapVirtualFile(FileNameRef, - Code.toNullTerminatedStringRef(CodeStorage)); + InMemoryFileSystem->addFile(FileNameRef, 0, + llvm::MemoryBuffer::getMemBuffer( + Code.toNullTerminatedStringRef(CodeStorage))); if (!Invocation.run()) return nullptr; Index: unittests/Tooling/ToolingTest.cpp =================================================================== --- unittests/Tooling/ToolingTest.cpp +++ unittests/Tooling/ToolingTest.cpp @@ -149,8 +149,13 @@ } TEST(ToolInvocation, TestMapVirtualFile) { - IntrusiveRefCntPtr Files( - new clang::FileManager(clang::FileSystemOptions())); + llvm::IntrusiveRefCntPtr OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + OverlayFileSystem->pushOverlay(InMemoryFileSystem); + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), OverlayFileSystem)); std::vector Args; Args.push_back("tool-executable"); Args.push_back("-Idef"); @@ -158,8 +163,10 @@ Args.push_back("test.cpp"); clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, Files.get()); - Invocation.mapVirtualFile("test.cpp", "#include \n"); - Invocation.mapVirtualFile("def/abc", "\n"); + InMemoryFileSystem->addFile( + "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include \n")); + InMemoryFileSystem->addFile("def/abc", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); EXPECT_TRUE(Invocation.run()); } @@ -168,8 +175,13 @@ // mapped module.map is found on the include path. In the future, expand this // test to run a full modules enabled compilation, so we make sure we can // rerun modules compilations with a virtual file system. - IntrusiveRefCntPtr Files( - new clang::FileManager(clang::FileSystemOptions())); + llvm::IntrusiveRefCntPtr OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + OverlayFileSystem->pushOverlay(InMemoryFileSystem); + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), OverlayFileSystem)); std::vector Args; Args.push_back("tool-executable"); Args.push_back("-Idef"); @@ -177,11 +189,14 @@ Args.push_back("test.cpp"); clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, Files.get()); - Invocation.mapVirtualFile("test.cpp", "#include \n"); - Invocation.mapVirtualFile("def/abc", "\n"); + InMemoryFileSystem->addFile( + "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include \n")); + InMemoryFileSystem->addFile("def/abc", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); // Add a module.map file in the include directory of our header, so we trigger // the module.map header search logic. - Invocation.mapVirtualFile("def/module.map", "\n"); + InMemoryFileSystem->addFile("def/module.map", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); EXPECT_TRUE(Invocation.run()); }