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