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/MC/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 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 (is_style_windows(llvm::sys::path::Style::native)) 89 std::replace(S.begin(), S.end(), '\\', '/'); 90 EXPECT_EQ( 91 "Found candidate GCC installation: " 92 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n" 93 "Selected GCC installation: /usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n" 94 "Candidate multilib: .;@m32\n" 95 "Selected multilib: .;@m32\n", 96 S); 97 } 98 99 { 100 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 101 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags, 102 "clang LLVM compiler", InMemoryFileSystem); 103 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 104 {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=/sysroot", 105 "foo.cpp"})); 106 ASSERT_TRUE(C); 107 std::string S; 108 { 109 llvm::raw_string_ostream OS(S); 110 C->getDefaultToolChain().printVerboseInfo(OS); 111 } 112 if (is_style_windows(llvm::sys::path::Style::native)) 113 std::replace(S.begin(), S.end(), '\\', '/'); 114 // Test that 4.5.3 from --sysroot is not overridden by 4.6.3 (larger 115 // version) from /usr. 116 EXPECT_EQ("Found candidate GCC installation: " 117 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n" 118 "Selected GCC installation: " 119 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n" 120 "Candidate multilib: .;@m32\n" 121 "Selected multilib: .;@m32\n", 122 S); 123 } 124 } 125 126 TEST(ToolChainTest, VFSGCCInstallationRelativeDir) { 127 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 128 129 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 130 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 131 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 132 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 133 new llvm::vfs::InMemoryFileSystem); 134 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 135 "clang LLVM compiler", InMemoryFileSystem); 136 137 const char *EmptyFiles[] = { 138 "foo.cpp", "/home/test/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o", 139 "/home/test/include/arm-linux-gnueabi/.keep"}; 140 141 for (const char *Path : EmptyFiles) 142 InMemoryFileSystem->addFile(Path, 0, 143 llvm::MemoryBuffer::getMemBuffer("\n")); 144 145 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 146 {"-fsyntax-only", "--gcc-toolchain=", "foo.cpp"})); 147 EXPECT_TRUE(C); 148 149 std::string S; 150 { 151 llvm::raw_string_ostream OS(S); 152 C->getDefaultToolChain().printVerboseInfo(OS); 153 } 154 if (is_style_windows(llvm::sys::path::Style::native)) 155 std::replace(S.begin(), S.end(), '\\', '/'); 156 EXPECT_EQ("Found candidate GCC installation: " 157 "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n" 158 "Selected GCC installation: " 159 "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n" 160 "Candidate multilib: .;@m32\n" 161 "Selected multilib: .;@m32\n", 162 S); 163 } 164 165 TEST(ToolChainTest, DefaultDriverMode) { 166 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 167 168 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 169 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 170 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 171 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 172 new llvm::vfs::InMemoryFileSystem); 173 174 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 175 "clang LLVM compiler", InMemoryFileSystem); 176 CCDriver.setCheckInputsExist(false); 177 Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags, 178 "clang LLVM compiler", InMemoryFileSystem); 179 CXXDriver.setCheckInputsExist(false); 180 Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags, 181 "clang LLVM compiler", InMemoryFileSystem); 182 CLDriver.setCheckInputsExist(false); 183 184 std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation( 185 { "/home/test/bin/clang", "foo.cpp"})); 186 std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation( 187 { "/home/test/bin/clang++", "foo.cpp"})); 188 std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation( 189 { "/home/test/bin/clang-cl", "foo.cpp"})); 190 191 EXPECT_TRUE(CC); 192 EXPECT_TRUE(CXX); 193 EXPECT_TRUE(CL); 194 EXPECT_TRUE(CCDriver.CCCIsCC()); 195 EXPECT_TRUE(CXXDriver.CCCIsCXX()); 196 EXPECT_TRUE(CLDriver.IsCLMode()); 197 } 198 TEST(ToolChainTest, InvalidArgument) { 199 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 200 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 201 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 202 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 203 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags); 204 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 205 {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"})); 206 EXPECT_TRUE(C); 207 EXPECT_TRUE(C->containsError()); 208 } 209 210 TEST(ToolChainTest, ParsedClangName) { 211 ParsedClangName Empty; 212 EXPECT_TRUE(Empty.TargetPrefix.empty()); 213 EXPECT_TRUE(Empty.ModeSuffix.empty()); 214 EXPECT_TRUE(Empty.DriverMode == nullptr); 215 EXPECT_FALSE(Empty.TargetIsValid); 216 217 ParsedClangName DriverOnly("clang", nullptr); 218 EXPECT_TRUE(DriverOnly.TargetPrefix.empty()); 219 EXPECT_TRUE(DriverOnly.ModeSuffix == "clang"); 220 EXPECT_TRUE(DriverOnly.DriverMode == nullptr); 221 EXPECT_FALSE(DriverOnly.TargetIsValid); 222 223 ParsedClangName DriverOnly2("clang++", "--driver-mode=g++"); 224 EXPECT_TRUE(DriverOnly2.TargetPrefix.empty()); 225 EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++"); 226 EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++"); 227 EXPECT_FALSE(DriverOnly2.TargetIsValid); 228 229 ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true); 230 EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386"); 231 EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++"); 232 EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++"); 233 EXPECT_TRUE(TargetAndMode.TargetIsValid); 234 } 235 236 TEST(ToolChainTest, GetTargetAndMode) { 237 llvm::InitializeAllTargets(); 238 std::string IgnoredError; 239 if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError)) 240 return; 241 242 ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang"); 243 EXPECT_TRUE(Res.TargetPrefix.empty()); 244 EXPECT_TRUE(Res.ModeSuffix == "clang"); 245 EXPECT_TRUE(Res.DriverMode == nullptr); 246 EXPECT_FALSE(Res.TargetIsValid); 247 248 Res = ToolChain::getTargetAndModeFromProgramName("clang++"); 249 EXPECT_TRUE(Res.TargetPrefix.empty()); 250 EXPECT_TRUE(Res.ModeSuffix == "clang++"); 251 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); 252 EXPECT_FALSE(Res.TargetIsValid); 253 254 Res = ToolChain::getTargetAndModeFromProgramName("clang++6.0"); 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++-release"); 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("x86_64-clang++"); 267 EXPECT_TRUE(Res.TargetPrefix == "x86_64"); 268 EXPECT_TRUE(Res.ModeSuffix == "clang++"); 269 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); 270 EXPECT_TRUE(Res.TargetIsValid); 271 272 Res = ToolChain::getTargetAndModeFromProgramName( 273 "x86_64-linux-gnu-clang-c++"); 274 EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu"); 275 EXPECT_TRUE(Res.ModeSuffix == "clang-c++"); 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++-tot"); 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("qqq"); 287 EXPECT_TRUE(Res.TargetPrefix.empty()); 288 EXPECT_TRUE(Res.ModeSuffix.empty()); 289 EXPECT_TRUE(Res.DriverMode == nullptr); 290 EXPECT_FALSE(Res.TargetIsValid); 291 292 Res = ToolChain::getTargetAndModeFromProgramName("x86_64-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("qqq-clang-cl"); 299 EXPECT_TRUE(Res.TargetPrefix == "qqq"); 300 EXPECT_TRUE(Res.ModeSuffix == "clang-cl"); 301 EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl"); 302 EXPECT_FALSE(Res.TargetIsValid); 303 } 304 305 TEST(ToolChainTest, CommandOutput) { 306 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 307 308 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 309 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 310 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 311 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 312 new llvm::vfs::InMemoryFileSystem); 313 314 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 315 "clang LLVM compiler", InMemoryFileSystem); 316 CCDriver.setCheckInputsExist(false); 317 std::unique_ptr<Compilation> CC( 318 CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"})); 319 const JobList &Jobs = CC->getJobs(); 320 321 const auto &CmdCompile = Jobs.getJobs().front(); 322 const auto &InFile = CmdCompile->getInputInfos().front().getFilename(); 323 EXPECT_STREQ(InFile, "foo.cpp"); 324 auto ObjFile = CmdCompile->getOutputFilenames().front(); 325 EXPECT_TRUE(StringRef(ObjFile).endswith(".o")); 326 327 const auto &CmdLink = Jobs.getJobs().back(); 328 const auto LinkInFile = CmdLink->getInputInfos().front().getFilename(); 329 EXPECT_EQ(ObjFile, LinkInFile); 330 auto ExeFile = CmdLink->getOutputFilenames().front(); 331 EXPECT_EQ("a.out", ExeFile); 332 } 333 334 TEST(ToolChainTest, PostCallback) { 335 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 336 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 337 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 338 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 339 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 340 new llvm::vfs::InMemoryFileSystem); 341 342 // The executable path must not exist. 343 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 344 "clang LLVM compiler", InMemoryFileSystem); 345 CCDriver.setCheckInputsExist(false); 346 std::unique_ptr<Compilation> CC( 347 CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"})); 348 bool CallbackHasCalled = false; 349 CC->setPostCallback( 350 [&](const Command &C, int Ret) { CallbackHasCalled = true; }); 351 const JobList &Jobs = CC->getJobs(); 352 auto &CmdCompile = Jobs.getJobs().front(); 353 const Command *FailingCmd = nullptr; 354 CC->ExecuteCommand(*CmdCompile, FailingCmd); 355 EXPECT_TRUE(CallbackHasCalled); 356 } 357 358 TEST(GetDriverMode, PrefersLastDriverMode) { 359 static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo", 360 "--driver-mode=bar", "foo.cpp"}; 361 EXPECT_EQ(getDriverMode(Args[0], llvm::makeArrayRef(Args).slice(1)), "bar"); 362 } 363 364 } // end anonymous namespace. 365