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/Driver/Compilation.h"
18 #include "clang/Driver/Driver.h"
19 #include "llvm/ADT/ArrayRef.h"
20 #include "llvm/Support/TargetRegistry.h"
21 #include "llvm/Support/TargetSelect.h"
22 #include "llvm/Support/VirtualFileSystem.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include "gtest/gtest.h"
25 using namespace clang;
26 using namespace clang::driver;
27
28 namespace {
29
TEST(ToolChainTest,VFSGCCInstallation)30 TEST(ToolChainTest, VFSGCCInstallation) {
31 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
32
33 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
34 struct TestDiagnosticConsumer : public DiagnosticConsumer {};
35 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
36 new llvm::vfs::InMemoryFileSystem);
37
38 const char *EmptyFiles[] = {
39 "foo.cpp",
40 "/bin/clang",
41 "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o",
42 "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o",
43 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o",
44 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o",
45 "/usr/lib/arm-linux-gnueabi/crt1.o",
46 "/usr/lib/arm-linux-gnueabi/crti.o",
47 "/usr/lib/arm-linux-gnueabi/crtn.o",
48 "/usr/lib/arm-linux-gnueabihf/crt1.o",
49 "/usr/lib/arm-linux-gnueabihf/crti.o",
50 "/usr/lib/arm-linux-gnueabihf/crtn.o",
51 "/usr/include/arm-linux-gnueabi/.keep",
52 "/usr/include/arm-linux-gnueabihf/.keep",
53 "/lib/arm-linux-gnueabi/.keep",
54 "/lib/arm-linux-gnueabihf/.keep",
55
56 "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtbegin.o",
57 "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtend.o",
58 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtbegin.o",
59 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtend.o",
60 "/sysroot/usr/lib/arm-linux-gnueabi/crt1.o",
61 "/sysroot/usr/lib/arm-linux-gnueabi/crti.o",
62 "/sysroot/usr/lib/arm-linux-gnueabi/crtn.o",
63 "/sysroot/usr/lib/arm-linux-gnueabihf/crt1.o",
64 "/sysroot/usr/lib/arm-linux-gnueabihf/crti.o",
65 "/sysroot/usr/lib/arm-linux-gnueabihf/crtn.o",
66 "/sysroot/usr/include/arm-linux-gnueabi/.keep",
67 "/sysroot/usr/include/arm-linux-gnueabihf/.keep",
68 "/sysroot/lib/arm-linux-gnueabi/.keep",
69 "/sysroot/lib/arm-linux-gnueabihf/.keep",
70 };
71
72 for (const char *Path : EmptyFiles)
73 InMemoryFileSystem->addFile(Path, 0,
74 llvm::MemoryBuffer::getMemBuffer("\n"));
75
76 {
77 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
78 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags,
79 "clang LLVM compiler", InMemoryFileSystem);
80 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
81 {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=", "foo.cpp"}));
82 ASSERT_TRUE(C);
83 std::string S;
84 {
85 llvm::raw_string_ostream OS(S);
86 C->getDefaultToolChain().printVerboseInfo(OS);
87 }
88 #if _WIN32
89 std::replace(S.begin(), S.end(), '\\', '/');
90 #endif
91 EXPECT_EQ(
92 "Found candidate GCC installation: "
93 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n"
94 "Selected GCC installation: /usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n"
95 "Candidate multilib: .;@m32\n"
96 "Selected multilib: .;@m32\n",
97 S);
98 }
99
100 {
101 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
102 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags,
103 "clang LLVM compiler", InMemoryFileSystem);
104 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
105 {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=/sysroot",
106 "foo.cpp"}));
107 ASSERT_TRUE(C);
108 std::string S;
109 {
110 llvm::raw_string_ostream OS(S);
111 C->getDefaultToolChain().printVerboseInfo(OS);
112 }
113 #if _WIN32
114 std::replace(S.begin(), S.end(), '\\', '/');
115 #endif
116 // Test that 4.5.3 from --sysroot is not overridden by 4.6.3 (larger
117 // version) from /usr.
118 EXPECT_EQ("Found candidate GCC installation: "
119 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n"
120 "Selected GCC installation: "
121 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n"
122 "Candidate multilib: .;@m32\n"
123 "Selected multilib: .;@m32\n",
124 S);
125 }
126 }
127
TEST(ToolChainTest,VFSGCCInstallationRelativeDir)128 TEST(ToolChainTest, VFSGCCInstallationRelativeDir) {
129 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
130
131 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
132 struct TestDiagnosticConsumer : public DiagnosticConsumer {};
133 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
134 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
135 new llvm::vfs::InMemoryFileSystem);
136 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
137 "clang LLVM compiler", InMemoryFileSystem);
138
139 const char *EmptyFiles[] = {
140 "foo.cpp", "/home/test/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o",
141 "/home/test/include/arm-linux-gnueabi/.keep"};
142
143 for (const char *Path : EmptyFiles)
144 InMemoryFileSystem->addFile(Path, 0,
145 llvm::MemoryBuffer::getMemBuffer("\n"));
146
147 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
148 {"-fsyntax-only", "--gcc-toolchain=", "foo.cpp"}));
149 EXPECT_TRUE(C);
150
151 std::string S;
152 {
153 llvm::raw_string_ostream OS(S);
154 C->getDefaultToolChain().printVerboseInfo(OS);
155 }
156 #if _WIN32
157 std::replace(S.begin(), S.end(), '\\', '/');
158 #endif
159 EXPECT_EQ("Found candidate GCC installation: "
160 "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n"
161 "Selected GCC installation: "
162 "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n"
163 "Candidate multilib: .;@m32\n"
164 "Selected multilib: .;@m32\n",
165 S);
166 }
167
TEST(ToolChainTest,DefaultDriverMode)168 TEST(ToolChainTest, DefaultDriverMode) {
169 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
170
171 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
172 struct TestDiagnosticConsumer : public DiagnosticConsumer {};
173 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
174 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
175 new llvm::vfs::InMemoryFileSystem);
176
177 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
178 "clang LLVM compiler", InMemoryFileSystem);
179 CCDriver.setCheckInputsExist(false);
180 Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags,
181 "clang LLVM compiler", InMemoryFileSystem);
182 CXXDriver.setCheckInputsExist(false);
183 Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags,
184 "clang LLVM compiler", InMemoryFileSystem);
185 CLDriver.setCheckInputsExist(false);
186
187 std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation(
188 { "/home/test/bin/clang", "foo.cpp"}));
189 std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation(
190 { "/home/test/bin/clang++", "foo.cpp"}));
191 std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation(
192 { "/home/test/bin/clang-cl", "foo.cpp"}));
193
194 EXPECT_TRUE(CC);
195 EXPECT_TRUE(CXX);
196 EXPECT_TRUE(CL);
197 EXPECT_TRUE(CCDriver.CCCIsCC());
198 EXPECT_TRUE(CXXDriver.CCCIsCXX());
199 EXPECT_TRUE(CLDriver.IsCLMode());
200 }
TEST(ToolChainTest,InvalidArgument)201 TEST(ToolChainTest, InvalidArgument) {
202 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
203 struct TestDiagnosticConsumer : public DiagnosticConsumer {};
204 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
205 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
206 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags);
207 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
208 {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"}));
209 EXPECT_TRUE(C);
210 EXPECT_TRUE(C->containsError());
211 }
212
TEST(ToolChainTest,ParsedClangName)213 TEST(ToolChainTest, ParsedClangName) {
214 ParsedClangName Empty;
215 EXPECT_TRUE(Empty.TargetPrefix.empty());
216 EXPECT_TRUE(Empty.ModeSuffix.empty());
217 EXPECT_TRUE(Empty.DriverMode == nullptr);
218 EXPECT_FALSE(Empty.TargetIsValid);
219
220 ParsedClangName DriverOnly("clang", nullptr);
221 EXPECT_TRUE(DriverOnly.TargetPrefix.empty());
222 EXPECT_TRUE(DriverOnly.ModeSuffix == "clang");
223 EXPECT_TRUE(DriverOnly.DriverMode == nullptr);
224 EXPECT_FALSE(DriverOnly.TargetIsValid);
225
226 ParsedClangName DriverOnly2("clang++", "--driver-mode=g++");
227 EXPECT_TRUE(DriverOnly2.TargetPrefix.empty());
228 EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++");
229 EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++");
230 EXPECT_FALSE(DriverOnly2.TargetIsValid);
231
232 ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true);
233 EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386");
234 EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++");
235 EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++");
236 EXPECT_TRUE(TargetAndMode.TargetIsValid);
237 }
238
TEST(ToolChainTest,GetTargetAndMode)239 TEST(ToolChainTest, GetTargetAndMode) {
240 llvm::InitializeAllTargets();
241 std::string IgnoredError;
242 if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError))
243 return;
244
245 ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang");
246 EXPECT_TRUE(Res.TargetPrefix.empty());
247 EXPECT_TRUE(Res.ModeSuffix == "clang");
248 EXPECT_TRUE(Res.DriverMode == nullptr);
249 EXPECT_FALSE(Res.TargetIsValid);
250
251 Res = ToolChain::getTargetAndModeFromProgramName("clang++");
252 EXPECT_TRUE(Res.TargetPrefix.empty());
253 EXPECT_TRUE(Res.ModeSuffix == "clang++");
254 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
255 EXPECT_FALSE(Res.TargetIsValid);
256
257 Res = ToolChain::getTargetAndModeFromProgramName("clang++6.0");
258 EXPECT_TRUE(Res.TargetPrefix.empty());
259 EXPECT_TRUE(Res.ModeSuffix == "clang++");
260 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
261 EXPECT_FALSE(Res.TargetIsValid);
262
263 Res = ToolChain::getTargetAndModeFromProgramName("clang++-release");
264 EXPECT_TRUE(Res.TargetPrefix.empty());
265 EXPECT_TRUE(Res.ModeSuffix == "clang++");
266 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
267 EXPECT_FALSE(Res.TargetIsValid);
268
269 Res = ToolChain::getTargetAndModeFromProgramName("x86_64-clang++");
270 EXPECT_TRUE(Res.TargetPrefix == "x86_64");
271 EXPECT_TRUE(Res.ModeSuffix == "clang++");
272 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
273 EXPECT_TRUE(Res.TargetIsValid);
274
275 Res = ToolChain::getTargetAndModeFromProgramName(
276 "x86_64-linux-gnu-clang-c++");
277 EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
278 EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
279 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
280 EXPECT_TRUE(Res.TargetIsValid);
281
282 Res = ToolChain::getTargetAndModeFromProgramName(
283 "x86_64-linux-gnu-clang-c++-tot");
284 EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
285 EXPECT_TRUE(Res.ModeSuffix == "clang-c++");
286 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
287 EXPECT_TRUE(Res.TargetIsValid);
288
289 Res = ToolChain::getTargetAndModeFromProgramName("qqq");
290 EXPECT_TRUE(Res.TargetPrefix.empty());
291 EXPECT_TRUE(Res.ModeSuffix.empty());
292 EXPECT_TRUE(Res.DriverMode == nullptr);
293 EXPECT_FALSE(Res.TargetIsValid);
294
295 Res = ToolChain::getTargetAndModeFromProgramName("x86_64-qqq");
296 EXPECT_TRUE(Res.TargetPrefix.empty());
297 EXPECT_TRUE(Res.ModeSuffix.empty());
298 EXPECT_TRUE(Res.DriverMode == nullptr);
299 EXPECT_FALSE(Res.TargetIsValid);
300
301 Res = ToolChain::getTargetAndModeFromProgramName("qqq-clang-cl");
302 EXPECT_TRUE(Res.TargetPrefix == "qqq");
303 EXPECT_TRUE(Res.ModeSuffix == "clang-cl");
304 EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl");
305 EXPECT_FALSE(Res.TargetIsValid);
306 }
307
TEST(ToolChainTest,CommandOutput)308 TEST(ToolChainTest, CommandOutput) {
309 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
310
311 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
312 struct TestDiagnosticConsumer : public DiagnosticConsumer {};
313 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
314 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
315 new llvm::vfs::InMemoryFileSystem);
316
317 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
318 "clang LLVM compiler", InMemoryFileSystem);
319 CCDriver.setCheckInputsExist(false);
320 std::unique_ptr<Compilation> CC(
321 CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
322 const JobList &Jobs = CC->getJobs();
323
324 const auto &CmdCompile = Jobs.getJobs().front();
325 const auto &InFile = CmdCompile->getInputInfos().front().getFilename();
326 EXPECT_STREQ(InFile, "foo.cpp");
327 auto ObjFile = CmdCompile->getOutputFilenames().front();
328 EXPECT_TRUE(StringRef(ObjFile).endswith(".o"));
329
330 const auto &CmdLink = Jobs.getJobs().back();
331 const auto LinkInFile = CmdLink->getInputInfos().front().getFilename();
332 EXPECT_EQ(ObjFile, LinkInFile);
333 auto ExeFile = CmdLink->getOutputFilenames().front();
334 EXPECT_EQ("a.out", ExeFile);
335 }
336
TEST(ToolChainTest,PostCallback)337 TEST(ToolChainTest, PostCallback) {
338 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
339 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
340 struct TestDiagnosticConsumer : public DiagnosticConsumer {};
341 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
342 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
343 new llvm::vfs::InMemoryFileSystem);
344
345 // The executable path must not exist.
346 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags,
347 "clang LLVM compiler", InMemoryFileSystem);
348 CCDriver.setCheckInputsExist(false);
349 std::unique_ptr<Compilation> CC(
350 CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"}));
351 bool CallbackHasCalled = false;
352 CC->setPostCallback(
353 [&](const Command &C, int Ret) { CallbackHasCalled = true; });
354 const JobList &Jobs = CC->getJobs();
355 auto &CmdCompile = Jobs.getJobs().front();
356 const Command *FailingCmd = nullptr;
357 CC->ExecuteCommand(*CmdCompile, FailingCmd);
358 EXPECT_TRUE(CallbackHasCalled);
359 }
360
TEST(GetDriverMode,PrefersLastDriverMode)361 TEST(GetDriverMode, PrefersLastDriverMode) {
362 static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo",
363 "--driver-mode=bar", "foo.cpp"};
364 EXPECT_EQ(getDriverMode(Args[0], llvm::makeArrayRef(Args).slice(1)), "bar");
365 }
366
367 } // end anonymous namespace.
368