Skip to content

Commit 08e19ff

Browse files
Merge pull request #7029 from hamishknight/time-and-place-5.9
[5.9] [clang] Fix ASTUnit working directory handling
2 parents 6fecd7a + 63fd70a commit 08e19ff

File tree

4 files changed

+156
-3
lines changed

4 files changed

+156
-3
lines changed

clang/lib/Frontend/ASTUnit.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,7 @@ bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
11451145
// Create the compiler instance to use for building the AST.
11461146
std::unique_ptr<CompilerInstance> Clang(
11471147
new CompilerInstance(std::move(PCHContainerOps)));
1148+
Clang->setInvocation(CCInvocation);
11481149

11491150
// Clean up on error, disengage it if the function returns successfully.
11501151
auto CleanOnError = llvm::make_scope_exit([&]() {
@@ -1171,7 +1172,6 @@ bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
11711172
llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance>
11721173
CICleanup(Clang.get());
11731174

1174-
Clang->setInvocation(CCInvocation);
11751175
OriginalSourceFile =
11761176
std::string(Clang->getFrontendOpts().Inputs[0].getFile());
11771177

@@ -1752,6 +1752,12 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
17521752
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
17531753
assert(Diags.get() && "no DiagnosticsEngine was provided");
17541754

1755+
// If no VFS was provided, create one that tracks the physical file system.
1756+
// If '-working-directory' was passed as an argument, 'createInvocation' will
1757+
// set this as the current working directory of the VFS.
1758+
if (!VFS)
1759+
VFS = llvm::vfs::createPhysicalFileSystem();
1760+
17551761
SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
17561762

17571763
std::shared_ptr<CompilerInvocation> CI;
@@ -1801,8 +1807,6 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
18011807
ConfigureDiags(Diags, *AST, CaptureDiagnostics);
18021808
AST->Diagnostics = Diags;
18031809
AST->FileSystemOpts = CI->getFileSystemOpts();
1804-
if (!VFS)
1805-
VFS = llvm::vfs::getRealFileSystem();
18061810
VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS);
18071811
AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS);
18081812
AST->ModuleCache = new InMemoryModuleCache;

clang/unittests/Frontend/ASTUnitTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,36 @@ TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) {
178178
ASSERT_NE(ErrUnit->stored_diag_size(), 0U);
179179
}
180180

181+
TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) {
182+
EXPECT_FALSE(
183+
llvm::sys::fs::createTemporaryFile("bar", "c", FD, InputFileName));
184+
auto Input = std::make_unique<ToolOutputFile>(InputFileName, FD);
185+
Input->os() << "";
186+
187+
SmallString<128> WorkingDir;
188+
ASSERT_FALSE(sys::fs::createUniqueDirectory("foo", WorkingDir));
189+
const char *Args[] = {"clang", "-working-directory", WorkingDir.c_str(),
190+
InputFileName.c_str()};
191+
192+
auto Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions());
193+
auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
194+
std::unique_ptr<clang::ASTUnit> ErrUnit;
195+
196+
auto *AST = ASTUnit::LoadFromCommandLine(
197+
&Args[0], &Args[4], PCHContainerOps, Diags, "", false,
198+
CaptureDiagsKind::All, None, true, 0, TU_Complete, false, false, false,
199+
SkipFunctionBodiesScope::None, false, true, false, false, None, &ErrUnit,
200+
nullptr);
201+
202+
ASSERT_NE(AST, nullptr);
203+
ASSERT_FALSE(Diags->hasErrorOccurred());
204+
205+
// Make sure '-working-directory' sets both the FileSystemOpts and underlying
206+
// VFS working directory.
207+
const auto &FM = AST->getFileManager();
208+
const auto &VFS = FM.getVirtualFileSystem();
209+
ASSERT_EQ(*VFS.getCurrentWorkingDirectory(), WorkingDir.str());
210+
ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir.str());
211+
}
212+
181213
} // anonymous namespace

clang/unittests/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_clang_unittest(FrontendTests
1313
CodeGenActionTest.cpp
1414
ParsedSourceLocationTest.cpp
1515
PCHPreambleTest.cpp
16+
ReparseWorkingDirTest.cpp
1617
OutputStreamTest.cpp
1718
TextDiagnosticTest.cpp
1819
UtilsTest.cpp
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//====-- unittests/Frontend/ReparseWorkingDirTest.cpp - FrontendAction tests =//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/Basic/Diagnostic.h"
10+
#include "clang/Basic/FileManager.h"
11+
#include "clang/Frontend/ASTUnit.h"
12+
#include "clang/Frontend/CompilerInstance.h"
13+
#include "clang/Frontend/CompilerInvocation.h"
14+
#include "clang/Frontend/FrontendActions.h"
15+
#include "clang/Frontend/FrontendOptions.h"
16+
#include "clang/Lex/PreprocessorOptions.h"
17+
#include "llvm/Support/FileSystem.h"
18+
#include "llvm/Support/MemoryBuffer.h"
19+
#include "llvm/Support/Path.h"
20+
#include "gtest/gtest.h"
21+
22+
using namespace llvm;
23+
using namespace clang;
24+
25+
namespace {
26+
class ReparseWorkingDirTest : public ::testing::Test {
27+
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS;
28+
std::shared_ptr<PCHContainerOperations> PCHContainerOpts;
29+
30+
public:
31+
void SetUp() override { VFS = new vfs::InMemoryFileSystem(); }
32+
void TearDown() override {}
33+
34+
void setWorkingDirectory(StringRef Path) {
35+
VFS->setCurrentWorkingDirectory(Path);
36+
}
37+
38+
void AddFile(const std::string &Filename, const std::string &Contents) {
39+
::time_t now;
40+
::time(&now);
41+
VFS->addFile(Filename, now,
42+
MemoryBuffer::getMemBufferCopy(Contents, Filename));
43+
}
44+
45+
std::unique_ptr<ASTUnit> ParseAST(StringRef EntryFile) {
46+
PCHContainerOpts = std::make_shared<PCHContainerOperations>();
47+
auto CI = std::make_shared<CompilerInvocation>();
48+
CI->getFrontendOpts().Inputs.push_back(FrontendInputFile(
49+
EntryFile, FrontendOptions::getInputKindForExtension(
50+
llvm::sys::path::extension(EntryFile).substr(1))));
51+
52+
CI->getHeaderSearchOpts().AddPath("headers",
53+
frontend::IncludeDirGroup::Quoted,
54+
/*isFramework*/ false,
55+
/*IgnoreSysRoot*/ false);
56+
57+
CI->getFileSystemOpts().WorkingDir = *VFS->getCurrentWorkingDirectory();
58+
CI->getTargetOpts().Triple = "i386-unknown-linux-gnu";
59+
60+
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
61+
CompilerInstance::createDiagnostics(new DiagnosticOptions,
62+
new DiagnosticConsumer));
63+
64+
FileManager *FileMgr = new FileManager(CI->getFileSystemOpts(), VFS);
65+
66+
std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
67+
CI, PCHContainerOpts, Diags, FileMgr, false, CaptureDiagsKind::None,
68+
/*PrecompilePreambleAfterNParses=*/1);
69+
return AST;
70+
}
71+
72+
bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) {
73+
bool reparseFailed =
74+
AST->Reparse(PCHContainerOpts, /*RemappedFiles*/ {}, VFS);
75+
return !reparseFailed;
76+
}
77+
};
78+
79+
TEST_F(ReparseWorkingDirTest, ReparseWorkingDir) {
80+
// Setup the working directory path. We use '//root/' to allow the path to be
81+
// valid on both Windows and Unix. We need the trailing slash for the path
82+
// to be treated as absolute.
83+
SmallString<16> WorkingDir;
84+
llvm::sys::path::append(WorkingDir, "//root",
85+
llvm::sys::path::get_separator());
86+
setWorkingDirectory(WorkingDir);
87+
88+
SmallString<32> Header;
89+
llvm::sys::path::append(Header, WorkingDir, "headers", "header.h");
90+
91+
SmallString<32> MainName;
92+
llvm::sys::path::append(MainName, WorkingDir, "main.cpp");
93+
94+
AddFile(MainName.str().str(), R"cpp(
95+
#include "header.h"
96+
int main() { return foo(); }
97+
)cpp");
98+
AddFile(Header.str().str(), R"h(
99+
static int foo() { return 0; }
100+
)h");
101+
102+
// Parse the main file, ensuring we can include the header.
103+
std::unique_ptr<ASTUnit> AST(ParseAST(MainName.str()));
104+
ASSERT_TRUE(AST.get());
105+
ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
106+
107+
// Reparse and check that the working directory was preserved.
108+
ASSERT_TRUE(ReparseAST(AST));
109+
110+
const auto &FM = AST->getFileManager();
111+
const auto &FS = FM.getVirtualFileSystem();
112+
ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir);
113+
ASSERT_EQ(*FS.getCurrentWorkingDirectory(), WorkingDir);
114+
}
115+
116+
} // end anonymous namespace

0 commit comments

Comments
 (0)