xref: /llvm-project/clang/unittests/Driver/ToolChainTest.cpp (revision 6d9fcc2ad874e4ee9b94eef4b85ffece18e501b1)
1 //===- unittests/Driver/ToolChainTest.cpp --- ToolChain 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 // Unit tests for ToolChains.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "clang/Driver/ToolChain.h"
14 #include "clang/Basic/DiagnosticIDs.h"
15 #include "clang/Basic/DiagnosticOptions.h"
16 #include "clang/Basic/LLVM.h"
17 #include "clang/Basic/TargetOptions.h"
18 #include "clang/Driver/Compilation.h"
19 #include "clang/Driver/Driver.h"
20 #include "clang/Frontend/CompilerInstance.h"
21 #include "llvm/ADT/ArrayRef.h"
22 #include "llvm/MC/TargetRegistry.h"
23 #include "llvm/Support/TargetSelect.h"
24 #include "llvm/Support/VirtualFileSystem.h"
25 #include "llvm/Support/raw_ostream.h"
26 #include "gtest/gtest.h"
27 #include <memory>
28 
29 #include "SimpleDiagnosticConsumer.h"
30 
31 using namespace clang;
32 using namespace clang::driver;
33 
34 namespace {
35 
36 TEST(ToolChainTest, VFSGCCInstallation) {
37   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
38 
39   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
40   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
41   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
42       new llvm::vfs::InMemoryFileSystem);
43 
44   const char *EmptyFiles[] = {
45       "foo.cpp",
46       "/bin/clang",
47       "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o",
48       "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o",
49       "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o",
50       "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o",
51       "/usr/lib/arm-linux-gnueabi/crt1.o",
52       "/usr/lib/arm-linux-gnueabi/crti.o",
53       "/usr/lib/arm-linux-gnueabi/crtn.o",
54       "/usr/lib/arm-linux-gnueabihf/crt1.o",
55       "/usr/lib/arm-linux-gnueabihf/crti.o",
56       "/usr/lib/arm-linux-gnueabihf/crtn.o",
57       "/usr/include/arm-linux-gnueabi/.keep",
58       "/usr/include/arm-linux-gnueabihf/.keep",
59       "/lib/arm-linux-gnueabi/.keep",
60       "/lib/arm-linux-gnueabihf/.keep",
61 
62       "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtbegin.o",
63       "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtend.o",
64       "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtbegin.o",
65       "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtend.o",
66       "/sysroot/usr/lib/arm-linux-gnueabi/crt1.o",
67       "/sysroot/usr/lib/arm-linux-gnueabi/crti.o",
68       "/sysroot/usr/lib/arm-linux-gnueabi/crtn.o",
69       "/sysroot/usr/lib/arm-linux-gnueabihf/crt1.o",
70       "/sysroot/usr/lib/arm-linux-gnueabihf/crti.o",
71       "/sysroot/usr/lib/arm-linux-gnueabihf/crtn.o",
72       "/sysroot/usr/include/arm-linux-gnueabi/.keep",
73       "/sysroot/usr/include/arm-linux-gnueabihf/.keep",
74       "/sysroot/lib/arm-linux-gnueabi/.keep",
75       "/sysroot/lib/arm-linux-gnueabihf/.keep",
76   };
77 
78   for (const char *Path : EmptyFiles)
79     InMemoryFileSystem->addFile(Path, 0,
80                                 llvm::MemoryBuffer::getMemBuffer("\n"));
81 
82   {
83     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
84     Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags,
85                      "clang LLVM compiler", InMemoryFileSystem);
86     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
87         {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=", "foo.cpp"}));
88     ASSERT_TRUE(C);
89     std::string S;
90     {
91       llvm::raw_string_ostream OS(S);
92       C->getDefaultToolChain().printVerboseInfo(OS);
93     }
94     if (is_style_windows(llvm::sys::path::Style::native))
95       std::replace(S.begin(), S.end(), '\\', '/');
96     EXPECT_EQ(
97         "Found candidate GCC installation: "
98         "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n"
99         "Selected GCC installation: /usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n"
100         "Candidate multilib: .;@m32\n"
101         "Selected multilib: .;@m32\n",
102         S);
103   }
104 
105   {
106     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
107     Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags,
108                      "clang LLVM compiler", InMemoryFileSystem);
109     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
110         {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=/sysroot",
111          "foo.cpp"}));
112     ASSERT_TRUE(C);
113     std::string S;
114     {
115       llvm::raw_string_ostream OS(S);
116       C->getDefaultToolChain().printVerboseInfo(OS);
117     }
118     if (is_style_windows(llvm::sys::path::Style::native))
119       std::replace(S.begin(), S.end(), '\\', '/');
120     // Test that 4.5.3 from --sysroot is not overridden by 4.6.3 (larger
121     // version) from /usr.
122     EXPECT_EQ("Found candidate GCC installation: "
123               "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n"
124               "Selected GCC installation: "
125               "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n"
126               "Candidate multilib: .;@m32\n"
127               "Selected multilib: .;@m32\n",
128               S);
129   }
130 }
131 
132 TEST(ToolChainTest, VFSGCCInstallationRelativeDir) {
133   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
134 
135   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
136   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
137   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
138   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
139       new llvm::vfs::InMemoryFileSystem);
140   Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
141                    "clang LLVM compiler", InMemoryFileSystem);
142 
143   const char *EmptyFiles[] = {
144       "foo.cpp", "/home/test/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o",
145       "/home/test/include/arm-linux-gnueabi/.keep"};
146 
147   for (const char *Path : EmptyFiles)
148     InMemoryFileSystem->addFile(Path, 0,
149                                 llvm::MemoryBuffer::getMemBuffer("\n"));
150 
151   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
152       {"-fsyntax-only", "--gcc-toolchain=", "foo.cpp"}));
153   EXPECT_TRUE(C);
154 
155   std::string S;
156   {
157     llvm::raw_string_ostream OS(S);
158     C->getDefaultToolChain().printVerboseInfo(OS);
159   }
160   if (is_style_windows(llvm::sys::path::Style::native))
161     std::replace(S.begin(), S.end(), '\\', '/');
162   EXPECT_EQ("Found candidate GCC installation: "
163             "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n"
164             "Selected GCC installation: "
165             "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n"
166             "Candidate multilib: .;@m32\n"
167             "Selected multilib: .;@m32\n",
168             S);
169 }
170 
171 TEST(ToolChainTest, DefaultDriverMode) {
172   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
173 
174   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
175   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
176   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
177   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
178       new llvm::vfs::InMemoryFileSystem);
179 
180   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
181                   "clang LLVM compiler", InMemoryFileSystem);
182   CCDriver.setCheckInputsExist(false);
183   Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags,
184                    "clang LLVM compiler", InMemoryFileSystem);
185   CXXDriver.setCheckInputsExist(false);
186   Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags,
187                   "clang LLVM compiler", InMemoryFileSystem);
188   CLDriver.setCheckInputsExist(false);
189 
190   std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation(
191       { "/home/test/bin/clang", "foo.cpp"}));
192   std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation(
193       { "/home/test/bin/clang++", "foo.cpp"}));
194   std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation(
195       { "/home/test/bin/clang-cl", "foo.cpp"}));
196 
197   EXPECT_TRUE(CC);
198   EXPECT_TRUE(CXX);
199   EXPECT_TRUE(CL);
200   EXPECT_TRUE(CCDriver.CCCIsCC());
201   EXPECT_TRUE(CXXDriver.CCCIsCXX());
202   EXPECT_TRUE(CLDriver.IsCLMode());
203 }
204 TEST(ToolChainTest, InvalidArgument) {
205   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
206   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
207   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
208   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
209   Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags);
210   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
211       {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"}));
212   EXPECT_TRUE(C);
213   EXPECT_TRUE(C->containsError());
214 }
215 
216 TEST(ToolChainTest, ParsedClangName) {
217   ParsedClangName Empty;
218   EXPECT_TRUE(Empty.TargetPrefix.empty());
219   EXPECT_TRUE(Empty.ModeSuffix.empty());
220   EXPECT_TRUE(Empty.DriverMode == nullptr);
221   EXPECT_FALSE(Empty.TargetIsValid);
222 
223   ParsedClangName DriverOnly("clang", nullptr);
224   EXPECT_TRUE(DriverOnly.TargetPrefix.empty());
225   EXPECT_TRUE(DriverOnly.ModeSuffix == "clang");
226   EXPECT_TRUE(DriverOnly.DriverMode == nullptr);
227   EXPECT_FALSE(DriverOnly.TargetIsValid);
228 
229   ParsedClangName DriverOnly2("clang++", "--driver-mode=g++");
230   EXPECT_TRUE(DriverOnly2.TargetPrefix.empty());
231   EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++");
232   EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++");
233   EXPECT_FALSE(DriverOnly2.TargetIsValid);
234 
235   ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true);
236   EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386");
237   EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++");
238   EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++");
239   EXPECT_TRUE(TargetAndMode.TargetIsValid);
240 }
241 
242 TEST(ToolChainTest, GetTargetAndMode) {
243   llvm::InitializeAllTargets();
244   std::string IgnoredError;
245   if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError))
246     GTEST_SKIP();
247 
248   ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang");
249   EXPECT_TRUE(Res.TargetPrefix.empty());
250   EXPECT_TRUE(Res.ModeSuffix == "clang");
251   EXPECT_TRUE(Res.DriverMode == nullptr);
252   EXPECT_FALSE(Res.TargetIsValid);
253 
254   Res = ToolChain::getTargetAndModeFromProgramName("clang++");
255   EXPECT_TRUE(Res.TargetPrefix.empty());
256   EXPECT_TRUE(Res.ModeSuffix == "clang++");
257   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
258   EXPECT_FALSE(Res.TargetIsValid);
259 
260   Res = ToolChain::getTargetAndModeFromProgramName("clang++6.0");
261   EXPECT_TRUE(Res.TargetPrefix.empty());
262   EXPECT_TRUE(Res.ModeSuffix == "clang++");
263   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
264   EXPECT_FALSE(Res.TargetIsValid);
265 
266   Res = ToolChain::getTargetAndModeFromProgramName("clang++-release");
267   EXPECT_TRUE(Res.TargetPrefix.empty());
268   EXPECT_TRUE(Res.ModeSuffix == "clang++");
269   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
270   EXPECT_FALSE(Res.TargetIsValid);
271 
272   Res = ToolChain::getTargetAndModeFromProgramName("x86_64-clang++");
273   EXPECT_TRUE(Res.TargetPrefix == "x86_64");
274   EXPECT_TRUE(Res.ModeSuffix == "clang++");
275   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
276   EXPECT_TRUE(Res.TargetIsValid);
277 
278   Res = ToolChain::getTargetAndModeFromProgramName(
279       "x86_64-linux-gnu-clang-c++");
280   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
281   EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
282   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
283   EXPECT_TRUE(Res.TargetIsValid);
284 
285   Res = ToolChain::getTargetAndModeFromProgramName(
286       "x86_64-linux-gnu-clang-c++-tot");
287   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
288   EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
289   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
290   EXPECT_TRUE(Res.TargetIsValid);
291 
292   Res = ToolChain::getTargetAndModeFromProgramName("qqq");
293   EXPECT_TRUE(Res.TargetPrefix.empty());
294   EXPECT_TRUE(Res.ModeSuffix.empty());
295   EXPECT_TRUE(Res.DriverMode == nullptr);
296   EXPECT_FALSE(Res.TargetIsValid);
297 
298   Res = ToolChain::getTargetAndModeFromProgramName("x86_64-qqq");
299   EXPECT_TRUE(Res.TargetPrefix.empty());
300   EXPECT_TRUE(Res.ModeSuffix.empty());
301   EXPECT_TRUE(Res.DriverMode == nullptr);
302   EXPECT_FALSE(Res.TargetIsValid);
303 
304   Res = ToolChain::getTargetAndModeFromProgramName("qqq-clang-cl");
305   EXPECT_TRUE(Res.TargetPrefix == "qqq");
306   EXPECT_TRUE(Res.ModeSuffix == "clang-cl");
307   EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl");
308   EXPECT_FALSE(Res.TargetIsValid);
309 
310   Res = ToolChain::getTargetAndModeFromProgramName("clang-dxc");
311   EXPECT_TRUE(Res.TargetPrefix.empty());
312   EXPECT_TRUE(Res.ModeSuffix == "clang-dxc");
313   EXPECT_STREQ(Res.DriverMode, "--driver-mode=dxc");
314   EXPECT_FALSE(Res.TargetIsValid);
315 }
316 
317 TEST(ToolChainTest, CommandOutput) {
318   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
319 
320   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
321   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
322   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
323   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
324       new llvm::vfs::InMemoryFileSystem);
325 
326   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
327                   "clang LLVM compiler", InMemoryFileSystem);
328   CCDriver.setCheckInputsExist(false);
329   std::unique_ptr<Compilation> CC(
330       CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
331   const JobList &Jobs = CC->getJobs();
332 
333   const auto &CmdCompile = Jobs.getJobs().front();
334   const auto &InFile = CmdCompile->getInputInfos().front().getFilename();
335   EXPECT_STREQ(InFile, "foo.cpp");
336   auto ObjFile = CmdCompile->getOutputFilenames().front();
337   EXPECT_TRUE(StringRef(ObjFile).endswith(".o"));
338 
339   const auto &CmdLink = Jobs.getJobs().back();
340   const auto LinkInFile = CmdLink->getInputInfos().front().getFilename();
341   EXPECT_EQ(ObjFile, LinkInFile);
342   auto ExeFile = CmdLink->getOutputFilenames().front();
343   EXPECT_EQ("a.out", ExeFile);
344 }
345 
346 TEST(ToolChainTest, PostCallback) {
347   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
348   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
349   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
350   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
351   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
352       new llvm::vfs::InMemoryFileSystem);
353 
354   // The executable path must not exist.
355   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
356                   "clang LLVM compiler", InMemoryFileSystem);
357   CCDriver.setCheckInputsExist(false);
358   std::unique_ptr<Compilation> CC(
359       CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
360   bool CallbackHasCalled = false;
361   CC->setPostCallback(
362       [&](const Command &C, int Ret) { CallbackHasCalled = true; });
363   const JobList &Jobs = CC->getJobs();
364   auto &CmdCompile = Jobs.getJobs().front();
365   const Command *FailingCmd = nullptr;
366   CC->ExecuteCommand(*CmdCompile, FailingCmd);
367   EXPECT_TRUE(CallbackHasCalled);
368 }
369 
370 TEST(CompilerInvocation, SplitSwarfSingleCrash) {
371   static constexpr const char *Args[] = {
372       "clang",     "--target=arm-linux-gnueabi",
373       "-gdwarf-4", "-gsplit-dwarf=single",
374       "-c",        "foo.cpp"};
375   CreateInvocationOptions CIOpts;
376   std::unique_ptr<CompilerInvocation> CI = createInvocation(Args, CIOpts);
377   EXPECT_TRUE(CI); // no-crash
378 }
379 
380 TEST(GetDriverMode, PrefersLastDriverMode) {
381   static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo",
382                                          "--driver-mode=bar", "foo.cpp"};
383   EXPECT_EQ(getDriverMode(Args[0], llvm::ArrayRef(Args).slice(1)), "bar");
384 }
385 
386 struct SimpleDiagnosticConsumer : public DiagnosticConsumer {
387   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
388                         const Diagnostic &Info) override {
389     if (DiagLevel == DiagnosticsEngine::Level::Error) {
390       Errors.emplace_back();
391       Info.FormatDiagnostic(Errors.back());
392     } else {
393       Msgs.emplace_back();
394       Info.FormatDiagnostic(Msgs.back());
395     }
396   }
397   void clear() override {
398     Msgs.clear();
399     Errors.clear();
400     DiagnosticConsumer::clear();
401   }
402   std::vector<SmallString<32>> Msgs;
403   std::vector<SmallString<32>> Errors;
404 };
405 
406 TEST(ToolChainTest, ConfigFileSearch) {
407   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
408   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
409   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
410   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
411   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
412       new llvm::vfs::InMemoryFileSystem);
413 
414 #ifdef _WIN32
415   const char *TestRoot = "C:\\";
416 #else
417   const char *TestRoot = "/";
418 #endif
419   FS->setCurrentWorkingDirectory(TestRoot);
420 
421   FS->addFile(
422       "/opt/sdk/root.cfg", 0,
423       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform0\n"));
424   FS->addFile(
425       "/home/test/sdk/root.cfg", 0,
426       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform1\n"));
427   FS->addFile(
428       "/home/test/bin/root.cfg", 0,
429       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform2\n"));
430 
431   {
432     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
433                      "clang LLVM compiler", FS);
434     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
435         {"/home/test/bin/clang", "--config", "root.cfg",
436          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
437     ASSERT_TRUE(C);
438     ASSERT_FALSE(C->containsError());
439     EXPECT_EQ("/opt/sdk/platform1", TheDriver.SysRoot);
440   }
441   {
442     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
443                      "clang LLVM compiler", FS);
444     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
445         {"/home/test/bin/clang", "--config", "root.cfg",
446          "--config-system-dir=/opt/sdk", "--config-user-dir="}));
447     ASSERT_TRUE(C);
448     ASSERT_FALSE(C->containsError());
449     EXPECT_EQ("/opt/sdk/platform0", TheDriver.SysRoot);
450   }
451   {
452     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
453                      "clang LLVM compiler", FS);
454     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
455         {"/home/test/bin/clang", "--config", "root.cfg",
456          "--config-system-dir=", "--config-user-dir="}));
457     ASSERT_TRUE(C);
458     ASSERT_FALSE(C->containsError());
459     EXPECT_EQ("/opt/sdk/platform2", TheDriver.SysRoot);
460   }
461 }
462 
463 struct FileSystemWithError : public llvm::vfs::FileSystem {
464   llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
465     return std::make_error_code(std::errc::no_such_file_or_directory);
466   }
467   llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
468   openFileForRead(const Twine &Path) override {
469     return std::make_error_code(std::errc::permission_denied);
470   }
471   llvm::vfs::directory_iterator dir_begin(const Twine &Dir,
472                                           std::error_code &EC) override {
473     return llvm::vfs::directory_iterator();
474   }
475   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
476     return std::make_error_code(std::errc::permission_denied);
477   }
478   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
479     return std::make_error_code(std::errc::permission_denied);
480   }
481 };
482 
483 TEST(ToolChainTest, ConfigFileError) {
484   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
485   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
486   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
487       new SimpleDiagnosticConsumer());
488   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
489   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS(new FileSystemWithError);
490 
491   Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
492                    "clang LLVM compiler", FS);
493   std::unique_ptr<Compilation> C(
494       TheDriver.BuildCompilation({"/home/test/bin/clang", "--no-default-config",
495                                   "--config", "./root.cfg", "--version"}));
496   ASSERT_TRUE(C);
497   ASSERT_TRUE(C->containsError());
498   EXPECT_EQ(1U, Diags.getNumErrors());
499   EXPECT_STREQ("configuration file './root.cfg' cannot be opened: cannot get "
500                "absolute path",
501                DiagConsumer->Errors[0].c_str());
502 }
503 
504 TEST(ToolChainTest, BadConfigFile) {
505   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
506   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
507   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
508       new SimpleDiagnosticConsumer());
509   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
510   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
511       new llvm::vfs::InMemoryFileSystem);
512 
513 #ifdef _WIN32
514   const char *TestRoot = "C:\\";
515 #define FILENAME "C:/opt/root.cfg"
516 #define DIRNAME "C:/opt"
517 #else
518   const char *TestRoot = "/";
519 #define FILENAME "/opt/root.cfg"
520 #define DIRNAME "/opt"
521 #endif
522   // UTF-16 string must be aligned on 2-byte boundary. Strings and char arrays
523   // do not provide necessary alignment, so copy constant string into properly
524   // allocated memory in heap.
525   llvm::BumpPtrAllocator Alloc;
526   char *StrBuff = (char *)Alloc.Allocate(16, 4);
527   std::memset(StrBuff, 0, 16);
528   std::memcpy(StrBuff, "\xFF\xFE\x00\xD8\x00\x00", 6);
529   StringRef BadUTF(StrBuff, 6);
530   FS->setCurrentWorkingDirectory(TestRoot);
531   FS->addFile("/opt/root.cfg", 0, llvm::MemoryBuffer::getMemBuffer(BadUTF));
532   FS->addFile("/home/user/test.cfg", 0,
533               llvm::MemoryBuffer::getMemBuffer("@file.rsp"));
534 
535   {
536     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
537                      "clang LLVM compiler", FS);
538     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
539         {"/home/test/bin/clang", "--config", "/opt/root.cfg", "--version"}));
540     ASSERT_TRUE(C);
541     ASSERT_TRUE(C->containsError());
542     EXPECT_EQ(1U, DiagConsumer->Errors.size());
543     EXPECT_STREQ("cannot read configuration file '" FILENAME
544                  "': Could not convert UTF16 to UTF8",
545                  DiagConsumer->Errors[0].c_str());
546   }
547   DiagConsumer->clear();
548   {
549     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
550                      "clang LLVM compiler", FS);
551     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
552         {"/home/test/bin/clang", "--config", "/opt", "--version"}));
553     ASSERT_TRUE(C);
554     ASSERT_TRUE(C->containsError());
555     EXPECT_EQ(1U, DiagConsumer->Errors.size());
556     EXPECT_STREQ("configuration file '" DIRNAME
557                  "' cannot be opened: not a regular file",
558                  DiagConsumer->Errors[0].c_str());
559   }
560   DiagConsumer->clear();
561   {
562     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
563                      "clang LLVM compiler", FS);
564     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
565         {"/home/test/bin/clang", "--config", "root",
566          "--config-system-dir=", "--config-user-dir=", "--version"}));
567     ASSERT_TRUE(C);
568     ASSERT_TRUE(C->containsError());
569     EXPECT_EQ(1U, DiagConsumer->Errors.size());
570     EXPECT_STREQ("configuration file 'root' cannot be found",
571                  DiagConsumer->Errors[0].c_str());
572   }
573 
574 #undef FILENAME
575 #undef DIRNAME
576 }
577 
578 TEST(ToolChainTest, ConfigInexistentInclude) {
579   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
580   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
581   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
582       new SimpleDiagnosticConsumer());
583   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
584   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
585       new llvm::vfs::InMemoryFileSystem);
586 
587 #ifdef _WIN32
588   const char *TestRoot = "C:\\";
589 #define USERCONFIG "C:\\home\\user\\test.cfg"
590 #define UNEXISTENT "C:\\home\\user\\file.rsp"
591 #else
592   const char *TestRoot = "/";
593 #define USERCONFIG "/home/user/test.cfg"
594 #define UNEXISTENT "/home/user/file.rsp"
595 #endif
596   FS->setCurrentWorkingDirectory(TestRoot);
597   FS->addFile("/home/user/test.cfg", 0,
598               llvm::MemoryBuffer::getMemBuffer("@file.rsp"));
599 
600   {
601     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
602                      "clang LLVM compiler", FS);
603     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
604         {"/home/test/bin/clang", "--config", "test.cfg",
605          "--config-system-dir=", "--config-user-dir=/home/user", "--version"}));
606     ASSERT_TRUE(C);
607     ASSERT_TRUE(C->containsError());
608     EXPECT_EQ(1U, DiagConsumer->Errors.size());
609     EXPECT_STRCASEEQ("cannot read configuration file '" USERCONFIG
610                      "': cannot not open file '" UNEXISTENT
611                      "': no such file or directory",
612                      DiagConsumer->Errors[0].c_str());
613   }
614 
615 #undef USERCONFIG
616 #undef UNEXISTENT
617 }
618 
619 TEST(ToolChainTest, ConfigRecursiveInclude) {
620   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
621   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
622   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
623       new SimpleDiagnosticConsumer());
624   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
625   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
626       new llvm::vfs::InMemoryFileSystem);
627 
628 #ifdef _WIN32
629   const char *TestRoot = "C:\\";
630 #define USERCONFIG "C:\\home\\user\\test.cfg"
631 #define INCLUDED1 "C:\\home\\user\\file1.cfg"
632 #else
633   const char *TestRoot = "/";
634 #define USERCONFIG "/home/user/test.cfg"
635 #define INCLUDED1 "/home/user/file1.cfg"
636 #endif
637   FS->setCurrentWorkingDirectory(TestRoot);
638   FS->addFile("/home/user/test.cfg", 0,
639               llvm::MemoryBuffer::getMemBuffer("@file1.cfg"));
640   FS->addFile("/home/user/file1.cfg", 0,
641               llvm::MemoryBuffer::getMemBuffer("@file2.cfg"));
642   FS->addFile("/home/user/file2.cfg", 0,
643               llvm::MemoryBuffer::getMemBuffer("@file3.cfg"));
644   FS->addFile("/home/user/file3.cfg", 0,
645               llvm::MemoryBuffer::getMemBuffer("@file1.cfg"));
646 
647   {
648     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
649                      "clang LLVM compiler", FS);
650     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
651         {"/home/test/bin/clang", "--config", "test.cfg",
652          "--config-system-dir=", "--config-user-dir=/home/user", "--version"}));
653     ASSERT_TRUE(C);
654     ASSERT_TRUE(C->containsError());
655     EXPECT_EQ(1U, DiagConsumer->Errors.size());
656     EXPECT_STREQ("cannot read configuration file '" USERCONFIG
657                  "': recursive expansion of: '" INCLUDED1 "'",
658                  DiagConsumer->Errors[0].c_str());
659   }
660 
661 #undef USERCONFIG
662 #undef INCLUDED1
663 }
664 
665 TEST(ToolChainTest, NestedConfigFile) {
666   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
667   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
668   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
669   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
670   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
671       new llvm::vfs::InMemoryFileSystem);
672 
673 #ifdef _WIN32
674   const char *TestRoot = "C:\\";
675 #else
676   const char *TestRoot = "/";
677 #endif
678   FS->setCurrentWorkingDirectory(TestRoot);
679 
680   FS->addFile("/opt/sdk/root.cfg", 0,
681               llvm::MemoryBuffer::getMemBuffer("--config=platform.cfg\n"));
682   FS->addFile("/opt/sdk/platform.cfg", 0,
683               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-sys\n"));
684   FS->addFile("/home/test/bin/platform.cfg", 0,
685               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-bin\n"));
686 
687   SmallString<128> ClangExecutable("/home/test/bin/clang");
688   FS->makeAbsolute(ClangExecutable);
689 
690   // User file is absent - use system definitions.
691   {
692     Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags,
693                      "clang LLVM compiler", FS);
694     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
695         {"/home/test/bin/clang", "--config", "root.cfg",
696          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
697     ASSERT_TRUE(C);
698     ASSERT_FALSE(C->containsError());
699     EXPECT_EQ("/platform-sys", TheDriver.SysRoot);
700   }
701 
702   // User file overrides system definitions.
703   FS->addFile("/home/test/sdk/platform.cfg", 0,
704               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-user\n"));
705   {
706     Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags,
707                      "clang LLVM compiler", FS);
708     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
709         {"/home/test/bin/clang", "--config", "root.cfg",
710          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
711     ASSERT_TRUE(C);
712     ASSERT_FALSE(C->containsError());
713     EXPECT_EQ("/platform-user", TheDriver.SysRoot);
714   }
715 }
716 
717 } // end anonymous namespace.
718