1 //===- llvm/unittest/Support/CommandLineTest.cpp - CommandLine 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 #include "llvm/Support/CommandLine.h" 10 #include "llvm/ADT/STLExtras.h" 11 #include "llvm/ADT/SmallString.h" 12 #include "llvm/ADT/Triple.h" 13 #include "llvm/Config/config.h" 14 #include "llvm/Support/FileSystem.h" 15 #include "llvm/Support/InitLLVM.h" 16 #include "llvm/Support/MemoryBuffer.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, TokenizeWindowsCommandLine1) { 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, TokenizeWindowsCommandLine2) { 198 const char Input[] = "clang -c -DFOO=\"\"\"ABC\"\"\" x.cpp"; 199 const char *const Output[] = { "clang", "-c", "-DFOO=\"ABC\"", "x.cpp"}; 200 testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output, 201 array_lengthof(Output)); 202 } 203 204 TEST(CommandLineTest, TokenizeConfigFile1) { 205 const char *Input = "\\"; 206 const char *const Output[] = { "\\" }; 207 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 208 array_lengthof(Output)); 209 } 210 211 TEST(CommandLineTest, TokenizeConfigFile2) { 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, TokenizeConfigFile3) { 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, TokenizeConfigFile4) { 226 const char *Input = "abc\\\n123"; 227 const char *const Output[] = { "abc123" }; 228 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 229 array_lengthof(Output)); 230 } 231 232 TEST(CommandLineTest, TokenizeConfigFile5) { 233 const char *Input = "abc\\\r\n123"; 234 const char *const Output[] = { "abc123" }; 235 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 236 array_lengthof(Output)); 237 } 238 239 TEST(CommandLineTest, TokenizeConfigFile6) { 240 const char *Input = "abc\\\n"; 241 const char *const Output[] = { "abc" }; 242 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 243 array_lengthof(Output)); 244 } 245 246 TEST(CommandLineTest, TokenizeConfigFile7) { 247 const char *Input = "abc\\\r\n"; 248 const char *const Output[] = { "abc" }; 249 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 250 array_lengthof(Output)); 251 } 252 253 TEST(CommandLineTest, TokenizeConfigFile8) { 254 SmallVector<const char *, 0> Actual; 255 BumpPtrAllocator A; 256 StringSaver Saver(A); 257 cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false); 258 EXPECT_TRUE(Actual.empty()); 259 } 260 261 TEST(CommandLineTest, TokenizeConfigFile9) { 262 SmallVector<const char *, 0> Actual; 263 BumpPtrAllocator A; 264 StringSaver Saver(A); 265 cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false); 266 EXPECT_TRUE(Actual.empty()); 267 } 268 269 TEST(CommandLineTest, TokenizeConfigFile10) { 270 const char *Input = "\\\nabc"; 271 const char *const Output[] = { "abc" }; 272 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 273 array_lengthof(Output)); 274 } 275 276 TEST(CommandLineTest, TokenizeConfigFile11) { 277 const char *Input = "\\\r\nabc"; 278 const char *const Output[] = { "abc" }; 279 testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output, 280 array_lengthof(Output)); 281 } 282 283 TEST(CommandLineTest, AliasesWithArguments) { 284 static const size_t ARGC = 3; 285 const char *const Inputs[][ARGC] = { 286 { "-tool", "-actual=x", "-extra" }, 287 { "-tool", "-actual", "x" }, 288 { "-tool", "-alias=x", "-extra" }, 289 { "-tool", "-alias", "x" } 290 }; 291 292 for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) { 293 StackOption<std::string> Actual("actual"); 294 StackOption<bool> Extra("extra"); 295 StackOption<std::string> Input(cl::Positional); 296 297 cl::alias Alias("alias", llvm::cl::aliasopt(Actual)); 298 299 cl::ParseCommandLineOptions(ARGC, Inputs[i]); 300 EXPECT_EQ("x", Actual); 301 EXPECT_EQ(0, Input.getNumOccurrences()); 302 303 Alias.removeArgument(); 304 } 305 } 306 307 void testAliasRequired(int argc, const char *const *argv) { 308 StackOption<std::string> Option("option", cl::Required); 309 cl::alias Alias("o", llvm::cl::aliasopt(Option)); 310 311 cl::ParseCommandLineOptions(argc, argv); 312 EXPECT_EQ("x", Option); 313 EXPECT_EQ(1, Option.getNumOccurrences()); 314 315 Alias.removeArgument(); 316 } 317 318 TEST(CommandLineTest, AliasRequired) { 319 const char *opts1[] = { "-tool", "-option=x" }; 320 const char *opts2[] = { "-tool", "-o", "x" }; 321 testAliasRequired(array_lengthof(opts1), opts1); 322 testAliasRequired(array_lengthof(opts2), opts2); 323 } 324 325 TEST(CommandLineTest, HideUnrelatedOptions) { 326 StackOption<int> TestOption1("hide-option-1"); 327 StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory)); 328 329 cl::HideUnrelatedOptions(TestCategory); 330 331 ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag()) 332 << "Failed to hide extra option."; 333 ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag()) 334 << "Hid extra option that should be visable."; 335 336 StringMap<cl::Option *> &Map = 337 cl::getRegisteredOptions(*cl::TopLevelSubCommand); 338 ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag()) 339 << "Hid default option that should be visable."; 340 } 341 342 cl::OptionCategory TestCategory2("Test Options set 2", "Description"); 343 344 TEST(CommandLineTest, HideUnrelatedOptionsMulti) { 345 StackOption<int> TestOption1("multi-hide-option-1"); 346 StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory)); 347 StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2)); 348 349 const cl::OptionCategory *VisibleCategories[] = {&TestCategory, 350 &TestCategory2}; 351 352 cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories)); 353 354 ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag()) 355 << "Failed to hide extra option."; 356 ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag()) 357 << "Hid extra option that should be visable."; 358 ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag()) 359 << "Hid extra option that should be visable."; 360 361 StringMap<cl::Option *> &Map = 362 cl::getRegisteredOptions(*cl::TopLevelSubCommand); 363 ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag()) 364 << "Hid default option that should be visable."; 365 } 366 367 TEST(CommandLineTest, SetValueInSubcategories) { 368 cl::ResetCommandLineParser(); 369 370 StackSubCommand SC1("sc1", "First subcommand"); 371 StackSubCommand SC2("sc2", "Second subcommand"); 372 373 StackOption<bool> TopLevelOpt("top-level", cl::init(false)); 374 StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); 375 StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); 376 377 EXPECT_FALSE(TopLevelOpt); 378 EXPECT_FALSE(SC1Opt); 379 EXPECT_FALSE(SC2Opt); 380 const char *args[] = {"prog", "-top-level"}; 381 EXPECT_TRUE( 382 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 383 EXPECT_TRUE(TopLevelOpt); 384 EXPECT_FALSE(SC1Opt); 385 EXPECT_FALSE(SC2Opt); 386 387 TopLevelOpt = false; 388 389 cl::ResetAllOptionOccurrences(); 390 EXPECT_FALSE(TopLevelOpt); 391 EXPECT_FALSE(SC1Opt); 392 EXPECT_FALSE(SC2Opt); 393 const char *args2[] = {"prog", "sc1", "-sc1"}; 394 EXPECT_TRUE( 395 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 396 EXPECT_FALSE(TopLevelOpt); 397 EXPECT_TRUE(SC1Opt); 398 EXPECT_FALSE(SC2Opt); 399 400 SC1Opt = false; 401 402 cl::ResetAllOptionOccurrences(); 403 EXPECT_FALSE(TopLevelOpt); 404 EXPECT_FALSE(SC1Opt); 405 EXPECT_FALSE(SC2Opt); 406 const char *args3[] = {"prog", "sc2", "-sc2"}; 407 EXPECT_TRUE( 408 cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls())); 409 EXPECT_FALSE(TopLevelOpt); 410 EXPECT_FALSE(SC1Opt); 411 EXPECT_TRUE(SC2Opt); 412 } 413 414 TEST(CommandLineTest, LookupFailsInWrongSubCommand) { 415 cl::ResetCommandLineParser(); 416 417 StackSubCommand SC1("sc1", "First subcommand"); 418 StackSubCommand SC2("sc2", "Second subcommand"); 419 420 StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false)); 421 StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false)); 422 423 std::string Errs; 424 raw_string_ostream OS(Errs); 425 426 const char *args[] = {"prog", "sc1", "-sc2"}; 427 EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); 428 OS.flush(); 429 EXPECT_FALSE(Errs.empty()); 430 } 431 432 TEST(CommandLineTest, AddToAllSubCommands) { 433 cl::ResetCommandLineParser(); 434 435 StackSubCommand SC1("sc1", "First subcommand"); 436 StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands), 437 cl::init(false)); 438 StackSubCommand SC2("sc2", "Second subcommand"); 439 440 const char *args[] = {"prog", "-everywhere"}; 441 const char *args2[] = {"prog", "sc1", "-everywhere"}; 442 const char *args3[] = {"prog", "sc2", "-everywhere"}; 443 444 std::string Errs; 445 raw_string_ostream OS(Errs); 446 447 EXPECT_FALSE(AllOpt); 448 EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); 449 EXPECT_TRUE(AllOpt); 450 451 AllOpt = false; 452 453 cl::ResetAllOptionOccurrences(); 454 EXPECT_FALSE(AllOpt); 455 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); 456 EXPECT_TRUE(AllOpt); 457 458 AllOpt = false; 459 460 cl::ResetAllOptionOccurrences(); 461 EXPECT_FALSE(AllOpt); 462 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); 463 EXPECT_TRUE(AllOpt); 464 465 // Since all parsing succeeded, the error message should be empty. 466 OS.flush(); 467 EXPECT_TRUE(Errs.empty()); 468 } 469 470 TEST(CommandLineTest, ReparseCommandLineOptions) { 471 cl::ResetCommandLineParser(); 472 473 StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand), 474 cl::init(false)); 475 476 const char *args[] = {"prog", "-top-level"}; 477 478 EXPECT_FALSE(TopLevelOpt); 479 EXPECT_TRUE( 480 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 481 EXPECT_TRUE(TopLevelOpt); 482 483 TopLevelOpt = false; 484 485 cl::ResetAllOptionOccurrences(); 486 EXPECT_FALSE(TopLevelOpt); 487 EXPECT_TRUE( 488 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 489 EXPECT_TRUE(TopLevelOpt); 490 } 491 492 TEST(CommandLineTest, RemoveFromRegularSubCommand) { 493 cl::ResetCommandLineParser(); 494 495 StackSubCommand SC("sc", "Subcommand"); 496 StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false)); 497 StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false)); 498 499 const char *args[] = {"prog", "sc", "-remove-option"}; 500 501 std::string Errs; 502 raw_string_ostream OS(Errs); 503 504 EXPECT_FALSE(RemoveOption); 505 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); 506 EXPECT_TRUE(RemoveOption); 507 OS.flush(); 508 EXPECT_TRUE(Errs.empty()); 509 510 RemoveOption.removeArgument(); 511 512 cl::ResetAllOptionOccurrences(); 513 EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); 514 OS.flush(); 515 EXPECT_FALSE(Errs.empty()); 516 } 517 518 TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { 519 cl::ResetCommandLineParser(); 520 521 StackOption<bool> TopLevelRemove( 522 "top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false)); 523 StackOption<bool> TopLevelKeep( 524 "top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false)); 525 526 const char *args[] = {"prog", "-top-level-remove"}; 527 528 EXPECT_FALSE(TopLevelRemove); 529 EXPECT_TRUE( 530 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 531 EXPECT_TRUE(TopLevelRemove); 532 533 TopLevelRemove.removeArgument(); 534 535 cl::ResetAllOptionOccurrences(); 536 EXPECT_FALSE( 537 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 538 } 539 540 TEST(CommandLineTest, RemoveFromAllSubCommands) { 541 cl::ResetCommandLineParser(); 542 543 StackSubCommand SC1("sc1", "First Subcommand"); 544 StackSubCommand SC2("sc2", "Second Subcommand"); 545 StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands), 546 cl::init(false)); 547 StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands), 548 cl::init(false)); 549 550 const char *args0[] = {"prog", "-remove-option"}; 551 const char *args1[] = {"prog", "sc1", "-remove-option"}; 552 const char *args2[] = {"prog", "sc2", "-remove-option"}; 553 554 // It should work for all subcommands including the top-level. 555 EXPECT_FALSE(RemoveOption); 556 EXPECT_TRUE( 557 cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); 558 EXPECT_TRUE(RemoveOption); 559 560 RemoveOption = false; 561 562 cl::ResetAllOptionOccurrences(); 563 EXPECT_FALSE(RemoveOption); 564 EXPECT_TRUE( 565 cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); 566 EXPECT_TRUE(RemoveOption); 567 568 RemoveOption = false; 569 570 cl::ResetAllOptionOccurrences(); 571 EXPECT_FALSE(RemoveOption); 572 EXPECT_TRUE( 573 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 574 EXPECT_TRUE(RemoveOption); 575 576 RemoveOption.removeArgument(); 577 578 // It should not work for any subcommands including the top-level. 579 cl::ResetAllOptionOccurrences(); 580 EXPECT_FALSE( 581 cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); 582 cl::ResetAllOptionOccurrences(); 583 EXPECT_FALSE( 584 cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); 585 cl::ResetAllOptionOccurrences(); 586 EXPECT_FALSE( 587 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 588 } 589 590 TEST(CommandLineTest, GetRegisteredSubcommands) { 591 cl::ResetCommandLineParser(); 592 593 StackSubCommand SC1("sc1", "First Subcommand"); 594 StackOption<bool> Opt1("opt1", cl::sub(SC1), cl::init(false)); 595 StackSubCommand SC2("sc2", "Second subcommand"); 596 StackOption<bool> Opt2("opt2", cl::sub(SC2), cl::init(false)); 597 598 const char *args0[] = {"prog", "sc1"}; 599 const char *args1[] = {"prog", "sc2"}; 600 601 EXPECT_TRUE( 602 cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); 603 EXPECT_FALSE(Opt1); 604 EXPECT_FALSE(Opt2); 605 for (auto *S : cl::getRegisteredSubcommands()) { 606 if (*S) { 607 EXPECT_EQ("sc1", S->getName()); 608 } 609 } 610 611 cl::ResetAllOptionOccurrences(); 612 EXPECT_TRUE( 613 cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls())); 614 EXPECT_FALSE(Opt1); 615 EXPECT_FALSE(Opt2); 616 for (auto *S : cl::getRegisteredSubcommands()) { 617 if (*S) { 618 EXPECT_EQ("sc2", S->getName()); 619 } 620 } 621 } 622 623 TEST(CommandLineTest, DefaultOptions) { 624 cl::ResetCommandLineParser(); 625 626 StackOption<std::string> Bar("bar", cl::sub(*cl::AllSubCommands), 627 cl::DefaultOption); 628 StackOption<std::string, cl::alias> Bar_Alias( 629 "b", cl::desc("Alias for -bar"), cl::aliasopt(Bar), cl::DefaultOption); 630 631 StackOption<bool> Foo("foo", cl::init(false), cl::sub(*cl::AllSubCommands), 632 cl::DefaultOption); 633 StackOption<bool, cl::alias> Foo_Alias("f", cl::desc("Alias for -foo"), 634 cl::aliasopt(Foo), cl::DefaultOption); 635 636 StackSubCommand SC1("sc1", "First Subcommand"); 637 // Override "-b" and change type in sc1 SubCommand. 638 StackOption<bool> SC1_B("b", cl::sub(SC1), cl::init(false)); 639 StackSubCommand SC2("sc2", "Second subcommand"); 640 // Override "-foo" and change type in sc2 SubCommand. Note that this does not 641 // affect "-f" alias, which continues to work correctly. 642 StackOption<std::string> SC2_Foo("foo", cl::sub(SC2)); 643 644 const char *args0[] = {"prog", "-b", "args0 bar string", "-f"}; 645 EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args0) / sizeof(char *), args0, 646 StringRef(), &llvm::nulls())); 647 EXPECT_TRUE(Bar == "args0 bar string"); 648 EXPECT_TRUE(Foo); 649 EXPECT_FALSE(SC1_B); 650 EXPECT_TRUE(SC2_Foo.empty()); 651 652 cl::ResetAllOptionOccurrences(); 653 654 const char *args1[] = {"prog", "sc1", "-b", "-bar", "args1 bar string", "-f"}; 655 EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args1) / sizeof(char *), args1, 656 StringRef(), &llvm::nulls())); 657 EXPECT_TRUE(Bar == "args1 bar string"); 658 EXPECT_TRUE(Foo); 659 EXPECT_TRUE(SC1_B); 660 EXPECT_TRUE(SC2_Foo.empty()); 661 for (auto *S : cl::getRegisteredSubcommands()) { 662 if (*S) { 663 EXPECT_EQ("sc1", S->getName()); 664 } 665 } 666 667 cl::ResetAllOptionOccurrences(); 668 669 const char *args2[] = {"prog", "sc2", "-b", "args2 bar string", 670 "-f", "-foo", "foo string"}; 671 EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args2) / sizeof(char *), args2, 672 StringRef(), &llvm::nulls())); 673 EXPECT_TRUE(Bar == "args2 bar string"); 674 EXPECT_TRUE(Foo); 675 EXPECT_FALSE(SC1_B); 676 EXPECT_TRUE(SC2_Foo == "foo string"); 677 for (auto *S : cl::getRegisteredSubcommands()) { 678 if (*S) { 679 EXPECT_EQ("sc2", S->getName()); 680 } 681 } 682 cl::ResetCommandLineParser(); 683 } 684 685 TEST(CommandLineTest, ArgumentLimit) { 686 std::string args(32 * 4096, 'a'); 687 EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data())); 688 } 689 690 TEST(CommandLineTest, ResponseFileWindows) { 691 if (!Triple(sys::getProcessTriple()).isOSWindows()) 692 return; 693 694 StackOption<std::string, cl::list<std::string>> InputFilenames( 695 cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore); 696 StackOption<bool> TopLevelOpt("top-level", cl::init(false)); 697 698 // Create response file. 699 int FileDescriptor; 700 SmallString<64> TempPath; 701 std::error_code EC = 702 llvm::sys::fs::createTemporaryFile("resp-", ".txt", FileDescriptor, TempPath); 703 EXPECT_TRUE(!EC); 704 705 std::ofstream RspFile(TempPath.c_str()); 706 EXPECT_TRUE(RspFile.is_open()); 707 RspFile << "-top-level\npath\\dir\\file1\npath/dir/file2"; 708 RspFile.close(); 709 710 llvm::SmallString<128> RspOpt; 711 RspOpt.append(1, '@'); 712 RspOpt.append(TempPath.c_str()); 713 const char *args[] = {"prog", RspOpt.c_str()}; 714 EXPECT_FALSE(TopLevelOpt); 715 EXPECT_TRUE( 716 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 717 EXPECT_TRUE(TopLevelOpt); 718 EXPECT_TRUE(InputFilenames[0] == "path\\dir\\file1"); 719 EXPECT_TRUE(InputFilenames[1] == "path/dir/file2"); 720 721 llvm::sys::fs::remove(TempPath.c_str()); 722 } 723 724 TEST(CommandLineTest, ResponseFiles) { 725 llvm::SmallString<128> TestDir; 726 std::error_code EC = 727 llvm::sys::fs::createUniqueDirectory("unittest", TestDir); 728 EXPECT_TRUE(!EC); 729 730 // Create included response file of first level. 731 llvm::SmallString<128> IncludedFileName; 732 llvm::sys::path::append(IncludedFileName, TestDir, "resp1"); 733 std::ofstream IncludedFile(IncludedFileName.c_str()); 734 EXPECT_TRUE(IncludedFile.is_open()); 735 IncludedFile << "-option_1 -option_2\n" 736 "@incdir/resp2\n" 737 "-option_3=abcd\n"; 738 IncludedFile.close(); 739 740 // Directory for included file. 741 llvm::SmallString<128> IncDir; 742 llvm::sys::path::append(IncDir, TestDir, "incdir"); 743 EC = llvm::sys::fs::create_directory(IncDir); 744 EXPECT_TRUE(!EC); 745 746 // Create included response file of second level. 747 llvm::SmallString<128> IncludedFileName2; 748 llvm::sys::path::append(IncludedFileName2, IncDir, "resp2"); 749 std::ofstream IncludedFile2(IncludedFileName2.c_str()); 750 EXPECT_TRUE(IncludedFile2.is_open()); 751 IncludedFile2 << "-option_21 -option_22\n"; 752 IncludedFile2 << "-option_23=abcd\n"; 753 IncludedFile2.close(); 754 755 // Prepare 'file' with reference to response file. 756 SmallString<128> IncRef; 757 IncRef.append(1, '@'); 758 IncRef.append(IncludedFileName.c_str()); 759 llvm::SmallVector<const char *, 4> Argv = 760 { "test/test", "-flag_1", IncRef.c_str(), "-flag_2" }; 761 762 // Expand response files. 763 llvm::BumpPtrAllocator A; 764 llvm::StringSaver Saver(A); 765 bool Res = llvm::cl::ExpandResponseFiles( 766 Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, true); 767 EXPECT_TRUE(Res); 768 EXPECT_EQ(Argv.size(), 9U); 769 EXPECT_STREQ(Argv[0], "test/test"); 770 EXPECT_STREQ(Argv[1], "-flag_1"); 771 EXPECT_STREQ(Argv[2], "-option_1"); 772 EXPECT_STREQ(Argv[3], "-option_2"); 773 EXPECT_STREQ(Argv[4], "-option_21"); 774 EXPECT_STREQ(Argv[5], "-option_22"); 775 EXPECT_STREQ(Argv[6], "-option_23=abcd"); 776 EXPECT_STREQ(Argv[7], "-option_3=abcd"); 777 EXPECT_STREQ(Argv[8], "-flag_2"); 778 779 llvm::sys::fs::remove(IncludedFileName2); 780 llvm::sys::fs::remove(IncDir); 781 llvm::sys::fs::remove(IncludedFileName); 782 llvm::sys::fs::remove(TestDir); 783 } 784 785 TEST(CommandLineTest, RecursiveResponseFiles) { 786 SmallString<128> TestDir; 787 std::error_code EC = sys::fs::createUniqueDirectory("unittest", TestDir); 788 EXPECT_TRUE(!EC); 789 790 SmallString<128> ResponseFilePath; 791 sys::path::append(ResponseFilePath, TestDir, "recursive.rsp"); 792 std::string ResponseFileRef = std::string("@") + ResponseFilePath.c_str(); 793 794 std::ofstream ResponseFile(ResponseFilePath.str()); 795 EXPECT_TRUE(ResponseFile.is_open()); 796 ResponseFile << ResponseFileRef << "\n"; 797 ResponseFile << ResponseFileRef << "\n"; 798 ResponseFile.close(); 799 800 // Ensure the recursive expansion terminates. 801 llvm::SmallVector<const char *, 4> Argv = {"test/test", 802 ResponseFileRef.c_str()}; 803 llvm::BumpPtrAllocator A; 804 llvm::StringSaver Saver(A); 805 bool Res = llvm::cl::ExpandResponseFiles( 806 Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, false); 807 EXPECT_FALSE(Res); 808 809 // Ensure some expansion took place. 810 EXPECT_GT(Argv.size(), 2U); 811 EXPECT_STREQ(Argv[0], "test/test"); 812 for (size_t i = 1; i < Argv.size(); ++i) 813 EXPECT_STREQ(Argv[i], ResponseFileRef.c_str()); 814 } 815 816 TEST(CommandLineTest, ResponseFilesAtArguments) { 817 SmallString<128> TestDir; 818 std::error_code EC = sys::fs::createUniqueDirectory("unittest", TestDir); 819 EXPECT_TRUE(!EC); 820 821 SmallString<128> ResponseFilePath; 822 sys::path::append(ResponseFilePath, TestDir, "test.rsp"); 823 824 std::ofstream ResponseFile(ResponseFilePath.c_str()); 825 EXPECT_TRUE(ResponseFile.is_open()); 826 ResponseFile << "-foo" << "\n"; 827 ResponseFile << "-bar" << "\n"; 828 ResponseFile.close(); 829 830 // Ensure we expand rsp files after lots of non-rsp arguments starting with @. 831 constexpr size_t NON_RSP_AT_ARGS = 64; 832 llvm::SmallVector<const char *, 4> Argv = {"test/test"}; 833 Argv.append(NON_RSP_AT_ARGS, "@non_rsp_at_arg"); 834 std::string ResponseFileRef = std::string("@") + ResponseFilePath.c_str(); 835 Argv.push_back(ResponseFileRef.c_str()); 836 837 llvm::BumpPtrAllocator A; 838 llvm::StringSaver Saver(A); 839 bool Res = llvm::cl::ExpandResponseFiles( 840 Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, false); 841 EXPECT_FALSE(Res); 842 843 // ASSERT instead of EXPECT to prevent potential out-of-bounds access. 844 ASSERT_EQ(Argv.size(), 1 + NON_RSP_AT_ARGS + 2); 845 size_t i = 0; 846 EXPECT_STREQ(Argv[i++], "test/test"); 847 for (; i < 1 + NON_RSP_AT_ARGS; ++i) 848 EXPECT_STREQ(Argv[i], "@non_rsp_at_arg"); 849 EXPECT_STREQ(Argv[i++], "-foo"); 850 EXPECT_STREQ(Argv[i++], "-bar"); 851 } 852 853 TEST(CommandLineTest, SetDefautValue) { 854 cl::ResetCommandLineParser(); 855 856 StackOption<std::string> Opt1("opt1", cl::init("true")); 857 StackOption<bool> Opt2("opt2", cl::init(true)); 858 cl::alias Alias("alias", llvm::cl::aliasopt(Opt2)); 859 StackOption<int> Opt3("opt3", cl::init(3)); 860 861 const char *args[] = {"prog", "-opt1=false", "-opt2", "-opt3"}; 862 863 EXPECT_TRUE( 864 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 865 866 EXPECT_TRUE(Opt1 == "false"); 867 EXPECT_TRUE(Opt2); 868 EXPECT_TRUE(Opt3 == 3); 869 870 Opt2 = false; 871 Opt3 = 1; 872 873 cl::ResetAllOptionOccurrences(); 874 875 for (auto &OM : cl::getRegisteredOptions(*cl::TopLevelSubCommand)) { 876 cl::Option *O = OM.second; 877 if (O->ArgStr == "opt2") { 878 continue; 879 } 880 O->setDefault(); 881 } 882 883 EXPECT_TRUE(Opt1 == "true"); 884 EXPECT_TRUE(Opt2); 885 EXPECT_TRUE(Opt3 == 3); 886 Alias.removeArgument(); 887 } 888 889 TEST(CommandLineTest, ReadConfigFile) { 890 llvm::SmallVector<const char *, 1> Argv; 891 892 llvm::SmallString<128> TestDir; 893 std::error_code EC = 894 llvm::sys::fs::createUniqueDirectory("unittest", TestDir); 895 EXPECT_TRUE(!EC); 896 897 llvm::SmallString<128> TestCfg; 898 llvm::sys::path::append(TestCfg, TestDir, "foo"); 899 std::ofstream ConfigFile(TestCfg.c_str()); 900 EXPECT_TRUE(ConfigFile.is_open()); 901 ConfigFile << "# Comment\n" 902 "-option_1\n" 903 "@subconfig\n" 904 "-option_3=abcd\n" 905 "-option_4=\\\n" 906 "cdef\n"; 907 ConfigFile.close(); 908 909 llvm::SmallString<128> TestCfg2; 910 llvm::sys::path::append(TestCfg2, TestDir, "subconfig"); 911 std::ofstream ConfigFile2(TestCfg2.c_str()); 912 EXPECT_TRUE(ConfigFile2.is_open()); 913 ConfigFile2 << "-option_2\n" 914 "\n" 915 " # comment\n"; 916 ConfigFile2.close(); 917 918 // Make sure the current directory is not the directory where config files 919 // resides. In this case the code that expands response files will not find 920 // 'subconfig' unless it resolves nested inclusions relative to the including 921 // file. 922 llvm::SmallString<128> CurrDir; 923 EC = llvm::sys::fs::current_path(CurrDir); 924 EXPECT_TRUE(!EC); 925 EXPECT_TRUE(StringRef(CurrDir) != StringRef(TestDir)); 926 927 llvm::BumpPtrAllocator A; 928 llvm::StringSaver Saver(A); 929 bool Result = llvm::cl::readConfigFile(TestCfg, Saver, Argv); 930 931 EXPECT_TRUE(Result); 932 EXPECT_EQ(Argv.size(), 4U); 933 EXPECT_STREQ(Argv[0], "-option_1"); 934 EXPECT_STREQ(Argv[1], "-option_2"); 935 EXPECT_STREQ(Argv[2], "-option_3=abcd"); 936 EXPECT_STREQ(Argv[3], "-option_4=cdef"); 937 938 llvm::sys::fs::remove(TestCfg2); 939 llvm::sys::fs::remove(TestCfg); 940 llvm::sys::fs::remove(TestDir); 941 } 942 943 TEST(CommandLineTest, PositionalEatArgsError) { 944 StackOption<std::string, cl::list<std::string>> PosEatArgs( 945 "positional-eat-args", cl::Positional, cl::desc("<arguments>..."), 946 cl::ZeroOrMore, cl::PositionalEatsArgs); 947 948 const char *args[] = {"prog", "-positional-eat-args=XXXX"}; 949 const char *args2[] = {"prog", "-positional-eat-args=XXXX", "-foo"}; 950 const char *args3[] = {"prog", "-positional-eat-args", "-foo"}; 951 952 std::string Errs; 953 raw_string_ostream OS(Errs); 954 EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); OS.flush(); 955 EXPECT_FALSE(Errs.empty()); Errs.clear(); 956 EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); OS.flush(); 957 EXPECT_FALSE(Errs.empty()); Errs.clear(); 958 EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); OS.flush(); 959 EXPECT_TRUE(Errs.empty()); 960 } 961 962 #ifdef _WIN32 963 TEST(CommandLineTest, GetCommandLineArguments) { 964 int argc = __argc; 965 char **argv = __argv; 966 967 // GetCommandLineArguments is called in InitLLVM. 968 llvm::InitLLVM X(argc, argv); 969 970 EXPECT_EQ(llvm::sys::path::is_absolute(argv[0]), 971 llvm::sys::path::is_absolute(__argv[0])); 972 973 EXPECT_TRUE(llvm::sys::path::filename(argv[0]) 974 .equals_lower("supporttests.exe")) 975 << "Filename of test executable is " 976 << llvm::sys::path::filename(argv[0]); 977 } 978 #endif 979 980 class OutputRedirector { 981 public: 982 OutputRedirector(int RedirectFD) 983 : RedirectFD(RedirectFD), OldFD(dup(RedirectFD)) { 984 if (OldFD == -1 || 985 sys::fs::createTemporaryFile("unittest-redirect", "", NewFD, 986 FilePath) || 987 dup2(NewFD, RedirectFD) == -1) 988 Valid = false; 989 } 990 991 ~OutputRedirector() { 992 dup2(OldFD, RedirectFD); 993 close(OldFD); 994 close(NewFD); 995 } 996 997 SmallVector<char, 128> FilePath; 998 bool Valid = true; 999 1000 private: 1001 int RedirectFD; 1002 int OldFD; 1003 int NewFD; 1004 }; 1005 1006 struct AutoDeleteFile { 1007 SmallVector<char, 128> FilePath; 1008 ~AutoDeleteFile() { 1009 if (!FilePath.empty()) 1010 sys::fs::remove(std::string(FilePath.data(), FilePath.size())); 1011 } 1012 }; 1013 1014 class PrintOptionInfoTest : public ::testing::Test { 1015 public: 1016 // Return std::string because the output of a failing EXPECT check is 1017 // unreadable for StringRef. It also avoids any lifetime issues. 1018 template <typename... Ts> std::string runTest(Ts... OptionAttributes) { 1019 AutoDeleteFile File; 1020 { 1021 OutputRedirector Stdout(fileno(stdout)); 1022 if (!Stdout.Valid) 1023 return ""; 1024 File.FilePath = Stdout.FilePath; 1025 1026 StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText), 1027 OptionAttributes...); 1028 printOptionInfo(TestOption, 25); 1029 outs().flush(); 1030 } 1031 auto Buffer = MemoryBuffer::getFile(File.FilePath); 1032 if (!Buffer) 1033 return ""; 1034 return Buffer->get()->getBuffer().str(); 1035 } 1036 1037 enum class OptionValue { Val }; 1038 const StringRef Opt = "some-option"; 1039 const StringRef HelpText = "some help"; 1040 1041 private: 1042 // This is a workaround for cl::Option sub-classes having their 1043 // printOptionInfo functions private. 1044 void printOptionInfo(const cl::Option &O, size_t Width) { 1045 O.printOptionInfo(Width); 1046 } 1047 }; 1048 1049 TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithoutSentinel) { 1050 std::string Output = 1051 runTest(cl::ValueOptional, 1052 cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"))); 1053 1054 // clang-format off 1055 EXPECT_EQ(Output, (" -" + Opt + "=<value> - " + HelpText + "\n" 1056 " =v1 - desc1\n") 1057 .str()); 1058 // clang-format on 1059 } 1060 1061 TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinel) { 1062 std::string Output = runTest( 1063 cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"), 1064 clEnumValN(OptionValue::Val, "", ""))); 1065 1066 // clang-format off 1067 EXPECT_EQ(Output, 1068 (" -" + Opt + " - " + HelpText + "\n" 1069 " -" + Opt + "=<value> - " + HelpText + "\n" 1070 " =v1 - desc1\n") 1071 .str()); 1072 // clang-format on 1073 } 1074 1075 TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinelWithHelp) { 1076 std::string Output = runTest( 1077 cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"), 1078 clEnumValN(OptionValue::Val, "", "desc2"))); 1079 1080 // clang-format off 1081 EXPECT_EQ(Output, (" -" + Opt + " - " + HelpText + "\n" 1082 " -" + Opt + "=<value> - " + HelpText + "\n" 1083 " =v1 - desc1\n" 1084 " =<empty> - desc2\n") 1085 .str()); 1086 // clang-format on 1087 } 1088 1089 TEST_F(PrintOptionInfoTest, PrintOptionInfoValueRequiredWithEmptyValueName) { 1090 std::string Output = runTest( 1091 cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"), 1092 clEnumValN(OptionValue::Val, "", ""))); 1093 1094 // clang-format off 1095 EXPECT_EQ(Output, (" -" + Opt + "=<value> - " + HelpText + "\n" 1096 " =v1 - desc1\n" 1097 " =<empty>\n") 1098 .str()); 1099 // clang-format on 1100 } 1101 1102 TEST_F(PrintOptionInfoTest, PrintOptionInfoEmptyValueDescription) { 1103 std::string Output = runTest( 1104 cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", ""))); 1105 1106 // clang-format off 1107 EXPECT_EQ(Output, 1108 (" -" + Opt + "=<value> - " + HelpText + "\n" 1109 " =v1\n").str()); 1110 // clang-format on 1111 } 1112 1113 class GetOptionWidthTest : public ::testing::Test { 1114 public: 1115 enum class OptionValue { Val }; 1116 1117 template <typename... Ts> 1118 size_t runTest(StringRef ArgName, Ts... OptionAttributes) { 1119 StackOption<OptionValue> TestOption(ArgName, cl::desc("some help"), 1120 OptionAttributes...); 1121 return getOptionWidth(TestOption); 1122 } 1123 1124 private: 1125 // This is a workaround for cl::Option sub-classes having their 1126 // printOptionInfo 1127 // functions private. 1128 size_t getOptionWidth(const cl::Option &O) { return O.getOptionWidth(); } 1129 }; 1130 1131 TEST_F(GetOptionWidthTest, GetOptionWidthArgNameLonger) { 1132 StringRef ArgName("a-long-argument-name"); 1133 size_t ExpectedStrSize = (" -" + ArgName + "=<value> - ").str().size(); 1134 EXPECT_EQ( 1135 runTest(ArgName, cl::values(clEnumValN(OptionValue::Val, "v", "help"))), 1136 ExpectedStrSize); 1137 } 1138 1139 TEST_F(GetOptionWidthTest, GetOptionWidthFirstOptionNameLonger) { 1140 StringRef OptName("a-long-option-name"); 1141 size_t ExpectedStrSize = (" =" + OptName + " - ").str().size(); 1142 EXPECT_EQ( 1143 runTest("a", cl::values(clEnumValN(OptionValue::Val, OptName, "help"), 1144 clEnumValN(OptionValue::Val, "b", "help"))), 1145 ExpectedStrSize); 1146 } 1147 1148 TEST_F(GetOptionWidthTest, GetOptionWidthSecondOptionNameLonger) { 1149 StringRef OptName("a-long-option-name"); 1150 size_t ExpectedStrSize = (" =" + OptName + " - ").str().size(); 1151 EXPECT_EQ( 1152 runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"), 1153 clEnumValN(OptionValue::Val, OptName, "help"))), 1154 ExpectedStrSize); 1155 } 1156 1157 TEST_F(GetOptionWidthTest, GetOptionWidthEmptyOptionNameLonger) { 1158 size_t ExpectedStrSize = StringRef(" =<empty> - ").size(); 1159 // The length of a=<value> (including indentation) is actually the same as the 1160 // =<empty> string, so it is impossible to distinguish via testing the case 1161 // where the empty string is picked from where the option name is picked. 1162 EXPECT_EQ(runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"), 1163 clEnumValN(OptionValue::Val, "", "help"))), 1164 ExpectedStrSize); 1165 } 1166 1167 TEST_F(GetOptionWidthTest, 1168 GetOptionWidthValueOptionalEmptyOptionWithNoDescription) { 1169 StringRef ArgName("a"); 1170 // The length of a=<value> (including indentation) is actually the same as the 1171 // =<empty> string, so it is impossible to distinguish via testing the case 1172 // where the empty string is ignored from where it is not ignored. 1173 // The dash will not actually be printed, but the space it would take up is 1174 // included to ensure a consistent column width. 1175 size_t ExpectedStrSize = (" -" + ArgName + "=<value> - ").str().size(); 1176 EXPECT_EQ(runTest(ArgName, cl::ValueOptional, 1177 cl::values(clEnumValN(OptionValue::Val, "value", "help"), 1178 clEnumValN(OptionValue::Val, "", ""))), 1179 ExpectedStrSize); 1180 } 1181 1182 TEST_F(GetOptionWidthTest, 1183 GetOptionWidthValueRequiredEmptyOptionWithNoDescription) { 1184 // The length of a=<value> (including indentation) is actually the same as the 1185 // =<empty> string, so it is impossible to distinguish via testing the case 1186 // where the empty string is picked from where the option name is picked 1187 size_t ExpectedStrSize = StringRef(" =<empty> - ").size(); 1188 EXPECT_EQ(runTest("a", cl::ValueRequired, 1189 cl::values(clEnumValN(OptionValue::Val, "value", "help"), 1190 clEnumValN(OptionValue::Val, "", ""))), 1191 ExpectedStrSize); 1192 } 1193 1194 TEST(CommandLineTest, PrefixOptions) { 1195 cl::ResetCommandLineParser(); 1196 1197 StackOption<std::string, cl::list<std::string>> IncludeDirs( 1198 "I", cl::Prefix, cl::desc("Declare an include directory")); 1199 1200 // Test non-prefixed variant works with cl::Prefix options. 1201 EXPECT_TRUE(IncludeDirs.empty()); 1202 const char *args[] = {"prog", "-I=/usr/include"}; 1203 EXPECT_TRUE( 1204 cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); 1205 EXPECT_TRUE(IncludeDirs.size() == 1); 1206 EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0); 1207 1208 IncludeDirs.erase(IncludeDirs.begin()); 1209 cl::ResetAllOptionOccurrences(); 1210 1211 // Test non-prefixed variant works with cl::Prefix options when value is 1212 // passed in following argument. 1213 EXPECT_TRUE(IncludeDirs.empty()); 1214 const char *args2[] = {"prog", "-I", "/usr/include"}; 1215 EXPECT_TRUE( 1216 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 1217 EXPECT_TRUE(IncludeDirs.size() == 1); 1218 EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0); 1219 1220 IncludeDirs.erase(IncludeDirs.begin()); 1221 cl::ResetAllOptionOccurrences(); 1222 1223 // Test prefixed variant works with cl::Prefix options. 1224 EXPECT_TRUE(IncludeDirs.empty()); 1225 const char *args3[] = {"prog", "-I/usr/include"}; 1226 EXPECT_TRUE( 1227 cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls())); 1228 EXPECT_TRUE(IncludeDirs.size() == 1); 1229 EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0); 1230 1231 StackOption<std::string, cl::list<std::string>> MacroDefs( 1232 "D", cl::AlwaysPrefix, cl::desc("Define a macro"), 1233 cl::value_desc("MACRO[=VALUE]")); 1234 1235 cl::ResetAllOptionOccurrences(); 1236 1237 // Test non-prefixed variant does not work with cl::AlwaysPrefix options: 1238 // equal sign is part of the value. 1239 EXPECT_TRUE(MacroDefs.empty()); 1240 const char *args4[] = {"prog", "-D=HAVE_FOO"}; 1241 EXPECT_TRUE( 1242 cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls())); 1243 EXPECT_TRUE(MacroDefs.size() == 1); 1244 EXPECT_TRUE(MacroDefs.front().compare("=HAVE_FOO") == 0); 1245 1246 MacroDefs.erase(MacroDefs.begin()); 1247 cl::ResetAllOptionOccurrences(); 1248 1249 // Test non-prefixed variant does not allow value to be passed in following 1250 // argument with cl::AlwaysPrefix options. 1251 EXPECT_TRUE(MacroDefs.empty()); 1252 const char *args5[] = {"prog", "-D", "HAVE_FOO"}; 1253 EXPECT_FALSE( 1254 cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls())); 1255 EXPECT_TRUE(MacroDefs.empty()); 1256 1257 cl::ResetAllOptionOccurrences(); 1258 1259 // Test prefixed variant works with cl::AlwaysPrefix options. 1260 EXPECT_TRUE(MacroDefs.empty()); 1261 const char *args6[] = {"prog", "-DHAVE_FOO"}; 1262 EXPECT_TRUE( 1263 cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls())); 1264 EXPECT_TRUE(MacroDefs.size() == 1); 1265 EXPECT_TRUE(MacroDefs.front().compare("HAVE_FOO") == 0); 1266 } 1267 1268 TEST(CommandLineTest, GroupingWithValue) { 1269 cl::ResetCommandLineParser(); 1270 1271 StackOption<bool> OptF("f", cl::Grouping, cl::desc("Some flag")); 1272 StackOption<bool> OptB("b", cl::Grouping, cl::desc("Another flag")); 1273 StackOption<bool> OptD("d", cl::Grouping, cl::ValueDisallowed, 1274 cl::desc("ValueDisallowed option")); 1275 StackOption<std::string> OptV("v", cl::Grouping, 1276 cl::desc("ValueRequired option")); 1277 StackOption<std::string> OptO("o", cl::Grouping, cl::ValueOptional, 1278 cl::desc("ValueOptional option")); 1279 1280 // Should be possible to use an option which requires a value 1281 // at the end of a group. 1282 const char *args1[] = {"prog", "-fv", "val1"}; 1283 EXPECT_TRUE( 1284 cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); 1285 EXPECT_TRUE(OptF); 1286 EXPECT_STREQ("val1", OptV.c_str()); 1287 OptV.clear(); 1288 cl::ResetAllOptionOccurrences(); 1289 1290 // Should not crash if it is accidentally used elsewhere in the group. 1291 const char *args2[] = {"prog", "-vf", "val2"}; 1292 EXPECT_FALSE( 1293 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 1294 OptV.clear(); 1295 cl::ResetAllOptionOccurrences(); 1296 1297 // Should allow the "opt=value" form at the end of the group 1298 const char *args3[] = {"prog", "-fv=val3"}; 1299 EXPECT_TRUE( 1300 cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls())); 1301 EXPECT_TRUE(OptF); 1302 EXPECT_STREQ("val3", OptV.c_str()); 1303 OptV.clear(); 1304 cl::ResetAllOptionOccurrences(); 1305 1306 // Should allow assigning a value for a ValueOptional option 1307 // at the end of the group 1308 const char *args4[] = {"prog", "-fo=val4"}; 1309 EXPECT_TRUE( 1310 cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls())); 1311 EXPECT_TRUE(OptF); 1312 EXPECT_STREQ("val4", OptO.c_str()); 1313 OptO.clear(); 1314 cl::ResetAllOptionOccurrences(); 1315 1316 // Should assign an empty value if a ValueOptional option is used elsewhere 1317 // in the group. 1318 const char *args5[] = {"prog", "-fob"}; 1319 EXPECT_TRUE( 1320 cl::ParseCommandLineOptions(2, args5, StringRef(), &llvm::nulls())); 1321 EXPECT_TRUE(OptF); 1322 EXPECT_EQ(1, OptO.getNumOccurrences()); 1323 EXPECT_EQ(1, OptB.getNumOccurrences()); 1324 EXPECT_TRUE(OptO.empty()); 1325 cl::ResetAllOptionOccurrences(); 1326 1327 // Should not allow an assignment for a ValueDisallowed option. 1328 const char *args6[] = {"prog", "-fd=false"}; 1329 EXPECT_FALSE( 1330 cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls())); 1331 } 1332 1333 TEST(CommandLineTest, GroupingAndPrefix) { 1334 cl::ResetCommandLineParser(); 1335 1336 StackOption<bool> OptF("f", cl::Grouping, cl::desc("Some flag")); 1337 StackOption<bool> OptB("b", cl::Grouping, cl::desc("Another flag")); 1338 StackOption<std::string> OptP("p", cl::Prefix, cl::Grouping, 1339 cl::desc("Prefix and Grouping")); 1340 StackOption<std::string> OptA("a", cl::AlwaysPrefix, cl::Grouping, 1341 cl::desc("AlwaysPrefix and Grouping")); 1342 1343 // Should be possible to use a cl::Prefix option without grouping. 1344 const char *args1[] = {"prog", "-pval1"}; 1345 EXPECT_TRUE( 1346 cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls())); 1347 EXPECT_STREQ("val1", OptP.c_str()); 1348 OptP.clear(); 1349 cl::ResetAllOptionOccurrences(); 1350 1351 // Should be possible to pass a value in a separate argument. 1352 const char *args2[] = {"prog", "-p", "val2"}; 1353 EXPECT_TRUE( 1354 cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); 1355 EXPECT_STREQ("val2", OptP.c_str()); 1356 OptP.clear(); 1357 cl::ResetAllOptionOccurrences(); 1358 1359 // The "-opt=value" form should work, too. 1360 const char *args3[] = {"prog", "-p=val3"}; 1361 EXPECT_TRUE( 1362 cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls())); 1363 EXPECT_STREQ("val3", OptP.c_str()); 1364 OptP.clear(); 1365 cl::ResetAllOptionOccurrences(); 1366 1367 // All three previous cases should work the same way if an option with both 1368 // cl::Prefix and cl::Grouping modifiers is used at the end of a group. 1369 const char *args4[] = {"prog", "-fpval4"}; 1370 EXPECT_TRUE( 1371 cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls())); 1372 EXPECT_TRUE(OptF); 1373 EXPECT_STREQ("val4", OptP.c_str()); 1374 OptP.clear(); 1375 cl::ResetAllOptionOccurrences(); 1376 1377 const char *args5[] = {"prog", "-fp", "val5"}; 1378 EXPECT_TRUE( 1379 cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls())); 1380 EXPECT_TRUE(OptF); 1381 EXPECT_STREQ("val5", OptP.c_str()); 1382 OptP.clear(); 1383 cl::ResetAllOptionOccurrences(); 1384 1385 const char *args6[] = {"prog", "-fp=val6"}; 1386 EXPECT_TRUE( 1387 cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls())); 1388 EXPECT_TRUE(OptF); 1389 EXPECT_STREQ("val6", OptP.c_str()); 1390 OptP.clear(); 1391 cl::ResetAllOptionOccurrences(); 1392 1393 // Should assign a value even if the part after a cl::Prefix option is equal 1394 // to the name of another option. 1395 const char *args7[] = {"prog", "-fpb"}; 1396 EXPECT_TRUE( 1397 cl::ParseCommandLineOptions(2, args7, StringRef(), &llvm::nulls())); 1398 EXPECT_TRUE(OptF); 1399 EXPECT_STREQ("b", OptP.c_str()); 1400 EXPECT_FALSE(OptB); 1401 OptP.clear(); 1402 cl::ResetAllOptionOccurrences(); 1403 1404 // Should be possible to use a cl::AlwaysPrefix option without grouping. 1405 const char *args8[] = {"prog", "-aval8"}; 1406 EXPECT_TRUE( 1407 cl::ParseCommandLineOptions(2, args8, StringRef(), &llvm::nulls())); 1408 EXPECT_STREQ("val8", OptA.c_str()); 1409 OptA.clear(); 1410 cl::ResetAllOptionOccurrences(); 1411 1412 // Should not be possible to pass a value in a separate argument. 1413 const char *args9[] = {"prog", "-a", "val9"}; 1414 EXPECT_FALSE( 1415 cl::ParseCommandLineOptions(3, args9, StringRef(), &llvm::nulls())); 1416 cl::ResetAllOptionOccurrences(); 1417 1418 // With the "-opt=value" form, the "=" symbol should be preserved. 1419 const char *args10[] = {"prog", "-a=val10"}; 1420 EXPECT_TRUE( 1421 cl::ParseCommandLineOptions(2, args10, StringRef(), &llvm::nulls())); 1422 EXPECT_STREQ("=val10", OptA.c_str()); 1423 OptA.clear(); 1424 cl::ResetAllOptionOccurrences(); 1425 1426 // All three previous cases should work the same way if an option with both 1427 // cl::AlwaysPrefix and cl::Grouping modifiers is used at the end of a group. 1428 const char *args11[] = {"prog", "-faval11"}; 1429 EXPECT_TRUE( 1430 cl::ParseCommandLineOptions(2, args11, StringRef(), &llvm::nulls())); 1431 EXPECT_TRUE(OptF); 1432 EXPECT_STREQ("val11", OptA.c_str()); 1433 OptA.clear(); 1434 cl::ResetAllOptionOccurrences(); 1435 1436 const char *args12[] = {"prog", "-fa", "val12"}; 1437 EXPECT_FALSE( 1438 cl::ParseCommandLineOptions(3, args12, StringRef(), &llvm::nulls())); 1439 cl::ResetAllOptionOccurrences(); 1440 1441 const char *args13[] = {"prog", "-fa=val13"}; 1442 EXPECT_TRUE( 1443 cl::ParseCommandLineOptions(2, args13, StringRef(), &llvm::nulls())); 1444 EXPECT_TRUE(OptF); 1445 EXPECT_STREQ("=val13", OptA.c_str()); 1446 OptA.clear(); 1447 cl::ResetAllOptionOccurrences(); 1448 1449 // Should assign a value even if the part after a cl::AlwaysPrefix option 1450 // is equal to the name of another option. 1451 const char *args14[] = {"prog", "-fab"}; 1452 EXPECT_TRUE( 1453 cl::ParseCommandLineOptions(2, args14, StringRef(), &llvm::nulls())); 1454 EXPECT_TRUE(OptF); 1455 EXPECT_STREQ("b", OptA.c_str()); 1456 EXPECT_FALSE(OptB); 1457 OptA.clear(); 1458 cl::ResetAllOptionOccurrences(); 1459 } 1460 1461 } // anonymous namespace 1462