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