diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -192,6 +192,9 @@ void registerCategory(); +protected: + OptionCategory() {} + public: OptionCategory(StringRef const Name, StringRef const Description = "") @@ -405,7 +408,8 @@ desc(StringRef Str) : Desc(Str) {} - void apply(Option &O) const { O.setDescription(Desc); } + template + void apply(Opt &O) const { O.setDescription(Desc); } }; // value_desc - Modifier to set the value description shown in the -help @@ -415,7 +419,8 @@ value_desc(StringRef Str) : Desc(Str) {} - void apply(Option &O) const { O.setValueStr(Desc); } + template + void apply(Opt &O) const { O.setValueStr(Desc); } }; // init - Specify a default (initial) value for the command line argument, if @@ -642,8 +647,8 @@ template void apply(Opt &O) const { for (auto Value : Values) - O.getParser().addLiteralOption(Value.Name, Value.Value, - Value.Description); + O.addLiteralOption(Value.Name, Value.Value, + Value.Description); } }; @@ -1187,25 +1192,25 @@ }; template <> struct applicator { - static void opt(NumOccurrencesFlag N, Option &O) { + template static void opt(NumOccurrencesFlag N, Opt &O) { O.setNumOccurrencesFlag(N); } }; template <> struct applicator { - static void opt(ValueExpected VE, Option &O) { O.setValueExpectedFlag(VE); } + template static void opt(ValueExpected VE, Opt &O) { O.setValueExpectedFlag(VE); } }; template <> struct applicator { - static void opt(OptionHidden OH, Option &O) { O.setHiddenFlag(OH); } + template static void opt(OptionHidden OH, Opt &O) { O.setHiddenFlag(OH); } }; template <> struct applicator { - static void opt(FormattingFlags FF, Option &O) { O.setFormattingFlag(FF); } + template static void opt(FormattingFlags FF, Opt &O) { O.setFormattingFlag(FF); } }; template <> struct applicator { - static void opt(MiscFlags MF, Option &O) { + template static void opt(MiscFlags MF, Opt &O) { assert((MF != Grouping || O.ArgStr.size() == 1) && "cl::Grouping can only apply to single charater Options."); O.setMiscFlag(MF); @@ -1244,7 +1249,8 @@ public: opt_storage() = default; - bool setLocation(Option &O, DataType &L) { + template + bool setLocation(Opt &O, DataType &L) { if (Location) return O.error("cl::location(x) specified more than once!"); Location = &L; @@ -1392,6 +1398,11 @@ opt(const opt &) = delete; opt &operator=(const opt &) = delete; + template + void addLiteralOption(StringRef Name, const DT &V, StringRef HelpStr) { + getParser().addLiteralOption(Name, V, HelpStr); + } + // setInitialValue - Used by the cl::init modifier... void setInitialValue(const DataType &V) { this->setValue(V, true); } @@ -1578,6 +1589,11 @@ list(const list &) = delete; list &operator=(const list &) = delete; + template + void addLiteralOption(StringRef Name, const DT &V, StringRef HelpStr) { + getParser().addLiteralOption(Name, V, HelpStr); + } + ParserClass &getParser() { return Parser; } unsigned getPosition(unsigned optnum) const { @@ -2000,6 +2016,128 @@ /// where no options are supported. void ResetCommandLineParser(); +#ifndef NDEBUG +template > +using debug_opt = opt; + +using DebugSubCommand = SubCommand; +using DebugOptionCategory = OptionCategory; + +#else +class DebugSubCommand : public SubCommand { +public: + DebugSubCommand(StringRef, StringRef = "") {} +}; + +class DebugOptionCategory : public OptionCategory { +public: + DebugOptionCategory(StringRef, StringRef = "") {} +}; + +template +class debug_opt_storage {}; + +template class debug_opt_storage { +public: + DataType Value; + + // Make sure we initialize the value with the default constructor for the + // type. + debug_opt_storage() : Value(DataType()) {} + + template void setValue(const T &V, bool initial = false) { + Value = V; + } + DataType &getValue() { return Value; } + DataType getValue() const { return Value; } + + const OptionValue getDefault() const { return OptionValue(Value); } + + operator DataType() const { return getValue(); } + + // If the datatype is a pointer, support -> on it. + DataType operator->() const { return Value; } +}; + +// cl::opt_storage: specialized for external location. +template class debug_opt_storage { + void check_location() const { + assert(Location && "cl::location(...) not specified for a command " + "line option with external storage, " + "or cl::init specified before cl::location()!!"); + } +public: + DataType *Location = nullptr; // Where to store the object... + + debug_opt_storage() = default; + + template + bool setLocation(Opt &O, DataType &L) { + assert(!Location && "cl::location(x) specified more than once!"); + Location = &L; + return false; + } + template void setValue(const T &V, bool initial = false) { + check_location(); + *Location = V; + } + DataType &getValue() { return *Location; } + DataType getValue() const { return *Location; } + + const OptionValue getDefault() const { return OptionValue(*Location); } + + operator DataType() const { return getValue(); } + + // If the datatype is a pointer, support -> on it. + DataType operator->() const { return Location; } +}; + +// cl::debug_opt: Slimmed down version of cl::opt that doesn't register with the +// parser or maintain any unnecessary state. +template > +class debug_opt : public debug_opt_storage::value> { + +public: + debug_opt(const debug_opt &) = delete; + debug_opt &operator=(const debug_opt &) = delete; + + template + void addLiteralOption(StringRef Name, const DT &V, StringRef HelpStr) {} + + // setInitialValue - Used by the cl::init modifier... + void setInitialValue(const DataType &V) { this->setValue(V, true); } + + // Prints option name followed by message. Always returns true. + bool error(const Twine &, StringRef = StringRef(), + raw_ostream & = llvm::errs()) { + return true; + } + //bool error(const Twine &, raw_ostream &) { return true; } + + template explicit debug_opt(const Mods &... Ms) { + apply(this, Ms...); + } + + // From Option + void setArgStr(StringRef) {} + void setDescription(StringRef) {} + void setValueStr(StringRef) {} + void setNumOccurrencesFlag(enum NumOccurrencesFlag) {} + void setValueExpectedFlag(enum ValueExpected) {} + void setHiddenFlag(enum OptionHidden) {} + void setFormattingFlag(enum FormattingFlags) {} + void setMiscFlag(MiscFlags) {} + void setPosition(unsigned) {} + void addCategory(OptionCategory &C) {} + void addSubCommand(SubCommand &) {} + +}; + +#endif + } // end namespace cl } // end namespace llvm diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp --- a/llvm/lib/Support/Signals.cpp +++ b/llvm/lib/Support/Signals.cpp @@ -39,7 +39,7 @@ // Use explicit storage to avoid accessing cl::opt in a signal handler. static bool DisableSymbolicationFlag = false; -static cl::opt +static cl::debug_opt DisableSymbolication("disable-symbolication", cl::desc("Disable symbolizing crash backtraces."), cl::location(DisableSymbolicationFlag), cl::Hidden); diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -77,6 +77,32 @@ ~StackSubCommand() { unregisterSubCommand(); } }; +TEST(CommandLineTest, DebugOption) { + cl::OptionCategory Cat("Some category"); + bool Flag = true; + cl::debug_opt FlagOpt( + "flag", cl::desc("Simple flag."), cl::location(Flag), cl::init(false), + cl::cat(Cat), cl::DefaultOption, cl::Hidden); + + const char *args[] = {"prog", "--flag"}; + + // The value will always be present, and initialized. + EXPECT_FALSE(Flag); + +#ifndef NDEBUG + ASSERT_EQ(FlagOpt.Categories.size(), 1U); + ASSERT_EQ(cl::Hidden, FlagOpt.getOptionHiddenFlag()); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); + EXPECT_TRUE(Flag); +#else + // debug_opt's aren't available as commandline options in release builds. + EXPECT_FALSE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); + // Flag remains unchanged. + EXPECT_FALSE(Flag); +#endif +} cl::OptionCategory TestCategory("Test Options", "Description"); TEST(CommandLineTest, ModifyExisitingOption) {