Skip to content

Commit 0f4202f

Browse files
Zachary Turnerpow2clk
authored andcommitted
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 (cherry picked from LLVM commit 07670b3e984db32f291373fe12c392959f2aff67)
1 parent 4c4a94a commit 0f4202f

4 files changed

Lines changed: 682 additions & 110 deletions

File tree

include/llvm/Support/CommandLine.h

Lines changed: 84 additions & 7 deletions
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>
@@ -44,8 +46,9 @@ namespace cl {
4446
//===----------------------------------------------------------------------===//
4547
// ParseCommandLineOptions - Command line option processing entry point.
4648
//
47-
void ParseCommandLineOptions(int argc, const char *const *argv,
48-
const char *Overview = nullptr);
49+
bool ParseCommandLineOptions(int argc, const char *const *argv,
50+
const char *Overview = nullptr,
51+
bool IgnoreErrors = false);
4952

5053
//===----------------------------------------------------------------------===//
5154
// ParseEnvironmentOptions - Environment variable option processing alternate
@@ -171,6 +174,45 @@ class OptionCategory {
171174
// The general Option Category (used as default category).
172175
extern OptionCategory *GeneralCategory; // HLSL Change - GeneralCategory is now a pointer
173176

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

215258
inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
@@ -230,6 +273,16 @@ class Option {
230273

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

234287
//-------------------------------------------------------------------------===
235288
// Accessor functions set by OptionModifiers
@@ -244,6 +297,7 @@ class Option {
244297
void setMiscFlag(enum MiscFlags M) { Misc |= M; }
245298
void setPosition(unsigned pos) { Position = pos; }
246299
void setCategory(OptionCategory &C) { Category = &C; }
300+
void addSubCommand(SubCommand &S) { Subs.insert(&S); }
247301

248302
protected:
249303
explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
@@ -288,6 +342,7 @@ class Option {
288342

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

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

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

@@ -1590,6 +1653,7 @@ class alias : public Option {
15901653
error("cl::alias must have argument name specified!");
15911654
if (!AliasFor)
15921655
error("cl::alias must have an cl::aliasopt(option) specified!");
1656+
Subs = AliasFor->Subs;
15931657
addArgument();
15941658
}
15951659

@@ -1670,9 +1734,9 @@ void PrintHelpMessage(bool Hidden = false, bool Categorized = false);
16701734
/// Hopefully this API can be depricated soon. Any situation where options need
16711735
/// to be modified by tools or libraries should be handled by sane APIs rather
16721736
/// than just handing around a global list.
1673-
StringMap<Option *> &getRegisteredOptions();
1674-
// //
1675-
///////////////////////////////////////////////////////////////////////////////
1737+
StringMap<Option *> &getRegisteredOptions(SubCommand &Sub);
1738+
1739+
//===----------------------------------------------------------------------===//
16761740
// Standalone command line processing utilities.
16771741
//
16781742

@@ -1738,7 +1802,8 @@ bool ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer,
17381802
/// Some tools (like clang-format) like to be able to hide all options that are
17391803
/// not specific to the tool. This function allows a tool to specify a single
17401804
/// option category to display in the -help output.
1741-
void HideUnrelatedOptions(cl::OptionCategory &Category);
1805+
void HideUnrelatedOptions(cl::OptionCategory &Category,
1806+
SubCommand &Sub = *TopLevelSubCommand);
17421807

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

17521829
} // End namespace cl
17531830

0 commit comments

Comments
 (0)