// Create the compiler instance to use for building the AST.
std::unique_ptr<CompilerInstance> Clang(
new CompilerInstance(std::move(PCHContainerOps)));
+ Clang->setInvocation(CCInvocation);
// Clean up on error, disengage it if the function returns successfully.
auto CleanOnError = llvm::make_scope_exit([&]() {
llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance>
CICleanup(Clang.get());
- Clang->setInvocation(CCInvocation);
OriginalSourceFile =
std::string(Clang->getFrontendOpts().Inputs[0].getFile());
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
assert(Diags.get() && "no DiagnosticsEngine was provided");
+ // If no VFS was provided, create one that tracks the physical file system.
+ // If '-working-directory' was passed as an argument, 'createInvocation' will
+ // set this as the current working directory of the VFS.
+ if (!VFS)
+ VFS = llvm::vfs::createPhysicalFileSystem();
+
SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
std::shared_ptr<CompilerInvocation> CI;
ConfigureDiags(Diags, *AST, CaptureDiagnostics);
AST->Diagnostics = Diags;
AST->FileSystemOpts = CI->getFileSystemOpts();
- if (!VFS)
- VFS = llvm::vfs::getRealFileSystem();
VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS);
AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS);
AST->StorePreamblesInMemory = StorePreamblesInMemory;
ASSERT_NE(ErrUnit->stored_diag_size(), 0U);
}
+TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) {
+ EXPECT_FALSE(
+ llvm::sys::fs::createTemporaryFile("bar", "c", FD, InputFileName));
+ auto Input = std::make_unique<ToolOutputFile>(InputFileName, FD);
+ Input->os() << "";
+
+ SmallString<128> WorkingDir;
+ ASSERT_FALSE(sys::fs::createUniqueDirectory("foo", WorkingDir));
+ const char *Args[] = {"clang", "-working-directory", WorkingDir.c_str(),
+ InputFileName.c_str()};
+
+ auto Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions());
+ auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
+ std::unique_ptr<clang::ASTUnit> ErrUnit;
+
+ auto *AST = ASTUnit::LoadFromCommandLine(
+ &Args[0], &Args[4], PCHContainerOps, Diags, "", false, "", false,
+ CaptureDiagsKind::All, std::nullopt, true, 0, TU_Complete, false, false,
+ false, SkipFunctionBodiesScope::None, false, true, false, false,
+ std::nullopt, &ErrUnit, nullptr);
+
+ ASSERT_NE(AST, nullptr);
+ ASSERT_FALSE(Diags->hasErrorOccurred());
+
+ // Make sure '-working-directory' sets both the FileSystemOpts and underlying
+ // VFS working directory.
+ const auto &FM = AST->getFileManager();
+ const auto &VFS = FM.getVirtualFileSystem();
+ ASSERT_EQ(*VFS.getCurrentWorkingDirectory(), WorkingDir.str());
+ ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir.str());
+}
+
} // anonymous namespace
--- /dev/null
+//====-- unittests/Frontend/ReparseWorkingDirTest.cpp - FrontendAction tests =//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Lex/PreprocessorOptions.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 ReparseWorkingDirTest : public ::testing::Test {
+ IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS;
+ std::shared_ptr<PCHContainerOperations> PCHContainerOpts;
+
+public:
+ void SetUp() override { VFS = new vfs::InMemoryFileSystem(); }
+ void TearDown() override {}
+
+ void setWorkingDirectory(StringRef Path) {
+ VFS->setCurrentWorkingDirectory(Path);
+ }
+
+ void AddFile(const std::string &Filename, const std::string &Contents) {
+ ::time_t now;
+ ::time(&now);
+ VFS->addFile(Filename, now,
+ MemoryBuffer::getMemBufferCopy(Contents, Filename));
+ }
+
+ std::unique_ptr<ASTUnit> ParseAST(StringRef EntryFile) {
+ PCHContainerOpts = std::make_shared<PCHContainerOperations>();
+ auto CI = std::make_shared<CompilerInvocation>();
+ CI->getFrontendOpts().Inputs.push_back(FrontendInputFile(
+ EntryFile, FrontendOptions::getInputKindForExtension(
+ llvm::sys::path::extension(EntryFile).substr(1))));
+
+ CI->getHeaderSearchOpts().AddPath("headers",
+ frontend::IncludeDirGroup::Quoted,
+ /*isFramework*/ false,
+ /*IgnoreSysRoot*/ false);
+
+ CI->getFileSystemOpts().WorkingDir = *VFS->getCurrentWorkingDirectory();
+ CI->getTargetOpts().Triple = "i386-unknown-linux-gnu";
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+ CompilerInstance::createDiagnostics(new DiagnosticOptions,
+ new DiagnosticConsumer));
+
+ FileManager *FileMgr = new FileManager(CI->getFileSystemOpts(), VFS);
+
+ std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
+ CI, PCHContainerOpts, Diags, FileMgr, false, CaptureDiagsKind::None,
+ /*PrecompilePreambleAfterNParses=*/1);
+ return AST;
+ }
+
+ bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) {
+ bool reparseFailed =
+ AST->Reparse(PCHContainerOpts, /*RemappedFiles*/ {}, VFS);
+ return !reparseFailed;
+ }
+};
+
+TEST_F(ReparseWorkingDirTest, ReparseWorkingDir) {
+ // Setup the working directory path.
+ SmallString<16> WorkingDir;
+#ifdef _WIN32
+ WorkingDir = "C:\\";
+#else
+ WorkingDir = "/";
+#endif
+ llvm::sys::path::append(WorkingDir, "root");
+ setWorkingDirectory(WorkingDir);
+
+ SmallString<32> Header;
+ llvm::sys::path::append(Header, WorkingDir, "headers", "header.h");
+
+ SmallString<32> MainName;
+ llvm::sys::path::append(MainName, WorkingDir, "main.cpp");
+
+ AddFile(MainName.str().str(), R"cpp(
+#include "header.h"
+int main() { return foo(); }
+)cpp");
+ AddFile(Header.str().str(), R"h(
+static int foo() { return 0; }
+)h");
+
+ // Parse the main file, ensuring we can include the header.
+ std::unique_ptr<ASTUnit> AST(ParseAST(MainName.str()));
+ ASSERT_TRUE(AST.get());
+ ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+ // Reparse and check that the working directory was preserved.
+ ASSERT_TRUE(ReparseAST(AST));
+
+ const auto &FM = AST->getFileManager();
+ const auto &FS = FM.getVirtualFileSystem();
+ ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir);
+ ASSERT_EQ(*FS.getCurrentWorkingDirectory(), WorkingDir);
+}
+
+} // end anonymous namespace