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/TargetSelect.h" 24 #include "llvm/Support/VirtualFileSystem.h" 25 #include "llvm/Support/raw_ostream.h" 26 #include "gtest/gtest.h" 27 #include <memory> 28 29 #include "SimpleDiagnosticConsumer.h" 30 31 using namespace clang; 32 using namespace clang::driver; 33 34 namespace { 35 36 TEST(ToolChainTest, VFSGCCInstallation) { 37 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 38 39 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 40 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 41 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 42 new llvm::vfs::InMemoryFileSystem); 43 44 const char *EmptyFiles[] = { 45 "foo.cpp", 46 "/bin/clang", 47 "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o", 48 "/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o", 49 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o", 50 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o", 51 "/usr/lib/arm-linux-gnueabi/crt1.o", 52 "/usr/lib/arm-linux-gnueabi/crti.o", 53 "/usr/lib/arm-linux-gnueabi/crtn.o", 54 "/usr/lib/arm-linux-gnueabihf/crt1.o", 55 "/usr/lib/arm-linux-gnueabihf/crti.o", 56 "/usr/lib/arm-linux-gnueabihf/crtn.o", 57 "/usr/include/arm-linux-gnueabi/.keep", 58 "/usr/include/arm-linux-gnueabihf/.keep", 59 "/lib/arm-linux-gnueabi/.keep", 60 "/lib/arm-linux-gnueabihf/.keep", 61 62 "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtbegin.o", 63 "/sysroot/usr/lib/gcc/arm-linux-gnueabi/4.5.1/crtend.o", 64 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtbegin.o", 65 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3/crtend.o", 66 "/sysroot/usr/lib/arm-linux-gnueabi/crt1.o", 67 "/sysroot/usr/lib/arm-linux-gnueabi/crti.o", 68 "/sysroot/usr/lib/arm-linux-gnueabi/crtn.o", 69 "/sysroot/usr/lib/arm-linux-gnueabihf/crt1.o", 70 "/sysroot/usr/lib/arm-linux-gnueabihf/crti.o", 71 "/sysroot/usr/lib/arm-linux-gnueabihf/crtn.o", 72 "/sysroot/usr/include/arm-linux-gnueabi/.keep", 73 "/sysroot/usr/include/arm-linux-gnueabihf/.keep", 74 "/sysroot/lib/arm-linux-gnueabi/.keep", 75 "/sysroot/lib/arm-linux-gnueabihf/.keep", 76 }; 77 78 for (const char *Path : EmptyFiles) 79 InMemoryFileSystem->addFile(Path, 0, 80 llvm::MemoryBuffer::getMemBuffer("\n")); 81 82 { 83 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 84 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags, 85 "clang LLVM compiler", InMemoryFileSystem); 86 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 87 {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=", "foo.cpp"})); 88 ASSERT_TRUE(C); 89 std::string S; 90 { 91 llvm::raw_string_ostream OS(S); 92 C->getDefaultToolChain().printVerboseInfo(OS); 93 } 94 if (is_style_windows(llvm::sys::path::Style::native)) 95 std::replace(S.begin(), S.end(), '\\', '/'); 96 EXPECT_EQ( 97 "Found candidate GCC installation: " 98 "/usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n" 99 "Selected GCC installation: /usr/lib/gcc/arm-linux-gnueabihf/4.6.3\n" 100 "Candidate multilib: .;@m32\n" 101 "Selected multilib: .;@m32\n", 102 S); 103 } 104 105 { 106 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 107 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags, 108 "clang LLVM compiler", InMemoryFileSystem); 109 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 110 {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=/sysroot", 111 "foo.cpp"})); 112 ASSERT_TRUE(C); 113 std::string S; 114 { 115 llvm::raw_string_ostream OS(S); 116 C->getDefaultToolChain().printVerboseInfo(OS); 117 } 118 if (is_style_windows(llvm::sys::path::Style::native)) 119 std::replace(S.begin(), S.end(), '\\', '/'); 120 // Test that 4.5.3 from --sysroot is not overridden by 4.6.3 (larger 121 // version) from /usr. 122 EXPECT_EQ("Found candidate GCC installation: " 123 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n" 124 "Selected GCC installation: " 125 "/sysroot/usr/lib/gcc/arm-linux-gnueabihf/4.5.3\n" 126 "Candidate multilib: .;@m32\n" 127 "Selected multilib: .;@m32\n", 128 S); 129 } 130 } 131 132 TEST(ToolChainTest, VFSGCCInstallationRelativeDir) { 133 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 134 135 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 136 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 137 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 138 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 139 new llvm::vfs::InMemoryFileSystem); 140 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 141 "clang LLVM compiler", InMemoryFileSystem); 142 143 const char *EmptyFiles[] = { 144 "foo.cpp", "/home/test/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o", 145 "/home/test/include/arm-linux-gnueabi/.keep"}; 146 147 for (const char *Path : EmptyFiles) 148 InMemoryFileSystem->addFile(Path, 0, 149 llvm::MemoryBuffer::getMemBuffer("\n")); 150 151 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 152 {"-fsyntax-only", "--gcc-toolchain=", "foo.cpp"})); 153 EXPECT_TRUE(C); 154 155 std::string S; 156 { 157 llvm::raw_string_ostream OS(S); 158 C->getDefaultToolChain().printVerboseInfo(OS); 159 } 160 if (is_style_windows(llvm::sys::path::Style::native)) 161 std::replace(S.begin(), S.end(), '\\', '/'); 162 EXPECT_EQ("Found candidate GCC installation: " 163 "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n" 164 "Selected GCC installation: " 165 "/home/test/bin/../lib/gcc/arm-linux-gnueabi/4.6.1\n" 166 "Candidate multilib: .;@m32\n" 167 "Selected multilib: .;@m32\n", 168 S); 169 } 170 171 TEST(ToolChainTest, DefaultDriverMode) { 172 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 173 174 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 175 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 176 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 177 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 178 new llvm::vfs::InMemoryFileSystem); 179 180 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 181 "clang LLVM compiler", InMemoryFileSystem); 182 CCDriver.setCheckInputsExist(false); 183 Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags, 184 "clang LLVM compiler", InMemoryFileSystem); 185 CXXDriver.setCheckInputsExist(false); 186 Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags, 187 "clang LLVM compiler", InMemoryFileSystem); 188 CLDriver.setCheckInputsExist(false); 189 190 std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation( 191 { "/home/test/bin/clang", "foo.cpp"})); 192 std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation( 193 { "/home/test/bin/clang++", "foo.cpp"})); 194 std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation( 195 { "/home/test/bin/clang-cl", "foo.cpp"})); 196 197 EXPECT_TRUE(CC); 198 EXPECT_TRUE(CXX); 199 EXPECT_TRUE(CL); 200 EXPECT_TRUE(CCDriver.CCCIsCC()); 201 EXPECT_TRUE(CXXDriver.CCCIsCXX()); 202 EXPECT_TRUE(CLDriver.IsCLMode()); 203 } 204 TEST(ToolChainTest, InvalidArgument) { 205 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 206 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 207 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 208 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 209 Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags); 210 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 211 {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"})); 212 EXPECT_TRUE(C); 213 EXPECT_TRUE(C->containsError()); 214 } 215 216 TEST(ToolChainTest, ParsedClangName) { 217 ParsedClangName Empty; 218 EXPECT_TRUE(Empty.TargetPrefix.empty()); 219 EXPECT_TRUE(Empty.ModeSuffix.empty()); 220 EXPECT_TRUE(Empty.DriverMode == nullptr); 221 EXPECT_FALSE(Empty.TargetIsValid); 222 223 ParsedClangName DriverOnly("clang", nullptr); 224 EXPECT_TRUE(DriverOnly.TargetPrefix.empty()); 225 EXPECT_TRUE(DriverOnly.ModeSuffix == "clang"); 226 EXPECT_TRUE(DriverOnly.DriverMode == nullptr); 227 EXPECT_FALSE(DriverOnly.TargetIsValid); 228 229 ParsedClangName DriverOnly2("clang++", "--driver-mode=g++"); 230 EXPECT_TRUE(DriverOnly2.TargetPrefix.empty()); 231 EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++"); 232 EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++"); 233 EXPECT_FALSE(DriverOnly2.TargetIsValid); 234 235 ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true); 236 EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386"); 237 EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++"); 238 EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++"); 239 EXPECT_TRUE(TargetAndMode.TargetIsValid); 240 } 241 242 TEST(ToolChainTest, GetTargetAndMode) { 243 llvm::InitializeAllTargets(); 244 std::string IgnoredError; 245 if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError)) 246 GTEST_SKIP(); 247 248 ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang"); 249 EXPECT_TRUE(Res.TargetPrefix.empty()); 250 EXPECT_TRUE(Res.ModeSuffix == "clang"); 251 EXPECT_TRUE(Res.DriverMode == nullptr); 252 EXPECT_FALSE(Res.TargetIsValid); 253 254 Res = ToolChain::getTargetAndModeFromProgramName("clang++"); 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++6.0"); 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("clang++-release"); 267 EXPECT_TRUE(Res.TargetPrefix.empty()); 268 EXPECT_TRUE(Res.ModeSuffix == "clang++"); 269 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); 270 EXPECT_FALSE(Res.TargetIsValid); 271 272 Res = ToolChain::getTargetAndModeFromProgramName("x86_64-clang++"); 273 EXPECT_TRUE(Res.TargetPrefix == "x86_64"); 274 EXPECT_TRUE(Res.ModeSuffix == "clang++"); 275 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); 276 EXPECT_TRUE(Res.TargetIsValid); 277 278 Res = ToolChain::getTargetAndModeFromProgramName( 279 "x86_64-linux-gnu-clang-c++"); 280 EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu"); 281 EXPECT_TRUE(Res.ModeSuffix == "clang-c++"); 282 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); 283 EXPECT_TRUE(Res.TargetIsValid); 284 285 Res = ToolChain::getTargetAndModeFromProgramName( 286 "x86_64-linux-gnu-clang-c++-tot"); 287 EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu"); 288 EXPECT_TRUE(Res.ModeSuffix == "clang-c++"); 289 EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); 290 EXPECT_TRUE(Res.TargetIsValid); 291 292 Res = ToolChain::getTargetAndModeFromProgramName("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("x86_64-qqq"); 299 EXPECT_TRUE(Res.TargetPrefix.empty()); 300 EXPECT_TRUE(Res.ModeSuffix.empty()); 301 EXPECT_TRUE(Res.DriverMode == nullptr); 302 EXPECT_FALSE(Res.TargetIsValid); 303 304 Res = ToolChain::getTargetAndModeFromProgramName("qqq-clang-cl"); 305 EXPECT_TRUE(Res.TargetPrefix == "qqq"); 306 EXPECT_TRUE(Res.ModeSuffix == "clang-cl"); 307 EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl"); 308 EXPECT_FALSE(Res.TargetIsValid); 309 310 Res = ToolChain::getTargetAndModeFromProgramName("clang-dxc"); 311 EXPECT_TRUE(Res.TargetPrefix.empty()); 312 EXPECT_TRUE(Res.ModeSuffix == "clang-dxc"); 313 EXPECT_STREQ(Res.DriverMode, "--driver-mode=dxc"); 314 EXPECT_FALSE(Res.TargetIsValid); 315 } 316 317 TEST(ToolChainTest, CommandOutput) { 318 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 319 320 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 321 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 322 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 323 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 324 new llvm::vfs::InMemoryFileSystem); 325 326 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 327 "clang LLVM compiler", InMemoryFileSystem); 328 CCDriver.setCheckInputsExist(false); 329 std::unique_ptr<Compilation> CC( 330 CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"})); 331 const JobList &Jobs = CC->getJobs(); 332 333 const auto &CmdCompile = Jobs.getJobs().front(); 334 const auto &InFile = CmdCompile->getInputInfos().front().getFilename(); 335 EXPECT_STREQ(InFile, "foo.cpp"); 336 auto ObjFile = CmdCompile->getOutputFilenames().front(); 337 EXPECT_TRUE(StringRef(ObjFile).endswith(".o")); 338 339 const auto &CmdLink = Jobs.getJobs().back(); 340 const auto LinkInFile = CmdLink->getInputInfos().front().getFilename(); 341 EXPECT_EQ(ObjFile, LinkInFile); 342 auto ExeFile = CmdLink->getOutputFilenames().front(); 343 EXPECT_EQ("a.out", ExeFile); 344 } 345 346 TEST(ToolChainTest, PostCallback) { 347 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 348 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 349 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 350 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 351 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 352 new llvm::vfs::InMemoryFileSystem); 353 354 // The executable path must not exist. 355 Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 356 "clang LLVM compiler", InMemoryFileSystem); 357 CCDriver.setCheckInputsExist(false); 358 std::unique_ptr<Compilation> CC( 359 CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"})); 360 bool CallbackHasCalled = false; 361 CC->setPostCallback( 362 [&](const Command &C, int Ret) { CallbackHasCalled = true; }); 363 const JobList &Jobs = CC->getJobs(); 364 auto &CmdCompile = Jobs.getJobs().front(); 365 const Command *FailingCmd = nullptr; 366 CC->ExecuteCommand(*CmdCompile, FailingCmd); 367 EXPECT_TRUE(CallbackHasCalled); 368 } 369 370 TEST(CompilerInvocation, SplitSwarfSingleCrash) { 371 static constexpr const char *Args[] = { 372 "clang", "--target=arm-linux-gnueabi", 373 "-gdwarf-4", "-gsplit-dwarf=single", 374 "-c", "foo.cpp"}; 375 CreateInvocationOptions CIOpts; 376 std::unique_ptr<CompilerInvocation> CI = createInvocation(Args, CIOpts); 377 EXPECT_TRUE(CI); // no-crash 378 } 379 380 TEST(GetDriverMode, PrefersLastDriverMode) { 381 static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo", 382 "--driver-mode=bar", "foo.cpp"}; 383 EXPECT_EQ(getDriverMode(Args[0], llvm::ArrayRef(Args).slice(1)), "bar"); 384 } 385 386 struct SimpleDiagnosticConsumer : public DiagnosticConsumer { 387 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 388 const Diagnostic &Info) override { 389 if (DiagLevel == DiagnosticsEngine::Level::Error) { 390 Errors.emplace_back(); 391 Info.FormatDiagnostic(Errors.back()); 392 } else { 393 Msgs.emplace_back(); 394 Info.FormatDiagnostic(Msgs.back()); 395 } 396 } 397 void clear() override { 398 Msgs.clear(); 399 Errors.clear(); 400 DiagnosticConsumer::clear(); 401 } 402 std::vector<SmallString<32>> Msgs; 403 std::vector<SmallString<32>> Errors; 404 }; 405 406 TEST(ToolChainTest, ConfigFileSearch) { 407 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 408 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 409 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 410 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 411 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( 412 new llvm::vfs::InMemoryFileSystem); 413 414 #ifdef _WIN32 415 const char *TestRoot = "C:\\"; 416 #else 417 const char *TestRoot = "/"; 418 #endif 419 FS->setCurrentWorkingDirectory(TestRoot); 420 421 FS->addFile( 422 "/opt/sdk/root.cfg", 0, 423 llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform0\n")); 424 FS->addFile( 425 "/home/test/sdk/root.cfg", 0, 426 llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform1\n")); 427 FS->addFile( 428 "/home/test/bin/root.cfg", 0, 429 llvm::MemoryBuffer::getMemBuffer("--sysroot=/opt/sdk/platform2\n")); 430 431 { 432 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 433 "clang LLVM compiler", FS); 434 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 435 {"/home/test/bin/clang", "--config", "root.cfg", 436 "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"})); 437 ASSERT_TRUE(C); 438 ASSERT_FALSE(C->containsError()); 439 EXPECT_EQ("/opt/sdk/platform1", TheDriver.SysRoot); 440 } 441 { 442 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 443 "clang LLVM compiler", FS); 444 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 445 {"/home/test/bin/clang", "--config", "root.cfg", 446 "--config-system-dir=/opt/sdk", "--config-user-dir="})); 447 ASSERT_TRUE(C); 448 ASSERT_FALSE(C->containsError()); 449 EXPECT_EQ("/opt/sdk/platform0", TheDriver.SysRoot); 450 } 451 { 452 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 453 "clang LLVM compiler", FS); 454 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 455 {"/home/test/bin/clang", "--config", "root.cfg", 456 "--config-system-dir=", "--config-user-dir="})); 457 ASSERT_TRUE(C); 458 ASSERT_FALSE(C->containsError()); 459 EXPECT_EQ("/opt/sdk/platform2", TheDriver.SysRoot); 460 } 461 } 462 463 struct FileSystemWithError : public llvm::vfs::FileSystem { 464 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override { 465 return std::make_error_code(std::errc::no_such_file_or_directory); 466 } 467 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 468 openFileForRead(const Twine &Path) override { 469 return std::make_error_code(std::errc::permission_denied); 470 } 471 llvm::vfs::directory_iterator dir_begin(const Twine &Dir, 472 std::error_code &EC) override { 473 return llvm::vfs::directory_iterator(); 474 } 475 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 476 return std::make_error_code(std::errc::permission_denied); 477 } 478 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 479 return std::make_error_code(std::errc::permission_denied); 480 } 481 }; 482 483 TEST(ToolChainTest, ConfigFileError) { 484 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 485 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 486 std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer( 487 new SimpleDiagnosticConsumer()); 488 DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false); 489 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS(new FileSystemWithError); 490 491 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 492 "clang LLVM compiler", FS); 493 std::unique_ptr<Compilation> C( 494 TheDriver.BuildCompilation({"/home/test/bin/clang", "--no-default-config", 495 "--config", "./root.cfg", "--version"})); 496 ASSERT_TRUE(C); 497 ASSERT_TRUE(C->containsError()); 498 EXPECT_EQ(1U, Diags.getNumErrors()); 499 EXPECT_STREQ("configuration file './root.cfg' cannot be opened: cannot get " 500 "absolute path", 501 DiagConsumer->Errors[0].c_str()); 502 } 503 504 TEST(ToolChainTest, BadConfigFile) { 505 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 506 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 507 std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer( 508 new SimpleDiagnosticConsumer()); 509 DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false); 510 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( 511 new llvm::vfs::InMemoryFileSystem); 512 513 #ifdef _WIN32 514 const char *TestRoot = "C:\\"; 515 #define FILENAME "C:/opt/root.cfg" 516 #define DIRNAME "C:/opt" 517 #else 518 const char *TestRoot = "/"; 519 #define FILENAME "/opt/root.cfg" 520 #define DIRNAME "/opt" 521 #endif 522 // UTF-16 string must be aligned on 2-byte boundary. Strings and char arrays 523 // do not provide necessary alignment, so copy constant string into properly 524 // allocated memory in heap. 525 llvm::BumpPtrAllocator Alloc; 526 char *StrBuff = (char *)Alloc.Allocate(16, 4); 527 std::memset(StrBuff, 0, 16); 528 std::memcpy(StrBuff, "\xFF\xFE\x00\xD8\x00\x00", 6); 529 StringRef BadUTF(StrBuff, 6); 530 FS->setCurrentWorkingDirectory(TestRoot); 531 FS->addFile("/opt/root.cfg", 0, llvm::MemoryBuffer::getMemBuffer(BadUTF)); 532 FS->addFile("/home/user/test.cfg", 0, 533 llvm::MemoryBuffer::getMemBuffer("@file.rsp")); 534 535 { 536 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 537 "clang LLVM compiler", FS); 538 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 539 {"/home/test/bin/clang", "--config", "/opt/root.cfg", "--version"})); 540 ASSERT_TRUE(C); 541 ASSERT_TRUE(C->containsError()); 542 EXPECT_EQ(1U, DiagConsumer->Errors.size()); 543 EXPECT_STREQ("cannot read configuration file '" FILENAME 544 "': Could not convert UTF16 to UTF8", 545 DiagConsumer->Errors[0].c_str()); 546 } 547 DiagConsumer->clear(); 548 { 549 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 550 "clang LLVM compiler", FS); 551 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 552 {"/home/test/bin/clang", "--config", "/opt", "--version"})); 553 ASSERT_TRUE(C); 554 ASSERT_TRUE(C->containsError()); 555 EXPECT_EQ(1U, DiagConsumer->Errors.size()); 556 EXPECT_STREQ("configuration file '" DIRNAME 557 "' cannot be opened: not a regular file", 558 DiagConsumer->Errors[0].c_str()); 559 } 560 DiagConsumer->clear(); 561 { 562 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 563 "clang LLVM compiler", FS); 564 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 565 {"/home/test/bin/clang", "--config", "root", 566 "--config-system-dir=", "--config-user-dir=", "--version"})); 567 ASSERT_TRUE(C); 568 ASSERT_TRUE(C->containsError()); 569 EXPECT_EQ(1U, DiagConsumer->Errors.size()); 570 EXPECT_STREQ("configuration file 'root' cannot be found", 571 DiagConsumer->Errors[0].c_str()); 572 } 573 574 #undef FILENAME 575 #undef DIRNAME 576 } 577 578 TEST(ToolChainTest, ConfigInexistentInclude) { 579 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 580 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 581 std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer( 582 new SimpleDiagnosticConsumer()); 583 DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false); 584 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( 585 new llvm::vfs::InMemoryFileSystem); 586 587 #ifdef _WIN32 588 const char *TestRoot = "C:\\"; 589 #define USERCONFIG "C:\\home\\user\\test.cfg" 590 #define UNEXISTENT "C:\\home\\user\\file.rsp" 591 #else 592 const char *TestRoot = "/"; 593 #define USERCONFIG "/home/user/test.cfg" 594 #define UNEXISTENT "/home/user/file.rsp" 595 #endif 596 FS->setCurrentWorkingDirectory(TestRoot); 597 FS->addFile("/home/user/test.cfg", 0, 598 llvm::MemoryBuffer::getMemBuffer("@file.rsp")); 599 600 { 601 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 602 "clang LLVM compiler", FS); 603 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 604 {"/home/test/bin/clang", "--config", "test.cfg", 605 "--config-system-dir=", "--config-user-dir=/home/user", "--version"})); 606 ASSERT_TRUE(C); 607 ASSERT_TRUE(C->containsError()); 608 EXPECT_EQ(1U, DiagConsumer->Errors.size()); 609 EXPECT_STRCASEEQ("cannot read configuration file '" USERCONFIG 610 "': cannot not open file '" UNEXISTENT 611 "': no such file or directory", 612 DiagConsumer->Errors[0].c_str()); 613 } 614 615 #undef USERCONFIG 616 #undef UNEXISTENT 617 } 618 619 TEST(ToolChainTest, ConfigRecursiveInclude) { 620 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 621 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 622 std::unique_ptr<SimpleDiagnosticConsumer> DiagConsumer( 623 new SimpleDiagnosticConsumer()); 624 DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagConsumer.get(), false); 625 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( 626 new llvm::vfs::InMemoryFileSystem); 627 628 #ifdef _WIN32 629 const char *TestRoot = "C:\\"; 630 #define USERCONFIG "C:\\home\\user\\test.cfg" 631 #define INCLUDED1 "C:\\home\\user\\file1.cfg" 632 #else 633 const char *TestRoot = "/"; 634 #define USERCONFIG "/home/user/test.cfg" 635 #define INCLUDED1 "/home/user/file1.cfg" 636 #endif 637 FS->setCurrentWorkingDirectory(TestRoot); 638 FS->addFile("/home/user/test.cfg", 0, 639 llvm::MemoryBuffer::getMemBuffer("@file1.cfg")); 640 FS->addFile("/home/user/file1.cfg", 0, 641 llvm::MemoryBuffer::getMemBuffer("@file2.cfg")); 642 FS->addFile("/home/user/file2.cfg", 0, 643 llvm::MemoryBuffer::getMemBuffer("@file3.cfg")); 644 FS->addFile("/home/user/file3.cfg", 0, 645 llvm::MemoryBuffer::getMemBuffer("@file1.cfg")); 646 647 { 648 Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, 649 "clang LLVM compiler", FS); 650 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 651 {"/home/test/bin/clang", "--config", "test.cfg", 652 "--config-system-dir=", "--config-user-dir=/home/user", "--version"})); 653 ASSERT_TRUE(C); 654 ASSERT_TRUE(C->containsError()); 655 EXPECT_EQ(1U, DiagConsumer->Errors.size()); 656 EXPECT_STREQ("cannot read configuration file '" USERCONFIG 657 "': recursive expansion of: '" INCLUDED1 "'", 658 DiagConsumer->Errors[0].c_str()); 659 } 660 661 #undef USERCONFIG 662 #undef INCLUDED1 663 } 664 665 TEST(ToolChainTest, NestedConfigFile) { 666 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 667 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 668 struct TestDiagnosticConsumer : public DiagnosticConsumer {}; 669 DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); 670 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( 671 new llvm::vfs::InMemoryFileSystem); 672 673 #ifdef _WIN32 674 const char *TestRoot = "C:\\"; 675 #else 676 const char *TestRoot = "/"; 677 #endif 678 FS->setCurrentWorkingDirectory(TestRoot); 679 680 FS->addFile("/opt/sdk/root.cfg", 0, 681 llvm::MemoryBuffer::getMemBuffer("--config=platform.cfg\n")); 682 FS->addFile("/opt/sdk/platform.cfg", 0, 683 llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-sys\n")); 684 FS->addFile("/home/test/bin/platform.cfg", 0, 685 llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-bin\n")); 686 687 SmallString<128> ClangExecutable("/home/test/bin/clang"); 688 FS->makeAbsolute(ClangExecutable); 689 690 // User file is absent - use system definitions. 691 { 692 Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags, 693 "clang LLVM compiler", FS); 694 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 695 {"/home/test/bin/clang", "--config", "root.cfg", 696 "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"})); 697 ASSERT_TRUE(C); 698 ASSERT_FALSE(C->containsError()); 699 EXPECT_EQ("/platform-sys", TheDriver.SysRoot); 700 } 701 702 // User file overrides system definitions. 703 FS->addFile("/home/test/sdk/platform.cfg", 0, 704 llvm::MemoryBuffer::getMemBuffer("--sysroot=/platform-user\n")); 705 { 706 Driver TheDriver(ClangExecutable, "arm-linux-gnueabi", Diags, 707 "clang LLVM compiler", FS); 708 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation( 709 {"/home/test/bin/clang", "--config", "root.cfg", 710 "--config-system-dir=/opt/sdk", "--config-user-dir=/home/test/sdk"})); 711 ASSERT_TRUE(C); 712 ASSERT_FALSE(C->containsError()); 713 EXPECT_EQ("/platform-user", TheDriver.SysRoot); 714 } 715 } 716 717 } // end anonymous namespace. 718