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 _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 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 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 } 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 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 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 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 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 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