xref: /llvm-project/clang/unittests/Driver/ToolChainTest.cpp (revision 55e1441f7b5d01a37fc46eb1711f03ee69845316)
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/Host.h"
24 #include "llvm/Support/TargetSelect.h"
25 #include "llvm/Support/VirtualFileSystem.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include "gtest/gtest.h"
28 #include <memory>
29 
30 #include "SimpleDiagnosticConsumer.h"
31 
32 using namespace clang;
33 using namespace clang::driver;
34 
35 namespace {
36 
37 TEST(ToolChainTest, VFSGCCInstallation) {
38   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
39 
40   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
41   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
42   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
43       new llvm::vfs::InMemoryFileSystem);
44 
45   const char *EmptyFiles[] = {
46       "foo.cpp",
47       "/bin/clang",
48       "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o",
49       "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o",
50       "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o",
51       "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o",
52       "/usr/lib/arm-linux-gnueabi/crt1.o",
53       "/usr/lib/arm-linux-gnueabi/crti.o",
54       "/usr/lib/arm-linux-gnueabi/crtn.o",
55       "/usr/lib/arm-linux-gnueabihf/crt1.o",
56       "/usr/lib/arm-linux-gnueabihf/crti.o",
57       "/usr/lib/arm-linux-gnueabihf/crtn.o",
58       "/usr/include/arm-linux-gnueabi/.keep",
59       "/usr/include/arm-linux-gnueabihf/.keep",
60       "/lib/arm-linux-gnueabi/.keep",
61       "/lib/arm-linux-gnueabihf/.keep",
62 
63       "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtbegin.o",
64       "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtend.o",
65       "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtbegin.o",
66       "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtend.o",
67       "/sysroot/usr/lib/arm-linux-gnueabi/crt1.o",
68       "/sysroot/usr/lib/arm-linux-gnueabi/crti.o",
69       "/sysroot/usr/lib/arm-linux-gnueabi/crtn.o",
70       "/sysroot/usr/lib/arm-linux-gnueabihf/crt1.o",
71       "/sysroot/usr/lib/arm-linux-gnueabihf/crti.o",
72       "/sysroot/usr/lib/arm-linux-gnueabihf/crtn.o",
73       "/sysroot/usr/include/arm-linux-gnueabi/.keep",
74       "/sysroot/usr/include/arm-linux-gnueabihf/.keep",
75       "/sysroot/lib/arm-linux-gnueabi/.keep",
76       "/sysroot/lib/arm-linux-gnueabihf/.keep",
77   };
78 
79   for (const char *Path : EmptyFiles)
80     InMemoryFileSystem->addFile(Path, 0,
81                                 llvm::MemoryBuffer::getMemBuffer("\n"));
82 
83   {
84     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
85     Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags,
86                      "clang LLVM compiler", InMemoryFileSystem);
87     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
88         {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=", "foo.cpp"}));
89     ASSERT_TRUE(C);
90     std::string S;
91     {
92       llvm::raw_string_ostream OS(S);
93       C->getDefaultToolChain().printVerboseInfo(OS);
94     }
95     if (is_style_windows(llvm::sys::path::Style::native))
96       std::replace(S.begin(), S.end(), '\\', '/');
97     EXPECT_EQ(
98         "Found candidate GCC installation: "
99         "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n"
100         "Selected GCC installation: /usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n"
101         "Candidate multilib: .;@m32\n"
102         "Selected multilib: .;@m32\n",
103         S);
104   }
105 
106   {
107     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
108     Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags,
109                      "clang LLVM compiler", InMemoryFileSystem);
110     std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
111         {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=/sysroot",
112          "foo.cpp"}));
113     ASSERT_TRUE(C);
114     std::string S;
115     {
116       llvm::raw_string_ostream OS(S);
117       C->getDefaultToolChain().printVerboseInfo(OS);
118     }
119     if (is_style_windows(llvm::sys::path::Style::native))
120       std::replace(S.begin(), S.end(), '\\', '/');
121     // Test that 4.5.3 from --sysroot is not overridden by 4.6.3 (larger
122     // version) from /usr.
123     EXPECT_EQ("Found candidate GCC installation: "
124               "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n"
125               "Selected GCC installation: "
126               "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n"
127               "Candidate multilib: .;@m32\n"
128               "Selected multilib: .;@m32\n",
129               S);
130   }
131 }
132 
133 TEST(ToolChainTest, VFSGCCInstallationRelativeDir) {
134   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
135 
136   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
137   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
138   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
139   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
140       new llvm::vfs::InMemoryFileSystem);
141   Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
142                    "clang LLVM compiler", InMemoryFileSystem);
143 
144   const char *EmptyFiles[] = {
145       "foo.cpp", "/home/test/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o",
146       "/home/test/include/arm-linux-gnueabi/.keep"};
147 
148   for (const char *Path : EmptyFiles)
149     InMemoryFileSystem->addFile(Path, 0,
150                                 llvm::MemoryBuffer::getMemBuffer("\n"));
151 
152   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
153       {"-fsyntax-only", "--gcc-toolchain=", "foo.cpp"}));
154   EXPECT_TRUE(C);
155 
156   std::string S;
157   {
158     llvm::raw_string_ostream OS(S);
159     C->getDefaultToolChain().printVerboseInfo(OS);
160   }
161   if (is_style_windows(llvm::sys::path::Style::native))
162     std::replace(S.begin(), S.end(), '\\', '/');
163   EXPECT_EQ("Found candidate GCC installation: "
164             "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n"
165             "Selected GCC installation: "
166             "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n"
167             "Candidate multilib: .;@m32\n"
168             "Selected multilib: .;@m32\n",
169             S);
170 }
171 
172 TEST(ToolChainTest, DefaultDriverMode) {
173   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
174 
175   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
176   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
177   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
178   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
179       new llvm::vfs::InMemoryFileSystem);
180 
181   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
182                   "clang LLVM compiler", InMemoryFileSystem);
183   CCDriver.setCheckInputsExist(false);
184   Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags,
185                    "clang LLVM compiler", InMemoryFileSystem);
186   CXXDriver.setCheckInputsExist(false);
187   Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags,
188                   "clang LLVM compiler", InMemoryFileSystem);
189   CLDriver.setCheckInputsExist(false);
190 
191   std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation(
192       { "/home/test/bin/clang", "foo.cpp"}));
193   std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation(
194       { "/home/test/bin/clang++", "foo.cpp"}));
195   std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation(
196       { "/home/test/bin/clang-cl", "foo.cpp"}));
197 
198   EXPECT_TRUE(CC);
199   EXPECT_TRUE(CXX);
200   EXPECT_TRUE(CL);
201   EXPECT_TRUE(CCDriver.CCCIsCC());
202   EXPECT_TRUE(CXXDriver.CCCIsCXX());
203   EXPECT_TRUE(CLDriver.IsCLMode());
204 }
205 TEST(ToolChainTest, InvalidArgument) {
206   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
207   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
208   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
209   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
210   Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags);
211   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
212       {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"}));
213   EXPECT_TRUE(C);
214   EXPECT_TRUE(C->containsError());
215 }
216 
217 TEST(ToolChainTest, ParsedClangName) {
218   ParsedClangName Empty;
219   EXPECT_TRUE(Empty.TargetPrefix.empty());
220   EXPECT_TRUE(Empty.ModeSuffix.empty());
221   EXPECT_TRUE(Empty.DriverMode == nullptr);
222   EXPECT_FALSE(Empty.TargetIsValid);
223 
224   ParsedClangName DriverOnly("clang", nullptr);
225   EXPECT_TRUE(DriverOnly.TargetPrefix.empty());
226   EXPECT_TRUE(DriverOnly.ModeSuffix == "clang");
227   EXPECT_TRUE(DriverOnly.DriverMode == nullptr);
228   EXPECT_FALSE(DriverOnly.TargetIsValid);
229 
230   ParsedClangName DriverOnly2("clang++", "--driver-mode=g++");
231   EXPECT_TRUE(DriverOnly2.TargetPrefix.empty());
232   EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++");
233   EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++");
234   EXPECT_FALSE(DriverOnly2.TargetIsValid);
235 
236   ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true);
237   EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386");
238   EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++");
239   EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++");
240   EXPECT_TRUE(TargetAndMode.TargetIsValid);
241 }
242 
243 TEST(ToolChainTest, GetTargetAndMode) {
244   llvm::InitializeAllTargets();
245   std::string IgnoredError;
246   if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError))
247     return;
248 
249   ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang");
250   EXPECT_TRUE(Res.TargetPrefix.empty());
251   EXPECT_TRUE(Res.ModeSuffix == "clang");
252   EXPECT_TRUE(Res.DriverMode == nullptr);
253   EXPECT_FALSE(Res.TargetIsValid);
254 
255   Res = ToolChain::getTargetAndModeFromProgramName("clang++");
256   EXPECT_TRUE(Res.TargetPrefix.empty());
257   EXPECT_TRUE(Res.ModeSuffix == "clang++");
258   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
259   EXPECT_FALSE(Res.TargetIsValid);
260 
261   Res = ToolChain::getTargetAndModeFromProgramName("clang++6.0");
262   EXPECT_TRUE(Res.TargetPrefix.empty());
263   EXPECT_TRUE(Res.ModeSuffix == "clang++");
264   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
265   EXPECT_FALSE(Res.TargetIsValid);
266 
267   Res = ToolChain::getTargetAndModeFromProgramName("clang++-release");
268   EXPECT_TRUE(Res.TargetPrefix.empty());
269   EXPECT_TRUE(Res.ModeSuffix == "clang++");
270   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
271   EXPECT_FALSE(Res.TargetIsValid);
272 
273   Res = ToolChain::getTargetAndModeFromProgramName("x86_64-clang++");
274   EXPECT_TRUE(Res.TargetPrefix == "x86_64");
275   EXPECT_TRUE(Res.ModeSuffix == "clang++");
276   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
277   EXPECT_TRUE(Res.TargetIsValid);
278 
279   Res = ToolChain::getTargetAndModeFromProgramName(
280       "x86_64-linux-gnu-clang-c++");
281   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
282   EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
283   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
284   EXPECT_TRUE(Res.TargetIsValid);
285 
286   Res = ToolChain::getTargetAndModeFromProgramName(
287       "x86_64-linux-gnu-clang-c++-tot");
288   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
289   EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
290   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
291   EXPECT_TRUE(Res.TargetIsValid);
292 
293   Res = ToolChain::getTargetAndModeFromProgramName("qqq");
294   EXPECT_TRUE(Res.TargetPrefix.empty());
295   EXPECT_TRUE(Res.ModeSuffix.empty());
296   EXPECT_TRUE(Res.DriverMode == nullptr);
297   EXPECT_FALSE(Res.TargetIsValid);
298 
299   Res = ToolChain::getTargetAndModeFromProgramName("x86_64-qqq");
300   EXPECT_TRUE(Res.TargetPrefix.empty());
301   EXPECT_TRUE(Res.ModeSuffix.empty());
302   EXPECT_TRUE(Res.DriverMode == nullptr);
303   EXPECT_FALSE(Res.TargetIsValid);
304 
305   Res = ToolChain::getTargetAndModeFromProgramName("qqq-clang-cl");
306   EXPECT_TRUE(Res.TargetPrefix == "qqq");
307   EXPECT_TRUE(Res.ModeSuffix == "clang-cl");
308   EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl");
309   EXPECT_FALSE(Res.TargetIsValid);
310 
311   Res = ToolChain::getTargetAndModeFromProgramName("clang-dxc");
312   EXPECT_TRUE(Res.TargetPrefix.empty());
313   EXPECT_TRUE(Res.ModeSuffix == "clang-dxc");
314   EXPECT_STREQ(Res.DriverMode, "--driver-mode=dxc");
315   EXPECT_FALSE(Res.TargetIsValid);
316 }
317 
318 TEST(ToolChainTest, CommandOutput) {
319   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
320 
321   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
322   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
323   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
324   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
325       new llvm::vfs::InMemoryFileSystem);
326 
327   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
328                   "clang LLVM compiler", InMemoryFileSystem);
329   CCDriver.setCheckInputsExist(false);
330   std::unique_ptr<Compilation> CC(
331       CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
332   const JobList &Jobs = CC->getJobs();
333 
334   const auto &CmdCompile = Jobs.getJobs().front();
335   const auto &InFile = CmdCompile->getInputInfos().front().getFilename();
336   EXPECT_STREQ(InFile, "foo.cpp");
337   auto ObjFile = CmdCompile->getOutputFilenames().front();
338   EXPECT_TRUE(StringRef(ObjFile).endswith(".o"));
339 
340   const auto &CmdLink = Jobs.getJobs().back();
341   const auto LinkInFile = CmdLink->getInputInfos().front().getFilename();
342   EXPECT_EQ(ObjFile, LinkInFile);
343   auto ExeFile = CmdLink->getOutputFilenames().front();
344   EXPECT_EQ("a.out", ExeFile);
345 }
346 
347 TEST(ToolChainTest, PostCallback) {
348   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
349   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
350   struct TestDiagnosticConsumer : public DiagnosticConsumer {};
351   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
352   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
353       new llvm::vfs::InMemoryFileSystem);
354 
355   // The executable path must not exist.
356   Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
357                   "clang LLVM compiler", InMemoryFileSystem);
358   CCDriver.setCheckInputsExist(false);
359   std::unique_ptr<Compilation> CC(
360       CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
361   bool CallbackHasCalled = false;
362   CC->setPostCallback(
363       [&](const Command &C, int Ret) { CallbackHasCalled = true; });
364   const JobList &Jobs = CC->getJobs();
365   auto &CmdCompile = Jobs.getJobs().front();
366   const Command *FailingCmd = nullptr;
367   CC->ExecuteCommand(*CmdCompile, FailingCmd);
368   EXPECT_TRUE(CallbackHasCalled);
369 }
370 
371 TEST(GetDriverMode, PrefersLastDriverMode) {
372   static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo",
373                                          "--driver-mode=bar", "foo.cpp"};
374   EXPECT_EQ(getDriverMode(Args[0], llvm::makeArrayRef(Args).slice(1)), "bar");
375 }
376 
377 struct SimpleDiagnosticConsumer : public DiagnosticConsumer {
378   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
379                         const Diagnostic &Info) override {
380     if (DiagLevel == DiagnosticsEngine::Level::Error) {
381       Errors.emplace_back();
382       Info.FormatDiagnostic(Errors.back());
383     } else {
384       Msgs.emplace_back();
385       Info.FormatDiagnostic(Msgs.back());
386     }
387   }
388   void clear() override {
389     Msgs.clear();
390     Errors.clear();
391     DiagnosticConsumer::clear();
392   }
393   std::vector<SmallString<32>> Msgs;
394   std::vector<SmallString<32>> Errors;
395 };
396 
397 TEST(ToolChainTest, Toolsets) {
398   // Ignore this test on Windows hosts.
399   llvm::Triple Host(llvm::sys::getProcessTriple());
400   if (Host.isOSWindows())
401     GTEST_SKIP();
402 
403   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
404   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
405 
406   // Check (newer) GCC toolset installation.
407   {
408     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
409         new llvm::vfs::InMemoryFileSystem);
410 
411     // These should be ignored.
412     InMemoryFileSystem->addFile("/opt/rh/gcc-toolset-2", 0,
413                                 llvm::MemoryBuffer::getMemBuffer("\n"));
414     InMemoryFileSystem->addFile("/opt/rh/gcc-toolset-", 0,
415                                 llvm::MemoryBuffer::getMemBuffer("\n"));
416     InMemoryFileSystem->addFile("/opt/rh/gcc-toolset--", 0,
417                                 llvm::MemoryBuffer::getMemBuffer("\n"));
418     InMemoryFileSystem->addFile("/opt/rh/gcc-toolset--1", 0,
419                                 llvm::MemoryBuffer::getMemBuffer("\n"));
420 
421     // File needed for GCC installation detection.
422     InMemoryFileSystem->addFile("/opt/rh/gcc-toolset-12/root/usr/lib/gcc/"
423                                 "x86_64-redhat-linux/11/crtbegin.o",
424                                 0, llvm::MemoryBuffer::getMemBuffer("\n"));
425 
426     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new SimpleDiagnosticConsumer);
427     Driver TheDriver("/bin/clang", "x86_64-redhat-linux", Diags,
428                      "clang LLVM compiler", InMemoryFileSystem);
429     std::unique_ptr<Compilation> C(
430         TheDriver.BuildCompilation({"clang", "--gcc-toolchain="}));
431     ASSERT_TRUE(C);
432     std::string S;
433     {
434       llvm::raw_string_ostream OS(S);
435       C->getDefaultToolChain().printVerboseInfo(OS);
436     }
437     EXPECT_EQ("Found candidate GCC installation: "
438               "/opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/11\n"
439               "Selected GCC installation: "
440               "/opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/11\n"
441               "Candidate multilib: .;@m64\n"
442               "Selected multilib: .;@m64\n",
443               S);
444   }
445 
446   // And older devtoolset.
447   {
448     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
449         new llvm::vfs::InMemoryFileSystem);
450 
451     // These should be ignored.
452     InMemoryFileSystem->addFile("/opt/rh/devtoolset-2", 0,
453                                 llvm::MemoryBuffer::getMemBuffer("\n"));
454     InMemoryFileSystem->addFile("/opt/rh/devtoolset-", 0,
455                                 llvm::MemoryBuffer::getMemBuffer("\n"));
456     InMemoryFileSystem->addFile("/opt/rh/devtoolset--", 0,
457                                 llvm::MemoryBuffer::getMemBuffer("\n"));
458     InMemoryFileSystem->addFile("/opt/rh/devtoolset--1", 0,
459                                 llvm::MemoryBuffer::getMemBuffer("\n"));
460 
461     // File needed for GCC installation detection.
462     InMemoryFileSystem->addFile("/opt/rh/devtoolset-12/root/usr/lib/gcc/"
463                                 "x86_64-redhat-linux/11/crtbegin.o",
464                                 0, llvm::MemoryBuffer::getMemBuffer("\n"));
465 
466     DiagnosticsEngine Diags(DiagID, &*DiagOpts, new SimpleDiagnosticConsumer);
467     Driver TheDriver("/bin/clang", "x86_64-redhat-linux", Diags,
468                      "clang LLVM compiler", InMemoryFileSystem);
469     std::unique_ptr<Compilation> C(
470         TheDriver.BuildCompilation({"clang", "--gcc-toolchain="}));
471     ASSERT_TRUE(C);
472     std::string S;
473     {
474       llvm::raw_string_ostream OS(S);
475       C->getDefaultToolChain().printVerboseInfo(OS);
476     }
477     EXPECT_EQ("Found candidate GCC installation: "
478               "/opt/rh/devtoolset-12/root/usr/lib/gcc/x86_64-redhat-linux/11\n"
479               "Selected GCC installation: "
480               "/opt/rh/devtoolset-12/root/usr/lib/gcc/x86_64-redhat-linux/11\n"
481               "Candidate multilib: .;@m64\n"
482               "Selected multilib: .;@m64\n",
483               S);
484   }
485 }
486 
487 } // end anonymous namespace.
488