Skip to content

Commit 07670b3

Browse files
author
Zachary Turner
committed
Resubmit "Update llvm command line parser to support subcommands."
This fixes an issue where occurrence counts would be unexpectedly reset when parsing different parts of a command line multiple times. **ORIGINAL COMMIT MESSAGE** This allows command line tools to use syntaxes like the following: llvm-foo.exe command1 -o1 -o2 llvm-foo.exe command2 -p1 -p2 Where command1 and command2 contain completely different sets of valid options. This is backwards compatible with previous uses of llvm cl which did not support subcommands, as any option which specifies no optional subcommand (e.g. all existing code) goes into a special "top level" subcommand that expects dashed options to appear immediately after the program name. For example, code which is subcommand unaware would generate a command line such as the following, where no subcommand is specified: llvm-foo.exe -q1 -q2 The top level subcommand can co-exist with actual subcommands, as it is implemented as an actual subcommand which is searched if no explicit subcommand is specified. So llvm-foo.exe as specified above could be written so as to support all three aforementioned command lines simultaneously. There is one additional "special" subcommand called AllSubCommands, which can be used to inject an option into every subcommand. This is useful to support things like help, so that commands such as: llvm-foo.exe --help llvm-foo.exe command1 --help llvm-foo.exe command2 --help All work and display the help for the selected subcommand without having to explicitly go and write code to handle each one separately. This patch is submitted without an example of anything actually using subcommands, but a followup patch will convert the llvm-pdbdump tool to use subcommands. Reviewed By: beanz llvm-svn: 274171
1 parent c7a05a9 commit 07670b3

File tree

4 files changed

+680
-108
lines changed

4 files changed

+680
-108
lines changed

llvm/include/llvm/Support/CommandLine.h

+82-5
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
#define LLVM_SUPPORT_COMMANDLINE_H
2222

2323
#include "llvm/ADT/ArrayRef.h"
24+
#include "llvm/ADT/SmallPtrSet.h"
2425
#include "llvm/ADT/SmallVector.h"
2526
#include "llvm/ADT/StringMap.h"
2627
#include "llvm/ADT/Twine.h"
2728
#include "llvm/Support/Compiler.h"
29+
#include "llvm/Support/ManagedStatic.h"
2830
#include <cassert>
2931
#include <climits>
3032
#include <cstdarg>
@@ -43,8 +45,9 @@ namespace cl {
4345
//===----------------------------------------------------------------------===//
4446
// ParseCommandLineOptions - Command line option processing entry point.
4547
//
46-
void ParseCommandLineOptions(int argc, const char *const *argv,
47-
const char *Overview = nullptr);
48+
bool ParseCommandLineOptions(int argc, const char *const *argv,
49+
const char *Overview = nullptr,
50+
bool IgnoreErrors = false);
4851

4952
//===----------------------------------------------------------------------===//
5053
// ParseEnvironmentOptions - Environment variable option processing alternate
@@ -170,6 +173,45 @@ class OptionCategory {
170173
// The general Option Category (used as default category).
171174
extern OptionCategory GeneralCategory;
172175

176+
//===----------------------------------------------------------------------===//
177+
// SubCommand class
178+
//
179+
class SubCommand {
180+
private:
181+
const char *const Name = nullptr;
182+
const char *const Description = nullptr;
183+
184+
protected:
185+
void registerSubCommand();
186+
void unregisterSubCommand();
187+
188+
public:
189+
SubCommand(const char *const Name, const char *const Description = nullptr)
190+
: Name(Name), Description(Description) {
191+
registerSubCommand();
192+
}
193+
SubCommand() {}
194+
195+
void reset();
196+
197+
operator bool() const;
198+
199+
const char *getName() const { return Name; }
200+
const char *getDescription() const { return Description; }
201+
202+
SmallVector<Option *, 4> PositionalOpts;
203+
SmallVector<Option *, 4> SinkOpts;
204+
StringMap<Option *> OptionsMap;
205+
206+
Option *ConsumeAfterOpt = nullptr; // The ConsumeAfter option if it exists.
207+
};
208+
209+
// A special subcommand representing no subcommand
210+
extern ManagedStatic<SubCommand> TopLevelSubCommand;
211+
212+
// A special subcommand that can be used to put an option into all subcommands.
213+
extern ManagedStatic<SubCommand> AllSubCommands;
214+
173215
//===----------------------------------------------------------------------===//
174216
// Option Base class
175217
//
@@ -209,6 +251,7 @@ class Option {
209251
StringRef HelpStr; // The descriptive text message for -help
210252
StringRef ValueStr; // String describing what the value of this option is
211253
OptionCategory *Category; // The Category this option belongs to
254+
SmallPtrSet<SubCommand *, 4> Subs; // The subcommands this option belongs to.
212255
bool FullyInitialized; // Has addArguemnt been called?
213256

214257
inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
@@ -229,6 +272,16 @@ class Option {
229272

230273
// hasArgStr - Return true if the argstr != ""
231274
bool hasArgStr() const { return !ArgStr.empty(); }
275+
bool isPositional() const { return getFormattingFlag() == cl::Positional; }
276+
bool isSink() const { return getMiscFlags() & cl::Sink; }
277+
bool isConsumeAfter() const {
278+
return getNumOccurrencesFlag() == cl::ConsumeAfter;
279+
}
280+
bool isInAllSubCommands() const {
281+
return std::any_of(Subs.begin(), Subs.end(), [](const SubCommand *SC) {
282+
return SC == &*AllSubCommands;
283+
});
284+
}
232285

233286
//-------------------------------------------------------------------------===
234287
// Accessor functions set by OptionModifiers
@@ -243,6 +296,7 @@ class Option {
243296
void setMiscFlag(enum MiscFlags M) { Misc |= M; }
244297
void setPosition(unsigned pos) { Position = pos; }
245298
void setCategory(OptionCategory &C) { Category = &C; }
299+
void addSubCommand(SubCommand &S) { Subs.insert(&S); }
246300

247301
protected:
248302
explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
@@ -287,6 +341,7 @@ class Option {
287341

288342
public:
289343
inline int getNumOccurrences() const { return NumOccurrences; }
344+
inline void reset() { NumOccurrences = 0; }
290345
virtual ~Option() {}
291346
};
292347

@@ -349,6 +404,14 @@ struct cat {
349404
template <class Opt> void apply(Opt &O) const { O.setCategory(Category); }
350405
};
351406

407+
// sub - Specify the subcommand that this option belongs to.
408+
struct sub {
409+
SubCommand &Sub;
410+
sub(SubCommand &S) : Sub(S) {}
411+
412+
template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
413+
};
414+
352415
//===----------------------------------------------------------------------===//
353416
// OptionValue class
354417

@@ -1589,6 +1652,7 @@ class alias : public Option {
15891652
error("cl::alias must have argument name specified!");
15901653
if (!AliasFor)
15911654
error("cl::alias must have an cl::aliasopt(option) specified!");
1655+
Subs = AliasFor->Subs;
15921656
addArgument();
15931657
}
15941658

@@ -1669,7 +1733,7 @@ void PrintHelpMessage(bool Hidden = false, bool Categorized = false);
16691733
/// Hopefully this API can be depricated soon. Any situation where options need
16701734
/// to be modified by tools or libraries should be handled by sane APIs rather
16711735
/// than just handing around a global list.
1672-
StringMap<Option *> &getRegisteredOptions();
1736+
StringMap<Option *> &getRegisteredOptions(SubCommand &Sub);
16731737

16741738
//===----------------------------------------------------------------------===//
16751739
// Standalone command line processing utilities.
@@ -1737,7 +1801,8 @@ bool ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer,
17371801
/// Some tools (like clang-format) like to be able to hide all options that are
17381802
/// not specific to the tool. This function allows a tool to specify a single
17391803
/// option category to display in the -help output.
1740-
void HideUnrelatedOptions(cl::OptionCategory &Category);
1804+
void HideUnrelatedOptions(cl::OptionCategory &Category,
1805+
SubCommand &Sub = *TopLevelSubCommand);
17411806

17421807
/// \brief Mark all options not part of the categories as cl::ReallyHidden.
17431808
///
@@ -1746,7 +1811,19 @@ void HideUnrelatedOptions(cl::OptionCategory &Category);
17461811
/// Some tools (like clang-format) like to be able to hide all options that are
17471812
/// not specific to the tool. This function allows a tool to specify a single
17481813
/// option category to display in the -help output.
1749-
void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories);
1814+
void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories,
1815+
SubCommand &Sub = *TopLevelSubCommand);
1816+
1817+
/// \brief Reset all command line options to a state that looks as if they have
1818+
/// never appeared on the command line. This is useful for being able to parse
1819+
/// a command line multiple times (especially useful for writing tests).
1820+
void ResetAllOptionOccurrences();
1821+
1822+
/// \brief Reset the command line parser back to its initial state. This
1823+
/// removes
1824+
/// all options, categories, and subcommands and returns the parser to a state
1825+
/// where no options are supported.
1826+
void ResetCommandLineParser();
17501827

17511828
} // End namespace cl
17521829

0 commit comments

Comments
 (0)