xref: /llvm-project/clang/unittests/Driver/ToolChainTest.cpp (revision 18252e6c8e2b1bb9901b5cca9e9a75edfeda2c4e)
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, VFSSolarisMultiGCCInstallation) {
172   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
173 
174   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
175   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
176   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
177       new llvm::vfs::InMemoryFileSystem);
178 
179   const char *EmptyFiles[] = {
180       // Sort entries so the latest version doesn't come first.
181       "/usr/gcc/7/lib/gcc/sparcv9-sun-solaris2.11/7.5.0/32/crtbegin.o",
182       "/usr/gcc/7/lib/gcc/sparcv9-sun-solaris2.11/7.5.0/crtbegin.o",
183       "/usr/gcc/7/lib/gcc/x86_64-pc-solaris2.11/7.5.0/32/crtbegin.o",
184       "/usr/gcc/7/lib/gcc/x86_64-pc-solaris2.11/7.5.0/crtbegin.o",
185       "/usr/gcc/11/lib/gcc/sparcv9-sun-solaris2.11/11.4.0/crtbegin.o",
186       "/usr/gcc/11/lib/gcc/sparcv9-sun-solaris2.11/11.4.0/sparcv8plus/crtbegin.o",
187       "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0/32/crtbegin.o",
188       "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0/crtbegin.o",
189       "/usr/gcc/4.7/lib/gcc/i386-pc-solaris2.11/4.7.3/amd64/crtbegin.o",
190       "/usr/gcc/4.7/lib/gcc/i386-pc-solaris2.11/4.7.3/crtbegin.o",
191       "/usr/gcc/4.7/lib/gcc/sparc-sun-solaris2.11/4.7.3/crtbegin.o",
192       "/usr/gcc/4.7/lib/gcc/sparc-sun-solaris2.11/4.7.3/sparcv9/crtbegin.o",
193   };
194 
195   for (const char *Path : EmptyFiles)
196     InMemoryFileSystem->addFile(Path, 0,
197                                 llvm::MemoryBuffer::getMemBuffer("\n"));
198 
199   {
200     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
201     Driver TheDriver("/bin/clang", "i386-pc-solaris2.11", Diags,
202                      "clang LLVM compiler", InMemoryFileSystem);
203     std::unique_ptr<Compilation> C(
204         TheDriver.BuildCompilation({"-v", "--gcc-toolchain=", "--sysroot="}));
205     ASSERT_TRUE(C);
206     std::string S;
207     {
208       llvm::raw_string_ostream OS(S);
209       C->getDefaultToolChain().printVerboseInfo(OS);
210     }
211     if (is_style_windows(llvm::sys::path::Style::native))
212       std::replace(S.begin(), S.end(), '\\', '/');
213     EXPECT_EQ("Found candidate GCC installation: "
214               "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0\n"
215               "Selected GCC installation: "
216               "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0\n"
217               "Candidate multilib: .;@m64\n"
218               "Candidate multilib: 32;@m32\n"
219               "Selected multilib: 32;@m32\n",
220               S);
221   }
222 
223   {
224     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
225     Driver TheDriver("/bin/clang", "amd64-pc-solaris2.11", Diags,
226                      "clang LLVM compiler", InMemoryFileSystem);
227     std::unique_ptr<Compilation> C(
228         TheDriver.BuildCompilation({"-v", "--gcc-toolchain=", "--sysroot="}));
229     ASSERT_TRUE(C);
230     std::string S;
231     {
232       llvm::raw_string_ostream OS(S);
233       C->getDefaultToolChain().printVerboseInfo(OS);
234     }
235     if (is_style_windows(llvm::sys::path::Style::native))
236       std::replace(S.begin(), S.end(), '\\', '/');
237     EXPECT_EQ("Found candidate GCC installation: "
238               "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0\n"
239               "Selected GCC installation: "
240               "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0\n"
241               "Candidate multilib: .;@m64\n"
242               "Candidate multilib: 32;@m32\n"
243               "Selected multilib: .;@m64\n",
244               S);
245   }
246 
247   {
248     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
249     Driver TheDriver("/bin/clang", "x86_64-pc-solaris2.11", Diags,
250                      "clang LLVM compiler", InMemoryFileSystem);
251     std::unique_ptr<Compilation> C(
252         TheDriver.BuildCompilation({"-v", "--gcc-toolchain=", "--sysroot="}));
253     ASSERT_TRUE(C);
254     std::string S;
255     {
256       llvm::raw_string_ostream OS(S);
257       C->getDefaultToolChain().printVerboseInfo(OS);
258     }
259     if (is_style_windows(llvm::sys::path::Style::native))
260       std::replace(S.begin(), S.end(), '\\', '/');
261     EXPECT_EQ("Found candidate GCC installation: "
262               "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0\n"
263               "Selected GCC installation: "
264               "/usr/gcc/11/lib/gcc/x86_64-pc-solaris2.11/11.4.0\n"
265               "Candidate multilib: .;@m64\n"
266               "Candidate multilib: 32;@m32\n"
267               "Selected multilib: .;@m64\n",
268               S);
269   }
270 
271   {
272     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
273     Driver TheDriver("/bin/clang", "sparc-sun-solaris2.11", Diags,
274                      "clang LLVM compiler", InMemoryFileSystem);
275     std::unique_ptr<Compilation> C(
276         TheDriver.BuildCompilation({"-v", "--gcc-toolchain=", "--sysroot="}));
277     ASSERT_TRUE(C);
278     std::string S;
279     {
280       llvm::raw_string_ostream OS(S);
281       C->getDefaultToolChain().printVerboseInfo(OS);
282     }
283     if (is_style_windows(llvm::sys::path::Style::native))
284       std::replace(S.begin(), S.end(), '\\', '/');
285     EXPECT_EQ("Found candidate GCC installation: "
286               "/usr/gcc/11/lib/gcc/sparcv9-sun-solaris2.11/11.4.0\n"
287               "Selected GCC installation: "
288               "/usr/gcc/11/lib/gcc/sparcv9-sun-solaris2.11/11.4.0\n"
289               "Candidate multilib: .;@m64\n"
290               "Candidate multilib: sparcv8plus;@m32\n"
291               "Selected multilib: sparcv8plus;@m32\n",
292               S);
293   }
294   {
295     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
296     Driver TheDriver("/bin/clang", "sparcv9-sun-solaris2.11", Diags,
297                      "clang LLVM compiler", InMemoryFileSystem);
298     std::unique_ptr<Compilation> C(
299         TheDriver.BuildCompilation({"-v", "--gcc-toolchain=", "--sysroot="}));
300     ASSERT_TRUE(C);
301     std::string S;
302     {
303       llvm::raw_string_ostream OS(S);
304       C->getDefaultToolChain().printVerboseInfo(OS);
305     }
306     if (is_style_windows(llvm::sys::path::Style::native))
307       std::replace(S.begin(), S.end(), '\\', '/');
308     EXPECT_EQ("Found candidate GCC installation: "
309               "/usr/gcc/11/lib/gcc/sparcv9-sun-solaris2.11/11.4.0\n"
310               "Selected GCC installation: "
311               "/usr/gcc/11/lib/gcc/sparcv9-sun-solaris2.11/11.4.0\n"
312               "Candidate multilib: .;@m64\n"
313               "Candidate multilib: sparcv8plus;@m32\n"
314               "Selected multilib: .;@m64\n",
315               S);
316   }
317 }
318 
319 TEST(ToolChainTest, DefaultDriverMode) {
320   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
321 
322   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
323   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
324   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
325   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
326       new llvm::vfs::InMemoryFileSystem);
327 
328   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
329                   "clang LLVM compiler", InMemoryFileSystem);
330   CCDriver.setCheckInputsExist(false);
331   Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags,
332                    "clang LLVM compiler", InMemoryFileSystem);
333   CXXDriver.setCheckInputsExist(false);
334   Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags,
335                   "clang LLVM compiler", InMemoryFileSystem);
336   CLDriver.setCheckInputsExist(false);
337 
338   std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation(
339       { "/home/test/bin/clang", "foo.cpp"}));
340   std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation(
341       { "/home/test/bin/clang++", "foo.cpp"}));
342   std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation(
343       { "/home/test/bin/clang-cl", "foo.cpp"}));
344 
345   EXPECT_TRUE(CC);
346   EXPECT_TRUE(CXX);
347   EXPECT_TRUE(CL);
348   EXPECT_TRUE(CCDriver.CCCIsCC());
349   EXPECT_TRUE(CXXDriver.CCCIsCXX());
350   EXPECT_TRUE(CLDriver.IsCLMode());
351 }
352 TEST(ToolChainTest, InvalidArgument) {
353   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
354   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
355   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
356   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
357   Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags);
358   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
359       {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"}));
360   EXPECT_TRUE(C);
361   EXPECT_TRUE(C->containsError());
362 }
363 
364 TEST(ToolChainTest, ParsedClangName) {
365   ParsedClangName Empty;
366   EXPECT_TRUE(Empty.TargetPrefix.empty());
367   EXPECT_TRUE(Empty.ModeSuffix.empty());
368   EXPECT_TRUE(Empty.DriverMode == nullptr);
369   EXPECT_FALSE(Empty.TargetIsValid);
370 
371   ParsedClangName DriverOnly("clang", nullptr);
372   EXPECT_TRUE(DriverOnly.TargetPrefix.empty());
373   EXPECT_TRUE(DriverOnly.ModeSuffix == "clang");
374   EXPECT_TRUE(DriverOnly.DriverMode == nullptr);
375   EXPECT_FALSE(DriverOnly.TargetIsValid);
376 
377   ParsedClangName DriverOnly2("clang++", "--driver-mode=g++");
378   EXPECT_TRUE(DriverOnly2.TargetPrefix.empty());
379   EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++");
380   EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++");
381   EXPECT_FALSE(DriverOnly2.TargetIsValid);
382 
383   ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true);
384   EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386");
385   EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++");
386   EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++");
387   EXPECT_TRUE(TargetAndMode.TargetIsValid);
388 }
389 
390 TEST(ToolChainTest, GetTargetAndMode) {
391   llvm::InitializeAllTargets();
392   std::string IgnoredError;
393   if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError))
394     GTEST_SKIP();
395 
396   ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang");
397   EXPECT_TRUE(Res.TargetPrefix.empty());
398   EXPECT_TRUE(Res.ModeSuffix == "clang");
399   EXPECT_TRUE(Res.DriverMode == nullptr);
400   EXPECT_FALSE(Res.TargetIsValid);
401 
402   Res = ToolChain::getTargetAndModeFromProgramName("clang++");
403   EXPECT_TRUE(Res.TargetPrefix.empty());
404   EXPECT_TRUE(Res.ModeSuffix == "clang++");
405   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
406   EXPECT_FALSE(Res.TargetIsValid);
407 
408   Res = ToolChain::getTargetAndModeFromProgramName("clang++6.0");
409   EXPECT_TRUE(Res.TargetPrefix.empty());
410   EXPECT_TRUE(Res.ModeSuffix == "clang++");
411   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
412   EXPECT_FALSE(Res.TargetIsValid);
413 
414   Res = ToolChain::getTargetAndModeFromProgramName("clang++-release");
415   EXPECT_TRUE(Res.TargetPrefix.empty());
416   EXPECT_TRUE(Res.ModeSuffix == "clang++");
417   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
418   EXPECT_FALSE(Res.TargetIsValid);
419 
420   Res = ToolChain::getTargetAndModeFromProgramName("x86_64-clang++");
421   EXPECT_TRUE(Res.TargetPrefix == "x86_64");
422   EXPECT_TRUE(Res.ModeSuffix == "clang++");
423   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
424   EXPECT_TRUE(Res.TargetIsValid);
425 
426   Res = ToolChain::getTargetAndModeFromProgramName(
427       "x86_64-linux-gnu-clang-c++");
428   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
429   EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
430   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
431   EXPECT_TRUE(Res.TargetIsValid);
432 
433   Res = ToolChain::getTargetAndModeFromProgramName(
434       "x86_64-linux-gnu-clang-c++-tot");
435   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
436   EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
437   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
438   EXPECT_TRUE(Res.TargetIsValid);
439 
440   Res = ToolChain::getTargetAndModeFromProgramName("qqq");
441   EXPECT_TRUE(Res.TargetPrefix.empty());
442   EXPECT_TRUE(Res.ModeSuffix.empty());
443   EXPECT_TRUE(Res.DriverMode == nullptr);
444   EXPECT_FALSE(Res.TargetIsValid);
445 
446   Res = ToolChain::getTargetAndModeFromProgramName("x86_64-qqq");
447   EXPECT_TRUE(Res.TargetPrefix.empty());
448   EXPECT_TRUE(Res.ModeSuffix.empty());
449   EXPECT_TRUE(Res.DriverMode == nullptr);
450   EXPECT_FALSE(Res.TargetIsValid);
451 
452   Res = ToolChain::getTargetAndModeFromProgramName("qqq-clang-cl");
453   EXPECT_TRUE(Res.TargetPrefix == "qqq");
454   EXPECT_TRUE(Res.ModeSuffix == "clang-cl");
455   EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl");
456   EXPECT_FALSE(Res.TargetIsValid);
457 
458   Res = ToolChain::getTargetAndModeFromProgramName("clang-dxc");
459   EXPECT_TRUE(Res.TargetPrefix.empty());
460   EXPECT_TRUE(Res.ModeSuffix == "clang-dxc");
461   EXPECT_STREQ(Res.DriverMode, "--driver-mode=dxc");
462   EXPECT_FALSE(Res.TargetIsValid);
463 }
464 
465 TEST(ToolChainTest, CommandOutput) {
466   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
467 
468   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
469   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
470   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
471   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
472       new llvm::vfs::InMemoryFileSystem);
473 
474   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
475                   "clang LLVM compiler", InMemoryFileSystem);
476   CCDriver.setCheckInputsExist(false);
477   std::unique_ptr<Compilation> CC(
478       CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
479   const JobList &Jobs = CC->getJobs();
480 
481   const auto &CmdCompile = Jobs.getJobs().front();
482   const auto &InFile = CmdCompile->getInputInfos().front().getFilename();
483   EXPECT_STREQ(InFile, "foo.cpp");
484   auto ObjFile = CmdCompile->getOutputFilenames().front();
485   EXPECT_TRUE(StringRef(ObjFile).endswith(".o"));
486 
487   const auto &CmdLink = Jobs.getJobs().back();
488   const auto LinkInFile = CmdLink->getInputInfos().front().getFilename();
489   EXPECT_EQ(ObjFile, LinkInFile);
490   auto ExeFile = CmdLink->getOutputFilenames().front();
491   EXPECT_EQ("a.out", ExeFile);
492 }
493 
494 TEST(ToolChainTest, PostCallback) {
495   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
496   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
497   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
498   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
499   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
500       new llvm::vfs::InMemoryFileSystem);
501 
502   // The executable path must not exist.
503   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
504                   "clang LLVM compiler", InMemoryFileSystem);
505   CCDriver.setCheckInputsExist(false);
506   std::unique_ptr<Compilation> CC(
507       CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
508   bool CallbackHasCalled = false;
509   CC->setPostCallback(
510       [&](const Command &C, int Ret) { CallbackHasCalled = true; });
511   const JobList &Jobs = CC->getJobs();
512   auto &CmdCompile = Jobs.getJobs().front();
513   const Command *FailingCmd = nullptr;
514   CC->ExecuteCommand(*CmdCompile, FailingCmd);
515   EXPECT_TRUE(CallbackHasCalled);
516 }
517 
518 TEST(CompilerInvocation, SplitSwarfSingleCrash) {
519   static constexpr const char *Args[] = {
520       "clang",     "--target=arm-linux-gnueabi",
521       "-gdwarf-4", "-gsplit-dwarf=single",
522       "-c",        "foo.cpp"};
523   CreateInvocationOptions CIOpts;
524   std::unique_ptr<CompilerInvocation> CI = createInvocation(Args, CIOpts);
525   EXPECT_TRUE(CI); // no-crash
526 }
527 
528 TEST(GetDriverMode, PrefersLastDriverMode) {
529   static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo",
530                                          "--driver-mode=bar", "foo.cpp"};
531   EXPECT_EQ(getDriverMode(Args[0], llvm::ArrayRef(Args).slice(1)), "bar");
532 }
533 
534 struct SimpleDiagnosticConsumer : public DiagnosticConsumer {
535   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
536                         const Diagnostic &Info) override {
537     if (DiagLevel == DiagnosticsEngine::Level::Error) {
538       Errors.emplace_back();
539       Info.FormatDiagnostic(Errors.back());
540     } else {
541       Msgs.emplace_back();
542       Info.FormatDiagnostic(Msgs.back());
543     }
544   }
545   void clear() override {
546     Msgs.clear();
547     Errors.clear();
548     DiagnosticConsumer::clear();
549   }
550   std::vector<SmallString<32>> Msgs;
551   std::vector<SmallString<32>> Errors;
552 };
553 
554 TEST(ToolChainTest, ConfigFileSearch) {
555   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
556   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
557   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
558   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
559   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
560       new llvm::vfs::InMemoryFileSystem);
561 
562 #ifdef _WIN32
563   const char *TestRoot = "C:\\";
564 #else
565   const char *TestRoot = "/";
566 #endif
567   FS->setCurrentWorkingDirectory(TestRoot);
568 
569   FS->addFile(
570       "/opt/sdk/root.cfg", 0,
571       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform0\n"));
572   FS->addFile(
573       "/home/test/sdk/root.cfg", 0,
574       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform1\n"));
575   FS->addFile(
576       "/home/test/bin/root.cfg", 0,
577       llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform2\n"));
578 
579   {
580     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
581                      "clang LLVM compiler", FS);
582     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
583         {"/home/test/bin/clang", "--config", "root.cfg",
584          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
585     ASSERT_TRUE(C);
586     ASSERT_FALSE(C->containsError());
587     EXPECT_EQ("/opt/sdk/platform1", TheDriver.SysRoot);
588   }
589   {
590     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
591                      "clang LLVM compiler", FS);
592     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
593         {"/home/test/bin/clang", "--config", "root.cfg",
594          "--config-system-dir=/opt/sdk", "--config-user-dir="}));
595     ASSERT_TRUE(C);
596     ASSERT_FALSE(C->containsError());
597     EXPECT_EQ("/opt/sdk/platform0", TheDriver.SysRoot);
598   }
599   {
600     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
601                      "clang LLVM compiler", FS);
602     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
603         {"/home/test/bin/clang", "--config", "root.cfg",
604          "--config-system-dir=", "--config-user-dir="}));
605     ASSERT_TRUE(C);
606     ASSERT_FALSE(C->containsError());
607     EXPECT_EQ("/opt/sdk/platform2", TheDriver.SysRoot);
608   }
609 }
610 
611 struct FileSystemWithError : public llvm::vfs::FileSystem {
612   llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
613     return std::make_error_code(std::errc::no_such_file_or_directory);
614   }
615   llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
616   openFileForRead(const Twine &Path) override {
617     return std::make_error_code(std::errc::permission_denied);
618   }
619   llvm::vfs::directory_iterator dir_begin(const Twine &Dir,
620                                           std::error_code &EC) override {
621     return llvm::vfs::directory_iterator();
622   }
623   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
624     return std::make_error_code(std::errc::permission_denied);
625   }
626   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
627     return std::make_error_code(std::errc::permission_denied);
628   }
629 };
630 
631 TEST(ToolChainTest, ConfigFileError) {
632   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
633   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
634   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
635       new SimpleDiagnosticConsumer());
636   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
637   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS(new FileSystemWithError);
638 
639   Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
640                    "clang LLVM compiler", FS);
641   std::unique_ptr<Compilation> C(
642       TheDriver.BuildCompilation({"/home/test/bin/clang", "--no-default-config",
643                                   "--config", "./root.cfg", "--version"}));
644   ASSERT_TRUE(C);
645   ASSERT_TRUE(C->containsError());
646   EXPECT_EQ(1U, Diags.getNumErrors());
647   EXPECT_STREQ("configuration file './root.cfg' cannot be opened: cannot get "
648                "absolute path",
649                DiagConsumer->Errors[0].c_str());
650 }
651 
652 TEST(ToolChainTest, BadConfigFile) {
653   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
654   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
655   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
656       new SimpleDiagnosticConsumer());
657   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
658   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
659       new llvm::vfs::InMemoryFileSystem);
660 
661 #ifdef _WIN32
662   const char *TestRoot = "C:\\";
663 #define FILENAME "C:/opt/root.cfg"
664 #define DIRNAME "C:/opt"
665 #else
666   const char *TestRoot = "/";
667 #define FILENAME "/opt/root.cfg"
668 #define DIRNAME "/opt"
669 #endif
670   // UTF-16 string must be aligned on 2-byte boundary. Strings and char arrays
671   // do not provide necessary alignment, so copy constant string into properly
672   // allocated memory in heap.
673   llvm::BumpPtrAllocator Alloc;
674   char *StrBuff = (char *)Alloc.Allocate(16, 4);
675   std::memset(StrBuff, 0, 16);
676   std::memcpy(StrBuff, "\xFF\xFE\x00\xD8\x00\x00", 6);
677   StringRef BadUTF(StrBuff, 6);
678   FS->setCurrentWorkingDirectory(TestRoot);
679   FS->addFile("/opt/root.cfg", 0, llvm::MemoryBuffer::getMemBuffer(BadUTF));
680   FS->addFile("/home/user/test.cfg", 0,
681               llvm::MemoryBuffer::getMemBuffer("@file.rsp"));
682 
683   {
684     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
685                      "clang LLVM compiler", FS);
686     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
687         {"/home/test/bin/clang", "--config", "/opt/root.cfg", "--version"}));
688     ASSERT_TRUE(C);
689     ASSERT_TRUE(C->containsError());
690     EXPECT_EQ(1U, DiagConsumer->Errors.size());
691     EXPECT_STREQ("cannot read configuration file '" FILENAME
692                  "': Could not convert UTF16 to UTF8",
693                  DiagConsumer->Errors[0].c_str());
694   }
695   DiagConsumer->clear();
696   {
697     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
698                      "clang LLVM compiler", FS);
699     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
700         {"/home/test/bin/clang", "--config", "/opt", "--version"}));
701     ASSERT_TRUE(C);
702     ASSERT_TRUE(C->containsError());
703     EXPECT_EQ(1U, DiagConsumer->Errors.size());
704     EXPECT_STREQ("configuration file '" DIRNAME
705                  "' cannot be opened: not a regular file",
706                  DiagConsumer->Errors[0].c_str());
707   }
708   DiagConsumer->clear();
709   {
710     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
711                      "clang LLVM compiler", FS);
712     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
713         {"/home/test/bin/clang", "--config", "root",
714          "--config-system-dir=", "--config-user-dir=", "--version"}));
715     ASSERT_TRUE(C);
716     ASSERT_TRUE(C->containsError());
717     EXPECT_EQ(1U, DiagConsumer->Errors.size());
718     EXPECT_STREQ("configuration file 'root' cannot be found",
719                  DiagConsumer->Errors[0].c_str());
720   }
721 
722 #undef FILENAME
723 #undef DIRNAME
724 }
725 
726 TEST(ToolChainTest, ConfigInexistentInclude) {
727   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
728   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
729   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
730       new SimpleDiagnosticConsumer());
731   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
732   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
733       new llvm::vfs::InMemoryFileSystem);
734 
735 #ifdef _WIN32
736   const char *TestRoot = "C:\\";
737 #define USERCONFIG "C:\\home\\user\\test.cfg"
738 #define UNEXISTENT "C:\\home\\user\\file.rsp"
739 #else
740   const char *TestRoot = "/";
741 #define USERCONFIG "/home/user/test.cfg"
742 #define UNEXISTENT "/home/user/file.rsp"
743 #endif
744   FS->setCurrentWorkingDirectory(TestRoot);
745   FS->addFile("/home/user/test.cfg", 0,
746               llvm::MemoryBuffer::getMemBuffer("@file.rsp"));
747 
748   {
749     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
750                      "clang LLVM compiler", FS);
751     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
752         {"/home/test/bin/clang", "--config", "test.cfg",
753          "--config-system-dir=", "--config-user-dir=/home/user", "--version"}));
754     ASSERT_TRUE(C);
755     ASSERT_TRUE(C->containsError());
756     EXPECT_EQ(1U, DiagConsumer->Errors.size());
757     EXPECT_STRCASEEQ("cannot read configuration file '" USERCONFIG
758                      "': cannot not open file '" UNEXISTENT
759                      "': no such file or directory",
760                      DiagConsumer->Errors[0].c_str());
761   }
762 
763 #undef USERCONFIG
764 #undef UNEXISTENT
765 }
766 
767 TEST(ToolChainTest, ConfigRecursiveInclude) {
768   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
769   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
770   std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer(
771       new SimpleDiagnosticConsumer());
772   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false);
773   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
774       new llvm::vfs::InMemoryFileSystem);
775 
776 #ifdef _WIN32
777   const char *TestRoot = "C:\\";
778 #define USERCONFIG "C:\\home\\user\\test.cfg"
779 #define INCLUDED1 "C:\\home\\user\\file1.cfg"
780 #else
781   const char *TestRoot = "/";
782 #define USERCONFIG "/home/user/test.cfg"
783 #define INCLUDED1 "/home/user/file1.cfg"
784 #endif
785   FS->setCurrentWorkingDirectory(TestRoot);
786   FS->addFile("/home/user/test.cfg", 0,
787               llvm::MemoryBuffer::getMemBuffer("@file1.cfg"));
788   FS->addFile("/home/user/file1.cfg", 0,
789               llvm::MemoryBuffer::getMemBuffer("@file2.cfg"));
790   FS->addFile("/home/user/file2.cfg", 0,
791               llvm::MemoryBuffer::getMemBuffer("@file3.cfg"));
792   FS->addFile("/home/user/file3.cfg", 0,
793               llvm::MemoryBuffer::getMemBuffer("@file1.cfg"));
794 
795   {
796     Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
797                      "clang LLVM compiler", FS);
798     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
799         {"/home/test/bin/clang", "--config", "test.cfg",
800          "--config-system-dir=", "--config-user-dir=/home/user", "--version"}));
801     ASSERT_TRUE(C);
802     ASSERT_TRUE(C->containsError());
803     EXPECT_EQ(1U, DiagConsumer->Errors.size());
804     EXPECT_STREQ("cannot read configuration file '" USERCONFIG
805                  "': recursive expansion of: '" INCLUDED1 "'",
806                  DiagConsumer->Errors[0].c_str());
807   }
808 
809 #undef USERCONFIG
810 #undef INCLUDED1
811 }
812 
813 TEST(ToolChainTest, NestedConfigFile) {
814   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
815   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
816   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
817   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
818   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
819       new llvm::vfs::InMemoryFileSystem);
820 
821 #ifdef _WIN32
822   const char *TestRoot = "C:\\";
823 #else
824   const char *TestRoot = "/";
825 #endif
826   FS->setCurrentWorkingDirectory(TestRoot);
827 
828   FS->addFile("/opt/sdk/root.cfg", 0,
829               llvm::MemoryBuffer::getMemBuffer("--config=platform.cfg\n"));
830   FS->addFile("/opt/sdk/platform.cfg", 0,
831               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-sys\n"));
832   FS->addFile("/home/test/bin/platform.cfg", 0,
833               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-bin\n"));
834 
835   SmallString<128> ClangExecutable("/home/test/bin/clang");
836   FS->makeAbsolute(ClangExecutable);
837 
838   // User file is absent - use system definitions.
839   {
840     Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags,
841                      "clang LLVM compiler", FS);
842     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
843         {"/home/test/bin/clang", "--config", "root.cfg",
844          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
845     ASSERT_TRUE(C);
846     ASSERT_FALSE(C->containsError());
847     EXPECT_EQ("/platform-sys", TheDriver.SysRoot);
848   }
849 
850   // User file overrides system definitions.
851   FS->addFile("/home/test/sdk/platform.cfg", 0,
852               llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-user\n"));
853   {
854     Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags,
855                      "clang LLVM compiler", FS);
856     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
857         {"/home/test/bin/clang", "--config", "root.cfg",
858          "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"}));
859     ASSERT_TRUE(C);
860     ASSERT_FALSE(C->containsError());
861     EXPECT_EQ("/platform-user", TheDriver.SysRoot);
862   }
863 }
864 
865 } // end anonymous namespace.
866