xref: /llvm-project/clang/unittests/Driver/ToolChainTest.cpp (revision 1f67dc8b7c225290d1b3eb93d90e2c9861aeefc0)
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     return;
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(GetDriverMode, PrefersLastDriverMode) {
371   static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo",
372                                          "--driver-mode=bar", "foo.cpp"};
373   EXPECT_EQ(getDriverMode(Args[0], llvm::makeArrayRef(Args).slice(1)), "bar");
374 }
375 
376 struct SimpleDiagnosticConsumer : public DiagnosticConsumer {
377   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
378                         const Diagnostic &Info) override {
379     if (DiagLevel == DiagnosticsEngine::Level::Error) {
380       Errors.emplace_back();
381       Info.FormatDiagnostic(Errors.back());
382     } else {
383       Msgs.emplace_back();
384       Info.FormatDiagnostic(Msgs.back());
385     }
386   }
387   void clear() override {
388     Msgs.clear();
389     Errors.clear();
390     DiagnosticConsumer::clear();
391   }
392   std::vector<SmallString<32>> Msgs;
393   std::vector<SmallString<32>> Errors;
394 };
395 
396 TEST(ToolChainTest, ConfigFileSearch) {
397   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
398   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
399   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
400   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
401   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
402       new llvm::vfs::InMemoryFileSystem);
403 
404 #ifdef _WIN32
405   const char *TestRoot = "C:\\";
406 #else
407   const char *TestRoot = "/";
408 #endif
409   FS->setCurrentWorkingDirectory(TestRoot);
410 
411   FS->addFile(
412       "/opt/sdk/root.cfg", 0,
413       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform0\n"));
414   FS->addFile(
415       "/home/test/sdk/root.cfg", 0,
416       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform1\n"));
417   FS->addFile(
418       "/home/test/bin/root.cfg", 0,
419       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform2\n"));
420 
421   {
422     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
423                      "clang LLVM compiler", FS);
424     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
425         {"/home/test/bin/clang", "--config", "root.cfg",
426          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
427     ASSERT_TRUE(C);
428     ASSERT_FALSE(C->containsError());
429     EXPECT_EQ("/opt/sdk/platform1", TheDriver.SysRoot);
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="}));
437     ASSERT_TRUE(C);
438     ASSERT_FALSE(C->containsError());
439     EXPECT_EQ("/opt/sdk/platform0", 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=", "--config-user-dir="}));
447     ASSERT_TRUE(C);
448     ASSERT_FALSE(C->containsError());
449     EXPECT_EQ("/opt/sdk/platform2", TheDriver.SysRoot);
450   }
451 }
452 
453 struct FileSystemWithError : public llvm::vfs::FileSystem {
454   llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
455     return std::make_error_code(std::errc::no_such_file_or_directory);
456   }
457   llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
458   openFileForRead(const Twine &Path) override {
459     return std::make_error_code(std::errc::permission_denied);
460   }
461   llvm::vfs::directory_iterator dir_begin(const Twine &Dir,
462                                           std::error_code &EC) override {
463     return llvm::vfs::directory_iterator();
464   }
465   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
466     return std::make_error_code(std::errc::permission_denied);
467   }
468   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
469     return std::make_error_code(std::errc::permission_denied);
470   }
471 };
472 
473 TEST(ToolChainTest, ConfigFileError) {
474   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
475   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
476   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
477       new SimpleDiagnosticConsumer());
478   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
479   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS(new FileSystemWithError);
480 
481   Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
482                    "clang LLVM compiler", FS);
483   std::unique_ptr<Compilation> C(
484       TheDriver.BuildCompilation({"/home/test/bin/clang", "--no-default-config",
485                                   "--config", "./root.cfg", "--version"}));
486   ASSERT_TRUE(C);
487   ASSERT_TRUE(C->containsError());
488   EXPECT_EQ(1U, Diags.getNumErrors());
489   EXPECT_STREQ("configuration file './root.cfg' cannot be opened: cannot get "
490                "absolute path",
491                DiagConsumer->Errors[0].c_str());
492 }
493 
494 TEST(ToolChainTest, BadConfigFile) {
495   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
496   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
497   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
498       new SimpleDiagnosticConsumer());
499   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
500   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
501       new llvm::vfs::InMemoryFileSystem);
502 
503 #ifdef _WIN32
504   const char *TestRoot = "C:\\";
505 #define FILENAME "C:/opt/root.cfg"
506 #define DIRNAME "C:/opt"
507 #else
508   const char *TestRoot = "/";
509 #define FILENAME "/opt/root.cfg"
510 #define DIRNAME "/opt"
511 #endif
512   // UTF-16 string must be aligned on 2-byte boundary. Strings and char arrays
513   // do not provide necessary alignment, so copy constant string into properly
514   // allocated memory in heap.
515   llvm::BumpPtrAllocator Alloc;
516   char *StrBuff = (char *)Alloc.Allocate(16, 4);
517   std::memset(StrBuff, 0, 16);
518   std::memcpy(StrBuff, "\xFF\xFE\x00\xD8\x00\x00", 6);
519   StringRef BadUTF(StrBuff, 6);
520   FS->setCurrentWorkingDirectory(TestRoot);
521   FS->addFile("/opt/root.cfg", 0, llvm::MemoryBuffer::getMemBuffer(BadUTF));
522   FS->addFile("/home/user/test.cfg", 0,
523               llvm::MemoryBuffer::getMemBuffer("@file.rsp"));
524 
525   {
526     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
527                      "clang LLVM compiler", FS);
528     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
529         {"/home/test/bin/clang", "--config", "/opt/root.cfg", "--version"}));
530     ASSERT_TRUE(C);
531     ASSERT_TRUE(C->containsError());
532     EXPECT_EQ(1U, DiagConsumer->Errors.size());
533     EXPECT_STREQ("cannot read configuration file '" FILENAME
534                  "': Could not convert UTF16 to UTF8",
535                  DiagConsumer->Errors[0].c_str());
536   }
537   DiagConsumer->clear();
538   {
539     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
540                      "clang LLVM compiler", FS);
541     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
542         {"/home/test/bin/clang", "--config", "/opt", "--version"}));
543     ASSERT_TRUE(C);
544     ASSERT_TRUE(C->containsError());
545     EXPECT_EQ(1U, DiagConsumer->Errors.size());
546     EXPECT_STREQ("configuration file '" DIRNAME
547                  "' cannot be opened: not a regular file",
548                  DiagConsumer->Errors[0].c_str());
549   }
550   DiagConsumer->clear();
551   {
552     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
553                      "clang LLVM compiler", FS);
554     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
555         {"/home/test/bin/clang", "--config", "root",
556          "--config-system-dir=", "--config-user-dir=", "--version"}));
557     ASSERT_TRUE(C);
558     ASSERT_TRUE(C->containsError());
559     EXPECT_EQ(1U, DiagConsumer->Errors.size());
560     EXPECT_STREQ("configuration file 'root' cannot be found",
561                  DiagConsumer->Errors[0].c_str());
562   }
563 
564 #undef FILENAME
565 #undef DIRNAME
566 }
567 
568 TEST(ToolChainTest, ConfigInexistentInclude) {
569   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
570   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
571   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
572       new SimpleDiagnosticConsumer());
573   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
574   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
575       new llvm::vfs::InMemoryFileSystem);
576 
577 #ifdef _WIN32
578   const char *TestRoot = "C:\\";
579 #define USERCONFIG "C:\\home\\user\\test.cfg"
580 #define UNEXISTENT "C:\\home\\user\\file.rsp"
581 #else
582   const char *TestRoot = "/";
583 #define USERCONFIG "/home/user/test.cfg"
584 #define UNEXISTENT "/home/user/file.rsp"
585 #endif
586   FS->setCurrentWorkingDirectory(TestRoot);
587   FS->addFile("/home/user/test.cfg", 0,
588               llvm::MemoryBuffer::getMemBuffer("@file.rsp"));
589 
590   {
591     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
592                      "clang LLVM compiler", FS);
593     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
594         {"/home/test/bin/clang", "--config", "test.cfg",
595          "--config-system-dir=", "--config-user-dir=/home/user", "--version"}));
596     ASSERT_TRUE(C);
597     ASSERT_TRUE(C->containsError());
598     EXPECT_EQ(1U, DiagConsumer->Errors.size());
599     EXPECT_STRCASEEQ("cannot read configuration file '" USERCONFIG
600                      "': cannot not open file '" UNEXISTENT
601                      "': no such file or directory",
602                      DiagConsumer->Errors[0].c_str());
603   }
604 
605 #undef USERCONFIG
606 #undef UNEXISTENT
607 }
608 
609 TEST(ToolChainTest, ConfigRecursiveInclude) {
610   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
611   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
612   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
613       new SimpleDiagnosticConsumer());
614   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
615   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
616       new llvm::vfs::InMemoryFileSystem);
617 
618 #ifdef _WIN32
619   const char *TestRoot = "C:\\";
620 #define USERCONFIG "C:\\home\\user\\test.cfg"
621 #define INCLUDED1 "C:\\home\\user\\file1.cfg"
622 #else
623   const char *TestRoot = "/";
624 #define USERCONFIG "/home/user/test.cfg"
625 #define INCLUDED1 "/home/user/file1.cfg"
626 #endif
627   FS->setCurrentWorkingDirectory(TestRoot);
628   FS->addFile("/home/user/test.cfg", 0,
629               llvm::MemoryBuffer::getMemBuffer("@file1.cfg"));
630   FS->addFile("/home/user/file1.cfg", 0,
631               llvm::MemoryBuffer::getMemBuffer("@file2.cfg"));
632   FS->addFile("/home/user/file2.cfg", 0,
633               llvm::MemoryBuffer::getMemBuffer("@file3.cfg"));
634   FS->addFile("/home/user/file3.cfg", 0,
635               llvm::MemoryBuffer::getMemBuffer("@file1.cfg"));
636 
637   {
638     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
639                      "clang LLVM compiler", FS);
640     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
641         {"/home/test/bin/clang", "--config", "test.cfg",
642          "--config-system-dir=", "--config-user-dir=/home/user", "--version"}));
643     ASSERT_TRUE(C);
644     ASSERT_TRUE(C->containsError());
645     EXPECT_EQ(1U, DiagConsumer->Errors.size());
646     EXPECT_STREQ("cannot read configuration file '" USERCONFIG
647                  "': recursive expansion of: '" INCLUDED1 "'",
648                  DiagConsumer->Errors[0].c_str());
649   }
650 
651 #undef USERCONFIG
652 #undef INCLUDED1
653 }
654 
655 TEST(ToolChainTest, NestedConfigFile) {
656   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
657   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
658   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
659   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
660   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
661       new llvm::vfs::InMemoryFileSystem);
662 
663 #ifdef _WIN32
664   const char *TestRoot = "C:\\";
665 #else
666   const char *TestRoot = "/";
667 #endif
668   FS->setCurrentWorkingDirectory(TestRoot);
669 
670   FS->addFile("/opt/sdk/root.cfg", 0,
671               llvm::MemoryBuffer::getMemBuffer("--config=platform.cfg\n"));
672   FS->addFile("/opt/sdk/platform.cfg", 0,
673               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-sys\n"));
674   FS->addFile("/home/test/bin/platform.cfg", 0,
675               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-bin\n"));
676 
677   SmallString<128> ClangExecutable("/home/test/bin/clang");
678   FS->makeAbsolute(ClangExecutable);
679 
680   // User file is absent - use system definitions.
681   {
682     Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags,
683                      "clang LLVM compiler", FS);
684     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
685         {"/home/test/bin/clang", "--config", "root.cfg",
686          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
687     ASSERT_TRUE(C);
688     ASSERT_FALSE(C->containsError());
689     EXPECT_EQ("/platform-sys", TheDriver.SysRoot);
690   }
691 
692   // User file overrides system definitions.
693   FS->addFile("/home/test/sdk/platform.cfg", 0,
694               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-user\n"));
695   {
696     Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags,
697                      "clang LLVM compiler", FS);
698     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
699         {"/home/test/bin/clang", "--config", "root.cfg",
700          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
701     ASSERT_TRUE(C);
702     ASSERT_FALSE(C->containsError());
703     EXPECT_EQ("/platform-user", TheDriver.SysRoot);
704   }
705 }
706 
707 } // end anonymous namespace.
708