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