diff --git a/docs/CommandLine.rst b/docs/CommandLine.rst index 46ba6d32bb3..d34fb2c3822 100644 --- a/docs/CommandLine.rst +++ b/docs/CommandLine.rst @@ -128,6 +128,7 @@ this: USAGE: compiler [options] OPTIONS: + -h - Alias for -help -help - display available options (-help-hidden for more) -o - Specify output filename @@ -194,6 +195,7 @@ declarations above, the ``-help`` option synopsis is now extended to: USAGE: compiler [options] OPTIONS: + -h - Alias for -help -help - display available options (-help-hidden for more) -o - Specify output filename @@ -1251,6 +1253,14 @@ specify boolean properties that modify the option. with ``cl::CommaSeparated``, this modifier only makes sense with a `cl::list`_ option. +.. _cl::DefaultOption: + +* The **cl::DefaultOption** modifier is used to specify that the option is a + default that can be overridden by application specific parsers. For example, + the ``-help`` alias, ``-h``, is registered this way, so it can be overridden + by applications that need to use the ``-h`` option for another purpose, + either as a regular option or an alias for another option. + .. _response files: Response files diff --git a/include/llvm/Support/CommandLine.h b/include/llvm/Support/CommandLine.h index cb85534ad8e..1fb95957244 100644 --- a/include/llvm/Support/CommandLine.h +++ b/include/llvm/Support/CommandLine.h @@ -175,7 +175,10 @@ enum MiscFlags { // Miscellaneous flags to adjust argument // If this is enabled, multiple letter options are allowed to bunch together // with only a single hyphen for the whole group. This allows emulation // of the behavior that ls uses for example: ls -la === ls -l -a - Grouping = 0x08 + Grouping = 0x08, + + // Default option + DefaultOption = 0x10 }; //===----------------------------------------------------------------------===// @@ -270,7 +273,7 @@ class Option { unsigned Value : 2; unsigned HiddenFlag : 2; // enum OptionHidden unsigned Formatting : 2; // enum FormattingFlags - unsigned Misc : 4; + unsigned Misc : 5; unsigned Position = 0; // Position of last occurrence of the option unsigned AdditionalVals = 0; // Greater than 0 for multi-valued option. @@ -306,6 +309,7 @@ public: bool hasArgStr() const { return !ArgStr.empty(); } bool isPositional() const { return getFormattingFlag() == cl::Positional; } bool isSink() const { return getMiscFlags() & cl::Sink; } + bool isDefaultOption() const { return getMiscFlags() & cl::DefaultOption; } bool isConsumeAfter() const { return getNumOccurrencesFlag() == cl::ConsumeAfter; @@ -382,7 +386,7 @@ public: } inline int getNumOccurrences() const { return NumOccurrences; } - inline void reset() { NumOccurrences = 0; } + void reset(); }; //===----------------------------------------------------------------------===// @@ -1732,7 +1736,10 @@ class alias : public Option { error("cl::alias must have argument name specified!"); if (!AliasFor) error("cl::alias must have an cl::aliasopt(option) specified!"); + if (!Subs.empty()) + error("cl::alias must not have cl::sub(), aliased option's cl::sub() will be used!"); Subs = AliasFor->Subs; + Category = AliasFor->Category; addArgument(); } diff --git a/lib/Support/CommandLine.cpp b/lib/Support/CommandLine.cpp index 1858fe941ae..98d06f65c79 100644 --- a/lib/Support/CommandLine.cpp +++ b/lib/Support/CommandLine.cpp @@ -98,6 +98,11 @@ public: // This collects additional help to be printed. std::vector MoreHelp; + // This collects Options added with the cl::DefaultOption flag. Since they can + // be overridden, they are not added to the appropriate SubCommands until + // ParseCommandLineOptions actually runs. + SmallVector DefaultOptions; + // This collects the different option categories that have been registered. SmallPtrSet RegisteredOptionCategories; @@ -146,6 +151,11 @@ public: void addOption(Option *O, SubCommand *SC) { bool HadErrors = false; if (O->hasArgStr()) { + // If it's a DefaultOption, check to make sure it isn't already there. + if (O->isDefaultOption() && + SC->OptionsMap.find(O->ArgStr) != SC->OptionsMap.end()) + return; + // Add argument to the argument map! if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr @@ -185,7 +195,12 @@ public: } } - void addOption(Option *O) { + void addOption(Option *O, bool ProcessDefaultOption = false) { + if (!ProcessDefaultOption && O->isDefaultOption()) { + DefaultOptions.push_back(O); + return; + } + if (O->Subs.empty()) { addOption(O, &*TopLevelSubCommand); } else { @@ -201,8 +216,12 @@ public: OptionNames.push_back(O->ArgStr); SubCommand &Sub = *SC; - for (auto Name : OptionNames) - Sub.OptionsMap.erase(Name); + auto End = Sub.OptionsMap.end(); + for (auto Name : OptionNames) { + auto I = Sub.OptionsMap.find(Name); + if (I != End && I->getValue() == O) + Sub.OptionsMap.erase(I); + } if (O->getFormattingFlag() == cl::Positional) for (auto Opt = Sub.PositionalOpts.begin(); @@ -266,8 +285,13 @@ public: if (O->Subs.empty()) updateArgStr(O, NewName, &*TopLevelSubCommand); else { - for (auto SC : O->Subs) - updateArgStr(O, NewName, SC); + if (O->isInAllSubCommands()) { + for (auto SC : RegisteredSubCommands) + updateArgStr(O, NewName, SC); + } else { + for (auto SC : O->Subs) + updateArgStr(O, NewName, SC); + } } } @@ -331,6 +355,8 @@ public: AllSubCommands->reset(); registerSubCommand(&*TopLevelSubCommand); registerSubCommand(&*AllSubCommands); + + DefaultOptions.clear(); } private: @@ -366,6 +392,13 @@ void Option::setArgStr(StringRef S) { ArgStr = S; } +void Option::reset() { + NumOccurrences = 0; + setDefault(); + if (isDefaultOption()) + removeArgument(); +} + // Initialise the general option category. OptionCategory llvm::cl::GeneralCategory("General options"); @@ -1167,6 +1200,10 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, auto &SinkOpts = ChosenSubCommand->SinkOpts; auto &OptionsMap = ChosenSubCommand->OptionsMap; + for (auto O: DefaultOptions) { + addOption(O, true); + } + if (ConsumeAfterOpt) { assert(PositionalOpts.size() > 0 && "Cannot specify cl::ConsumeAfter without a positional argument!"); @@ -2146,6 +2183,9 @@ static cl::opt> cl::location(WrappedNormalPrinter), cl::ValueDisallowed, cl::cat(GenericCategory), cl::sub(*AllSubCommands)); +static cl::alias HOpA("h", cl::desc("Alias for -help"), cl::aliasopt(HOp), + cl::DefaultOption); + static cl::opt> HHOp("help-hidden", cl::desc("Display all available options"), cl::location(WrappedHiddenPrinter), cl::Hidden, cl::ValueDisallowed, diff --git a/test/Support/check-default-options.txt b/test/Support/check-default-options.txt new file mode 100644 index 00000000000..403092ec5c5 --- /dev/null +++ b/test/Support/check-default-options.txt @@ -0,0 +1,18 @@ +# RUN: llvm-objdump -help-hidden %t | FileCheck --check-prefix=CHECK-OBJDUMP %s +# RUN: llvm-readobj -help-hidden %t | FileCheck --check-prefix=CHECK-READOBJ %s +# RUN: llvm-tblgen -help-hidden %t | FileCheck --check-prefix=CHECK-TBLGEN %s +# RUN: llvm-opt-report -help-hidden %t | FileCheck --check-prefix=CHECK-OPT-RPT %s +# RUN: llvm-dwarfdump -help-hidden %t | FileCheck --check-prefix=CHECK-DWARF %s +# RUN: llvm-dwarfdump -h %t | FileCheck --check-prefix=CHECK-DWARF-H %s + + +# CHECK-OBJDUMP: -h - Alias for --section-headers +# CHECK-READOBJ: -h - Alias for --file-headers +# CHECK-TBLGEN: -h - Alias for -help +# CHECK-OPT-RPT: -h - Alias for -help +# CHECK-DWARF: -h - Alias for -help + +# llvm-dwarfdump declares `-h` option and prints special help in that case, +# which is weird, but makes for a good test, i.e., shows the default `-h` +# wasn't used. +# CHECK-DWARF-H-NOT: -help-list - Display list of available options (-help-list-hidden for more) diff --git a/tools/llvm-opt-report/OptReport.cpp b/tools/llvm-opt-report/OptReport.cpp index 3ea71fc47ef..a6ca6146b48 100644 --- a/tools/llvm-opt-report/OptReport.cpp +++ b/tools/llvm-opt-report/OptReport.cpp @@ -36,8 +36,6 @@ using namespace llvm; using namespace llvm::yaml; -static cl::opt Help("h", cl::desc("Alias for -help"), cl::Hidden); - // Mark all our options with this category, everything else (except for -version // and -help) will be hidden. static cl::OptionCategory @@ -440,11 +438,6 @@ int main(int argc, const char **argv) { "A tool to generate an optimization report from YAML optimization" " record files.\n"); - if (Help) { - cl::PrintHelpMessage(); - return 0; - } - LocationInfoTy LocationInfo; if (!readLocationInfo(LocationInfo)) return 1; diff --git a/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp index a7aee41244f..fcbfd33e4c0 100644 --- a/unittests/Support/CommandLineTest.cpp +++ b/unittests/Support/CommandLineTest.cpp @@ -620,6 +620,68 @@ TEST(CommandLineTest, GetRegisteredSubcommands) { } } +TEST(CommandLineTest, DefaultOptions) { + cl::ResetCommandLineParser(); + + StackOption Bar("bar", cl::sub(*cl::AllSubCommands), + cl::DefaultOption); + StackOption Bar_Alias( + "b", cl::desc("Alias for -bar"), cl::aliasopt(Bar), cl::DefaultOption); + + StackOption Foo("foo", cl::init(false), cl::sub(*cl::AllSubCommands), + cl::DefaultOption); + StackOption Foo_Alias("f", cl::desc("Alias for -foo"), + cl::aliasopt(Foo), cl::DefaultOption); + + StackSubCommand SC1("sc1", "First Subcommand"); + // Override "-b" and change type in sc1 SubCommand. + StackOption SC1_B("b", cl::sub(SC1), cl::init(false)); + StackSubCommand SC2("sc2", "Second subcommand"); + // Override "-foo" and change type in sc2 SubCommand. Note that this does not + // affect "-f" alias, which continues to work correctly. + StackOption SC2_Foo("foo", cl::sub(SC2)); + + const char *args0[] = {"prog", "-b", "args0 bar string", "-f"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args0) / sizeof(char *), args0, + StringRef(), &llvm::nulls())); + EXPECT_TRUE(Bar == "args0 bar string"); + EXPECT_TRUE(Foo); + EXPECT_FALSE(SC1_B); + EXPECT_TRUE(SC2_Foo.empty()); + + cl::ResetAllOptionOccurrences(); + + const char *args1[] = {"prog", "sc1", "-b", "-bar", "args1 bar string", "-f"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args1) / sizeof(char *), args1, + StringRef(), &llvm::nulls())); + EXPECT_TRUE(Bar == "args1 bar string"); + EXPECT_TRUE(Foo); + EXPECT_TRUE(SC1_B); + EXPECT_TRUE(SC2_Foo.empty()); + for (auto *S : cl::getRegisteredSubcommands()) { + if (*S) { + EXPECT_EQ("sc1", S->getName()); + } + } + + cl::ResetAllOptionOccurrences(); + + const char *args2[] = {"prog", "sc2", "-b", "args2 bar string", + "-f", "-foo", "foo string"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args2) / sizeof(char *), args2, + StringRef(), &llvm::nulls())); + EXPECT_TRUE(Bar == "args2 bar string"); + EXPECT_TRUE(Foo); + EXPECT_FALSE(SC1_B); + EXPECT_TRUE(SC2_Foo == "foo string"); + for (auto *S : cl::getRegisteredSubcommands()) { + if (*S) { + EXPECT_EQ("sc2", S->getName()); + } + } + cl::ResetCommandLineParser(); +} + TEST(CommandLineTest, ArgumentLimit) { std::string args(32 * 4096, 'a'); EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data()));