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