1 //===- llvm/unittest/Support/CommandLineTest.cpp - CommandLine tests ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/Support/CommandLine.h" 11 #include "llvm/ADT/STLExtras.h" 12 #include "llvm/ADT/SmallString.h" 13 #include "llvm/ADT/Triple.h" 14 #include "llvm/Config/config.h" 15 #include "llvm/Support/FileSystem.h" 16 #include "llvm/Support/InitLLVM.h" 17 #include "llvm/Support/Path.h" 18 #include "llvm/Support/Program.h" 19 #include "llvm/Support/StringSaver.h" 20 #include "gtest/gtest.h" 21 #include <fstream> 22 #include <stdlib.h> 23 #include <string> 24 25 using namespace llvm; 26 27 namespace { 28 29 class TempEnvVar { 30 public: 31 TempEnvVar(const char *name, const char *value) 32 : name(name) { 33 const char *old_value = getenv(name); 34 EXPECT_EQ(nullptr, old_value) << old_value; 35 #if HAVE_SETENV 36 setenv(name, value, true); 37 #else 38 # define SKIP_ENVIRONMENT_TESTS 39 #endif 40 } 41 42 ~TempEnvVar() { 43 #if HAVE_SETENV 44 // Assume setenv and unsetenv come together. 45 unsetenv(name); 46 #else 47 (void)name; // Suppress -Wunused-private-field. 48 #endif 49 } 50 51 private: 52 const char *const name; 53 }; 54 55 template <typename T, typename Base = cl::opt<T>> 56 class StackOption : public Base { 57 public: 58 template <class... Ts> 59 explicit StackOption(Ts &&... Ms) : Base(std::forward<Ts>(Ms)...) {} 60 61 ~StackOption() override { this->removeArgument(); } 62 63 template <class DT> StackOption<T> &operator=(const DT &V) { 64 this->setValue(V); 65 return *this; 66 } 67 }; 68 69 class StackSubCommand : public cl::SubCommand { 70 public: 71 StackSubCommand(StringRef Name, 72 StringRef Description = StringRef()) 73 : SubCommand(Name, Description) {} 74 75 StackSubCommand() : SubCommand() {} 76 77 ~StackSubCommand() { unregisterSubCommand(); } 78 }; 79 80 81 cl::OptionCategory TestCategory("Test Options", "Description"); 82 TEST(CommandLineTest, ModifyExisitingOption) { 83 StackOption<int> TestOption("test-option", cl::desc("old description")); 84 85 static const char Description[] = "New description"; 86 static const char ArgString[] = "new-test-option"; 87 static const char ValueString[] = "Integer"; 88 89 StringMap<cl::Option *> &Map = 90 cl::getRegisteredOptions(*cl::TopLevelSubCommand); 91 92 ASSERT_TRUE(Map.count("test-option") == 1) << 93 "Could not find option in map."; 94 95 cl::Option *Retrieved = Map["test-option"]; 96 ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option."; 97 98 ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) << 99 "Incorrect default option category."; 100 101 Retrieved->setCategory(TestCategory); 102 ASSERT_EQ(&TestCategory,Retrieved->Category) << 103 "Failed to modify option's option category."; 104 105 Retrieved->setDescription(Description); 106 ASSERT_STREQ(Retrieved->HelpStr.data(), Description) 107 << "Changing option description failed."; 108 109 Retrieved->setArgStr(ArgString); 110 ASSERT_STREQ(ArgString, Retrieved->ArgStr.data()) 111 << "Failed to modify option's Argument string."; 112 113 Retrieved->setValueStr(ValueString); 114 ASSERT_STREQ(Retrieved->ValueStr.data(), ValueString) 115 << "Failed to modify option's Value string."; 116 117 Retrieved->setHiddenFlag(cl::Hidden); 118 ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) << 119 "Failed to modify option's hidden flag."; 120 } 121 #ifndef SKIP_ENVIRONMENT_TESTS 122 123 const char test_env_var[] = "LLVM_TEST_COMMAND_LINE_FLAGS"; 124 125 cl::opt<std::string> EnvironmentTestOption("env-test-opt"); 126 TEST(CommandLineTest, ParseEnvironment) { 127 TempEnvVar TEV(test_env_var, "-env-test-opt=hello"); 128 EXPECT_EQ("", EnvironmentTestOption); 129 cl::ParseEnvironmentOptions("CommandLineTest", test_env_var); 130 EXPECT_EQ("hello", EnvironmentTestOption); 131 } 132 133 // This test used to make valgrind complain 134 // ("Conditional jump or move depends on uninitialised value(s)") 135 // 136 // Warning: Do not run any tests after this one that try to gain access to 137 // registered command line options because this will likely result in a 138 // SEGFAULT. This can occur because the cl::opt in the test below is declared 139 // on the stack which will be destroyed after the test completes but the 140 // command line system will still hold a pointer to a deallocated cl::Option. 141 TEST(CommandLineTest, ParseEnvironmentToLocalVar) { 142 // Put cl::opt on stack to check for proper initialization of fields. 143 StackOption<std::string> EnvironmentTestOptionLocal("env-test-opt-local"); 144 TempEnvVar TEV(test_env_var, "-env-test-opt-local=hello-local"); 145 EXPECT_EQ("", EnvironmentTestOptionLocal); 146 cl::ParseEnvironmentOptions("CommandLineTest", test_env_var); 147 EXPECT_EQ("hello-local", EnvironmentTestOptionLocal); 148 } 149 150 #endif // SKIP_ENVIRONMENT_TESTS 151 152 TEST(CommandLineTest, UseOptionCategory) { 153 StackOption<int> TestOption2("test-option", cl::cat(TestCategory)); 154 155 ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option " 156 "Category."; 157 } 158 159 typedef void ParserFunction(StringRef Source, StringSaver &Saver, 160 SmallVectorImpl<const char *> &NewArgv, 161 bool MarkEOLs); 162 163 void testCommandLineTokenizer(ParserFunction *parse, StringRef Input, 164 const char *const Output[], size_t OutputSize) { 165 SmallVector<const char *, 0> Actual; 166 BumpPtrAllocator A; 167 StringSaver Saver(A); 168 parse(Input, Saver, Actual, /*MarkEOLs=*/false); 169 EXPECT_EQ(OutputSize, Actual.size()); 170 for (unsigned I = 0, E = Actual.size(); I != E; ++I) { 171 if (I < OutputSize) { 172 EXPECT_STREQ(Output[I], Actual[I]); 173 } 174 } 175 } 176 177 TEST(CommandLineTest, TokenizeGNUCommandLine) { 178 const char Input[] = 179 "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) " 180 "foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\""; 181 const char *const Output[] = { 182 "foo bar", "foo bar", "foo bar", "foo\\bar", 183 "-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"}; 184 testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output, 185 array_lengthof(Output)); 186 } 187 188 TEST(CommandLineTest, TokenizeWindowsCommandLine) { 189 const char Input[] = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr " 190 "\"st \\\"u\" \\v"; 191 const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k", 192 "lmn", "o", "pqr", "st \"u", "\\v" }; 193 testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output, 194 array_lengthof(Output)); 195 } 196 197 TEST(CommandLineTest, TokenizeConfigFile1) { 198 const char *Input = "\\"; 199 const char *const Output[] = { "\\" }; 200 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 201 array_lengthof(Output)); 202 } 203 204 TEST(CommandLineTest, TokenizeConfigFile2) { 205 const char *Input = "\\abc"; 206 const char *const Output[] = { "abc" }; 207 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 208 array_lengthof(Output)); 209 } 210 211 TEST(CommandLineTest, TokenizeConfigFile3) { 212 const char *Input = "abc\\"; 213 const char *const Output[] = { "abc\\" }; 214 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 215 array_lengthof(Output)); 216 } 217 218 TEST(CommandLineTest, TokenizeConfigFile4) { 219 const char *Input = "abc\\\n123"; 220 const char *const Output[] = { "abc123" }; 221 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 222 array_lengthof(Output)); 223 } 224 225 TEST(CommandLineTest, TokenizeConfigFile5) { 226 const char *Input = "abc\\\r\n123"; 227 const char *const Output[] = { "abc123" }; 228 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 229 array_lengthof(Output)); 230 } 231 232 TEST(CommandLineTest, TokenizeConfigFile6) { 233 const char *Input = "abc\\\n"; 234 const char *const Output[] = { "abc" }; 235 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 236 array_lengthof(Output)); 237 } 238 239 TEST(CommandLineTest, TokenizeConfigFile7) { 240 const char *Input = "abc\\\r\n"; 241 const char *const Output[] = { "abc" }; 242 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 243 array_lengthof(Output)); 244 } 245 246 TEST(CommandLineTest, TokenizeConfigFile8) { 247 SmallVector<const char *, 0> Actual; 248 BumpPtrAllocator A; 249 StringSaver Saver(A); 250 cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false); 251 EXPECT_TRUE(Actual.empty()); 252 } 253 254 TEST(CommandLineTest, TokenizeConfigFile9) { 255 SmallVector<const char *, 0> Actual; 256 BumpPtrAllocator A; 257 StringSaver Saver(A); 258 cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false); 259 EXPECT_TRUE(Actual.empty()); 260 } 261 262 TEST(CommandLineTest, TokenizeConfigFile10) { 263 const char *Input = "\\\nabc"; 264 const char *const Output[] = { "abc" }; 265 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 266 array_lengthof(Output)); 267 } 268 269 TEST(CommandLineTest, TokenizeConfigFile11) { 270 const char *Input = "\\\r\nabc"; 271 const char *const Output[] = { "abc" }; 272 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 273 array_lengthof(Output)); 274 } 275 276 TEST(CommandLineTest, AliasesWithArguments) { 277 static const size_t ARGC = 3; 278 const char *const Inputs[][ARGC] = { 279 { "-tool", "-actual=x", "-extra" }, 280 { "-tool", "-actual", "x" }, 281 { "-tool", "-alias=x", "-extra" }, 282 { "-tool", "-alias", "x" } 283 }; 284 285 for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) { 286 StackOption<std::string> Actual("actual"); 287 StackOption<bool> Extra("extra"); 288 StackOption<std::string> Input(cl::Positional); 289 290 cl::alias Alias("alias", llvm::cl::aliasopt(Actual)); 291 292 cl::ParseCommandLineOptions(ARGC, Inputs[i]); 293 EXPECT_EQ("x", Actual); 294 EXPECT_EQ(0, Input.getNumOccurrences()); 295 296 Alias.removeArgument(); 297 } 298 } 299 300 void testAliasRequired(int argc, const char *const *argv) { 301 StackOption<std::string> Option("option", cl::Required); 302 cl::alias Alias("o", llvm::cl::aliasopt(Option)); 303 304 cl::ParseCommandLineOptions(argc, argv); 305 EXPECT_EQ("x", Option); 306 EXPECT_EQ(1, Option.getNumOccurrences()); 307 308 Alias.removeArgument(); 309 } 310 311 TEST(CommandLineTest, AliasRequired) { 312 const char *opts1[] = { "-tool", "-option=x" }; 313 const char *opts2[] = { "-tool", "-o", "x" }; 314 testAliasRequired(array_lengthof(opts1), opts1); 315 testAliasRequired(array_lengthof(opts2), opts2); 316 } 317 318 TEST(CommandLineTest, HideUnrelatedOptions) { 319 StackOption<int> TestOption1("hide-option-1"); 320 StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory)); 321 322 cl::HideUnrelatedOptions(TestCategory); 323 324 ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag()) 325 << "Failed to hide extra option."; 326 ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag()) 327 << "Hid extra option that should be visable."; 328 329 StringMap<cl::Option *> &Map = 330 cl::getRegisteredOptions(*cl::TopLevelSubCommand); 331 ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag()) 332 << "Hid default option that should be visable."; 333 } 334 335 cl::OptionCategory TestCategory2("Test Options set 2", "Description"); 336 337 TEST(CommandLineTest, HideUnrelatedOptionsMulti) { 338 StackOption<int> TestOption1("multi-hide-option-1"); 339 StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory)); 340 StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2)); 341 342 const cl::OptionCategory *VisibleCategories[] = {&TestCategory, 343 &TestCategory2}; 344 345 cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories)); 346 347 ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag()) 348 << "Failed to hide extra option."; 349 ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag()) 350 << "Hid extra option that should be visable."; 351 ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag()) 352 << "Hid extra option that should be visable."; 353 354 StringMap<cl::Option *> &Map = 355 cl::getRegisteredOptions(*cl::TopLevelSubCommand); 356 ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag()) 357 << "Hid default option that should be visable."; 358 } 359 360 TEST(CommandLineTest, SetValueInSubcategories) { 361 cl::ResetCommandLineParser(); 362 363 StackSubCommand SC1("sc1", "First subcommand"); 364 StackSubCommand SC2("sc2", "Second subcommand"); 365 366 StackOption<bool> TopLevelOpt("top-level", cl::init(false)); 367 StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); 368 StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); 369 370 EXPECT_FALSE(TopLevelOpt); 371 EXPECT_FALSE(SC1Opt); 372 EXPECT_FALSE(SC2Opt); 373 const char *args[] = {"prog", "-top-level"}; 374 EXPECT_TRUE( 375 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 376 EXPECT_TRUE(TopLevelOpt); 377 EXPECT_FALSE(SC1Opt); 378 EXPECT_FALSE(SC2Opt); 379 380 TopLevelOpt = false; 381 382 cl::ResetAllOptionOccurrences(); 383 EXPECT_FALSE(TopLevelOpt); 384 EXPECT_FALSE(SC1Opt); 385 EXPECT_FALSE(SC2Opt); 386 const char *args2[] = {"prog", "sc1", "-sc1"}; 387 EXPECT_TRUE( 388 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 389 EXPECT_FALSE(TopLevelOpt); 390 EXPECT_TRUE(SC1Opt); 391 EXPECT_FALSE(SC2Opt); 392 393 SC1Opt = false; 394 395 cl::ResetAllOptionOccurrences(); 396 EXPECT_FALSE(TopLevelOpt); 397 EXPECT_FALSE(SC1Opt); 398 EXPECT_FALSE(SC2Opt); 399 const char *args3[] = {"prog", "sc2", "-sc2"}; 400 EXPECT_TRUE( 401 cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls())); 402 EXPECT_FALSE(TopLevelOpt); 403 EXPECT_FALSE(SC1Opt); 404 EXPECT_TRUE(SC2Opt); 405 } 406 407 TEST(CommandLineTest, LookupFailsInWrongSubCommand) { 408 cl::ResetCommandLineParser(); 409 410 StackSubCommand SC1("sc1", "First subcommand"); 411 StackSubCommand SC2("sc2", "Second subcommand"); 412 413 StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); 414 StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); 415 416 std::string Errs; 417 raw_string_ostream OS(Errs); 418 419 const char *args[] = {"prog", "sc1", "-sc2"}; 420 EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); 421 OS.flush(); 422 EXPECT_FALSE(Errs.empty()); 423 } 424 425 TEST(CommandLineTest, AddToAllSubCommands) { 426 cl::ResetCommandLineParser(); 427 428 StackSubCommand SC1("sc1", "First subcommand"); 429 StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands), 430 cl::init(false)); 431 StackSubCommand SC2("sc2", "Second subcommand"); 432 433 const char *args[] = {"prog", "-everywhere"}; 434 const char *args2[] = {"prog", "sc1", "-everywhere"}; 435 const char *args3[] = {"prog", "sc2", "-everywhere"}; 436 437 std::string Errs; 438 raw_string_ostream OS(Errs); 439 440 EXPECT_FALSE(AllOpt); 441 EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); 442 EXPECT_TRUE(AllOpt); 443 444 AllOpt = false; 445 446 cl::ResetAllOptionOccurrences(); 447 EXPECT_FALSE(AllOpt); 448 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); 449 EXPECT_TRUE(AllOpt); 450 451 AllOpt = false; 452 453 cl::ResetAllOptionOccurrences(); 454 EXPECT_FALSE(AllOpt); 455 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); 456 EXPECT_TRUE(AllOpt); 457 458 // Since all parsing succeeded, the error message should be empty. 459 OS.flush(); 460 EXPECT_TRUE(Errs.empty()); 461 } 462 463 TEST(CommandLineTest, ReparseCommandLineOptions) { 464 cl::ResetCommandLineParser(); 465 466 StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand), 467 cl::init(false)); 468 469 const char *args[] = {"prog", "-top-level"}; 470 471 EXPECT_FALSE(TopLevelOpt); 472 EXPECT_TRUE( 473 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 474 EXPECT_TRUE(TopLevelOpt); 475 476 TopLevelOpt = false; 477 478 cl::ResetAllOptionOccurrences(); 479 EXPECT_FALSE(TopLevelOpt); 480 EXPECT_TRUE( 481 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 482 EXPECT_TRUE(TopLevelOpt); 483 } 484 485 TEST(CommandLineTest, RemoveFromRegularSubCommand) { 486 cl::ResetCommandLineParser(); 487 488 StackSubCommand SC("sc", "Subcommand"); 489 StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false)); 490 StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false)); 491 492 const char *args[] = {"prog", "sc", "-remove-option"}; 493 494 std::string Errs; 495 raw_string_ostream OS(Errs); 496 497 EXPECT_FALSE(RemoveOption); 498 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); 499 EXPECT_TRUE(RemoveOption); 500 OS.flush(); 501 EXPECT_TRUE(Errs.empty()); 502 503 RemoveOption.removeArgument(); 504 505 cl::ResetAllOptionOccurrences(); 506 EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); 507 OS.flush(); 508 EXPECT_FALSE(Errs.empty()); 509 } 510 511 TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { 512 cl::ResetCommandLineParser(); 513 514 StackOption<bool> TopLevelRemove( 515 "top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false)); 516 StackOption<bool> TopLevelKeep( 517 "top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false)); 518 519 const char *args[] = {"prog", "-top-level-remove"}; 520 521 EXPECT_FALSE(TopLevelRemove); 522 EXPECT_TRUE( 523 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 524 EXPECT_TRUE(TopLevelRemove); 525 526 TopLevelRemove.removeArgument(); 527 528 cl::ResetAllOptionOccurrences(); 529 EXPECT_FALSE( 530 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 531 } 532 533 TEST(CommandLineTest, RemoveFromAllSubCommands) { 534 cl::ResetCommandLineParser(); 535 536 StackSubCommand SC1("sc1", "First Subcommand"); 537 StackSubCommand SC2("sc2", "Second Subcommand"); 538 StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands), 539 cl::init(false)); 540 StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands), 541 cl::init(false)); 542 543 const char *args0[] = {"prog", "-remove-option"}; 544 const char *args1[] = {"prog", "sc1", "-remove-option"}; 545 const char *args2[] = {"prog", "sc2", "-remove-option"}; 546 547 // It should work for all subcommands including the top-level. 548 EXPECT_FALSE(RemoveOption); 549 EXPECT_TRUE( 550 cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); 551 EXPECT_TRUE(RemoveOption); 552 553 RemoveOption = false; 554 555 cl::ResetAllOptionOccurrences(); 556 EXPECT_FALSE(RemoveOption); 557 EXPECT_TRUE( 558 cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); 559 EXPECT_TRUE(RemoveOption); 560 561 RemoveOption = false; 562 563 cl::ResetAllOptionOccurrences(); 564 EXPECT_FALSE(RemoveOption); 565 EXPECT_TRUE( 566 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 567 EXPECT_TRUE(RemoveOption); 568 569 RemoveOption.removeArgument(); 570 571 // It should not work for any subcommands including the top-level. 572 cl::ResetAllOptionOccurrences(); 573 EXPECT_FALSE( 574 cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); 575 cl::ResetAllOptionOccurrences(); 576 EXPECT_FALSE( 577 cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); 578 cl::ResetAllOptionOccurrences(); 579 EXPECT_FALSE( 580 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 581 } 582 583 TEST(CommandLineTest, GetRegisteredSubcommands) { 584 cl::ResetCommandLineParser(); 585 586 StackSubCommand SC1("sc1", "First Subcommand"); 587 StackOption<bool> Opt1("opt1", cl::sub(SC1), cl::init(false)); 588 StackSubCommand SC2("sc2", "Second subcommand"); 589 StackOption<bool> Opt2("opt2", cl::sub(SC2), cl::init(false)); 590 591 const char *args0[] = {"prog", "sc1"}; 592 const char *args1[] = {"prog", "sc2"}; 593 594 EXPECT_TRUE( 595 cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); 596 EXPECT_FALSE(Opt1); 597 EXPECT_FALSE(Opt2); 598 for (auto *S : cl::getRegisteredSubcommands()) { 599 if (*S) { 600 EXPECT_EQ("sc1", S->getName()); 601 } 602 } 603 604 cl::ResetAllOptionOccurrences(); 605 EXPECT_TRUE( 606 cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls())); 607 EXPECT_FALSE(Opt1); 608 EXPECT_FALSE(Opt2); 609 for (auto *S : cl::getRegisteredSubcommands()) { 610 if (*S) { 611 EXPECT_EQ("sc2", S->getName()); 612 } 613 } 614 } 615 616 TEST(CommandLineTest, ArgumentLimit) { 617 std::string args(32 * 4096, 'a'); 618 EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data())); 619 } 620 621 TEST(CommandLineTest, ResponseFileWindows) { 622 if (!Triple(sys::getProcessTriple()).isOSWindows()) 623 return; 624 625 StackOption<std::string, cl::list<std::string>> InputFilenames( 626 cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore); 627 StackOption<bool> TopLevelOpt("top-level", cl::init(false)); 628 629 // Create response file. 630 int FileDescriptor; 631 SmallString<64> TempPath; 632 std::error_code EC = 633 llvm::sys::fs::createTemporaryFile("resp-", ".txt", FileDescriptor, TempPath); 634 EXPECT_TRUE(!EC); 635 636 std::ofstream RspFile(TempPath.c_str()); 637 EXPECT_TRUE(RspFile.is_open()); 638 RspFile << "-top-level\npath\\dir\\file1\npath/dir/file2"; 639 RspFile.close(); 640 641 llvm::SmallString<128> RspOpt; 642 RspOpt.append(1, '@'); 643 RspOpt.append(TempPath.c_str()); 644 const char *args[] = {"prog", RspOpt.c_str()}; 645 EXPECT_FALSE(TopLevelOpt); 646 EXPECT_TRUE( 647 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 648 EXPECT_TRUE(TopLevelOpt); 649 EXPECT_TRUE(InputFilenames[0] == "path\\dir\\file1"); 650 EXPECT_TRUE(InputFilenames[1] == "path/dir/file2"); 651 652 llvm::sys::fs::remove(TempPath.c_str()); 653 } 654 655 TEST(CommandLineTest, ResponseFiles) { 656 llvm::SmallString<128> TestDir; 657 std::error_code EC = 658 llvm::sys::fs::createUniqueDirectory("unittest", TestDir); 659 EXPECT_TRUE(!EC); 660 661 // Create included response file of first level. 662 llvm::SmallString<128> IncludedFileName; 663 llvm::sys::path::append(IncludedFileName, TestDir, "resp1"); 664 std::ofstream IncludedFile(IncludedFileName.c_str()); 665 EXPECT_TRUE(IncludedFile.is_open()); 666 IncludedFile << "-option_1 -option_2\n" 667 "@incdir/resp2\n" 668 "-option_3=abcd\n"; 669 IncludedFile.close(); 670 671 // Directory for included file. 672 llvm::SmallString<128> IncDir; 673 llvm::sys::path::append(IncDir, TestDir, "incdir"); 674 EC = llvm::sys::fs::create_directory(IncDir); 675 EXPECT_TRUE(!EC); 676 677 // Create included response file of second level. 678 llvm::SmallString<128> IncludedFileName2; 679 llvm::sys::path::append(IncludedFileName2, IncDir, "resp2"); 680 std::ofstream IncludedFile2(IncludedFileName2.c_str()); 681 EXPECT_TRUE(IncludedFile2.is_open()); 682 IncludedFile2 << "-option_21 -option_22\n"; 683 IncludedFile2 << "-option_23=abcd\n"; 684 IncludedFile2.close(); 685 686 // Prepare 'file' with reference to response file. 687 SmallString<128> IncRef; 688 IncRef.append(1, '@'); 689 IncRef.append(IncludedFileName.c_str()); 690 llvm::SmallVector<const char *, 4> Argv = 691 { "test/test", "-flag_1", IncRef.c_str(), "-flag_2" }; 692 693 // Expand response files. 694 llvm::BumpPtrAllocator A; 695 llvm::StringSaver Saver(A); 696 bool Res = llvm::cl::ExpandResponseFiles( 697 Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, true); 698 EXPECT_TRUE(Res); 699 EXPECT_EQ(Argv.size(), 9U); 700 EXPECT_STREQ(Argv[0], "test/test"); 701 EXPECT_STREQ(Argv[1], "-flag_1"); 702 EXPECT_STREQ(Argv[2], "-option_1"); 703 EXPECT_STREQ(Argv[3], "-option_2"); 704 EXPECT_STREQ(Argv[4], "-option_21"); 705 EXPECT_STREQ(Argv[5], "-option_22"); 706 EXPECT_STREQ(Argv[6], "-option_23=abcd"); 707 EXPECT_STREQ(Argv[7], "-option_3=abcd"); 708 EXPECT_STREQ(Argv[8], "-flag_2"); 709 710 llvm::sys::fs::remove(IncludedFileName2); 711 llvm::sys::fs::remove(IncDir); 712 llvm::sys::fs::remove(IncludedFileName); 713 llvm::sys::fs::remove(TestDir); 714 } 715 716 TEST(CommandLineTest, SetDefautValue) { 717 cl::ResetCommandLineParser(); 718 719 StackOption<std::string> Opt1("opt1", cl::init("true")); 720 StackOption<bool> Opt2("opt2", cl::init(true)); 721 cl::alias Alias("alias", llvm::cl::aliasopt(Opt2)); 722 StackOption<int> Opt3("opt3", cl::init(3)); 723 724 const char *args[] = {"prog", "-opt1=false", "-opt2", "-opt3"}; 725 726 EXPECT_TRUE( 727 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 728 729 EXPECT_TRUE(Opt1 == "false"); 730 EXPECT_TRUE(Opt2); 731 EXPECT_TRUE(Opt3 == 3); 732 733 Opt2 = false; 734 Opt3 = 1; 735 736 cl::ResetAllOptionOccurrences(); 737 738 for (auto &OM : cl::getRegisteredOptions(*cl::TopLevelSubCommand)) { 739 cl::Option *O = OM.second; 740 if (O->ArgStr == "opt2") { 741 continue; 742 } 743 O->setDefault(); 744 } 745 746 EXPECT_TRUE(Opt1 == "true"); 747 EXPECT_TRUE(Opt2); 748 EXPECT_TRUE(Opt3 == 3); 749 Alias.removeArgument(); 750 } 751 752 TEST(CommandLineTest, ReadConfigFile) { 753 llvm::SmallVector<const char *, 1> Argv; 754 755 llvm::SmallString<128> TestDir; 756 std::error_code EC = 757 llvm::sys::fs::createUniqueDirectory("unittest", TestDir); 758 EXPECT_TRUE(!EC); 759 760 llvm::SmallString<128> TestCfg; 761 llvm::sys::path::append(TestCfg, TestDir, "foo"); 762 std::ofstream ConfigFile(TestCfg.c_str()); 763 EXPECT_TRUE(ConfigFile.is_open()); 764 ConfigFile << "# Comment\n" 765 "-option_1\n" 766 "@subconfig\n" 767 "-option_3=abcd\n" 768 "-option_4=\\\n" 769 "cdef\n"; 770 ConfigFile.close(); 771 772 llvm::SmallString<128> TestCfg2; 773 llvm::sys::path::append(TestCfg2, TestDir, "subconfig"); 774 std::ofstream ConfigFile2(TestCfg2.c_str()); 775 EXPECT_TRUE(ConfigFile2.is_open()); 776 ConfigFile2 << "-option_2\n" 777 "\n" 778 " # comment\n"; 779 ConfigFile2.close(); 780 781 // Make sure the current directory is not the directory where config files 782 // resides. In this case the code that expands response files will not find 783 // 'subconfig' unless it resolves nested inclusions relative to the including 784 // file. 785 llvm::SmallString<128> CurrDir; 786 EC = llvm::sys::fs::current_path(CurrDir); 787 EXPECT_TRUE(!EC); 788 EXPECT_TRUE(StringRef(CurrDir) != StringRef(TestDir)); 789 790 llvm::BumpPtrAllocator A; 791 llvm::StringSaver Saver(A); 792 bool Result = llvm::cl::readConfigFile(TestCfg, Saver, Argv); 793 794 EXPECT_TRUE(Result); 795 EXPECT_EQ(Argv.size(), 4U); 796 EXPECT_STREQ(Argv[0], "-option_1"); 797 EXPECT_STREQ(Argv[1], "-option_2"); 798 EXPECT_STREQ(Argv[2], "-option_3=abcd"); 799 EXPECT_STREQ(Argv[3], "-option_4=cdef"); 800 801 llvm::sys::fs::remove(TestCfg2); 802 llvm::sys::fs::remove(TestCfg); 803 llvm::sys::fs::remove(TestDir); 804 } 805 806 TEST(CommandLineTest, PositionalEatArgsError) { 807 StackOption<std::string, cl::list<std::string>> PosEatArgs( 808 "positional-eat-args", cl::Positional, cl::desc("<arguments>..."), 809 cl::ZeroOrMore, cl::PositionalEatsArgs); 810 811 const char *args[] = {"prog", "-positional-eat-args=XXXX"}; 812 const char *args2[] = {"prog", "-positional-eat-args=XXXX", "-foo"}; 813 const char *args3[] = {"prog", "-positional-eat-args", "-foo"}; 814 815 std::string Errs; 816 raw_string_ostream OS(Errs); 817 EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); OS.flush(); 818 EXPECT_FALSE(Errs.empty()); Errs.clear(); 819 EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); OS.flush(); 820 EXPECT_FALSE(Errs.empty()); Errs.clear(); 821 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); OS.flush(); 822 EXPECT_TRUE(Errs.empty()); 823 } 824 825 #ifdef _WIN32 826 TEST(CommandLineTest, GetCommandLineArguments) { 827 int argc = __argc; 828 char **argv = __argv; 829 830 // GetCommandLineArguments is called in InitLLVM. 831 llvm::InitLLVM X(argc, argv); 832 833 EXPECT_EQ(llvm::sys::path::is_absolute(argv[0]), 834 llvm::sys::path::is_absolute(__argv[0])); 835 836 EXPECT_TRUE(llvm::sys::path::filename(argv[0]) 837 .equals_lower("supporttests.exe")) 838 << "Filename of test executable is " 839 << llvm::sys::path::filename(argv[0]); 840 } 841 #endif 842 843 } // anonymous namespace 844