forked from DirtyUnicorns/android_art
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmdline_parser.h
633 lines (518 loc) · 23.4 KB
/
cmdline_parser.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_CMDLINE_CMDLINE_PARSER_H_
#define ART_CMDLINE_CMDLINE_PARSER_H_
#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
#include "detail/cmdline_debug_detail.h"
#include "detail/cmdline_parse_argument_detail.h"
#include "detail/cmdline_parser_detail.h"
#include "cmdline_parse_result.h"
#include "cmdline_result.h"
#include "cmdline_type_parser.h"
#include "cmdline_types.h"
#include "token_range.h"
#include "base/variant_map.h"
#include <memory>
#include <vector>
namespace art {
// Build a parser for command line arguments with a small domain specific language.
// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
struct CmdlineParser {
template <typename TArg>
struct ArgumentBuilder;
struct Builder; // Build the parser.
struct UntypedArgumentBuilder; // Build arguments which weren't yet given a type.
private:
// Forward declare some functions that we need to use before fully-defining structs.
template <typename TArg>
static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
// Allow argument definitions to save their values when they are parsed,
// without having a dependency on CmdlineParser or any of the builders.
//
// A shared pointer to the save destination is saved into the load/save argument callbacks.
//
// This also allows the underlying storage (i.e. a variant map) to be released
// to the user, without having to recreate all of the callbacks.
struct SaveDestination {
SaveDestination() : variant_map_(new TVariantMap()) {}
// Save value to the variant map.
template <typename TArg>
void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
variant_map_->Set(key, value);
}
// Get the existing value from a map, creating the value if it did not already exist.
template <typename TArg>
TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
auto* ptr = variant_map_->Get(key);
if (ptr == nullptr) {
variant_map_->Set(key, TArg());
ptr = variant_map_->Get(key);
assert(ptr != nullptr);
}
return *ptr;
}
protected:
// Release the map, clearing it as a side-effect.
// Future saves will be distinct from previous saves.
TVariantMap&& ReleaseMap() {
return std::move(*variant_map_);
}
// Get a read-only reference to the variant map.
const TVariantMap& GetMap() {
return *variant_map_;
}
// Clear all potential save targets.
void Clear() {
variant_map_->Clear();
}
private:
// Don't try to copy or move this. Just don't.
SaveDestination(const SaveDestination&) = delete;
SaveDestination(SaveDestination&&) = delete;
SaveDestination& operator=(const SaveDestination&) = delete;
SaveDestination& operator=(SaveDestination&&) = delete;
std::shared_ptr<TVariantMap> variant_map_;
// Allow the parser to change the underlying pointers when we release the underlying storage.
friend struct CmdlineParser;
};
public:
// Builder for the argument definition of type TArg. Do not use this type directly,
// it is only a separate type to provide compile-time enforcement against doing
// illegal builds.
template <typename TArg>
struct ArgumentBuilder {
// Add a range check to this argument.
ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
argument_info_.has_range_ = true;
argument_info_.min_ = min;
argument_info_.max_ = max;
return *this;
}
// Map the list of names into the list of values. List of names must not have
// any wildcards '_' in it.
//
// Do not use if a value map has already been set.
ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
SetValuesInternal(value_list);
return *this;
}
// When used with a single alias, map the alias into this value.
// Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
ArgumentBuilder<TArg> WithValue(const TArg& value) {
return WithValues({ value });
}
// Map the parsed string values (from _) onto a concrete value. If no wildcard
// has been specified, then map the value directly from the arg name (i.e.
// if there are multiple aliases, then use the alias to do the mapping).
//
// Do not use if a values list has already been set.
ArgumentBuilder<TArg>& WithValueMap(
std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
assert(!argument_info_.has_value_list_);
argument_info_.has_value_map_ = true;
argument_info_.value_map_ = key_value_list;
return *this;
}
// If this argument is seen multiple times, successive arguments mutate the same value
// instead of replacing it with a new value.
ArgumentBuilder<TArg>& AppendValues() {
argument_info_.appending_values_ = true;
return *this;
}
// Convenience type alias for the variant map key type definition.
using MapKey = TVariantMapKey<TArg>;
// Write the results of this argument into the key.
// To look up the parsed arguments, get the map and then use this key with VariantMap::Get
CmdlineParser::Builder& IntoKey(const MapKey& key) {
// Only capture save destination as a pointer.
// This allows the parser to later on change the specific save targets.
auto save_destination = save_destination_;
save_value_ = [save_destination, &key](TArg& value) {
save_destination->SaveToMap(key, value);
CMDLINE_DEBUG_LOG << "Saved value into map '"
<< detail::ToStringAny(value) << "'" << std::endl;
};
load_value_ = [save_destination, &key]() -> TArg& {
TArg& value = save_destination->GetOrCreateFromMap(key);
CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
<< std::endl;
return value;
};
save_value_specified_ = true;
load_value_specified_ = true;
CompleteArgument();
return parent_;
}
// Ensure we always move this when returning a new builder.
ArgumentBuilder(ArgumentBuilder&&) = default;
protected:
// Used by builder to internally ignore arguments by dropping them on the floor after parsing.
CmdlineParser::Builder& IntoIgnore() {
save_value_ = [](TArg& value) {
CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
};
load_value_ = []() -> TArg& {
assert(false && "Should not be appending values to ignored arguments");
__builtin_trap(); // Blow up.
};
save_value_specified_ = true;
load_value_specified_ = true;
CompleteArgument();
return parent_;
}
void SetValuesInternal(const std::vector<TArg>&& value_list) {
assert(!argument_info_.has_value_map_);
argument_info_.has_value_list_ = true;
argument_info_.value_list_ = value_list;
}
void SetNames(std::vector<const char*>&& names) {
argument_info_.names_ = names;
}
void SetNames(std::initializer_list<const char*> names) {
argument_info_.names_ = names;
}
private:
// Copying is bad. Move only.
ArgumentBuilder(const ArgumentBuilder&) = delete;
// Called by any function that doesn't chain back into this builder.
// Completes the argument builder and save the information into the main builder.
void CompleteArgument() {
assert(save_value_specified_ &&
"No Into... function called, nowhere to save parsed values to");
assert(load_value_specified_ &&
"No Into... function called, nowhere to load parsed values from");
argument_info_.CompleteArgument();
// Appending the completed argument is destructive. The object is no longer
// usable since all the useful information got moved out of it.
AppendCompletedArgument(parent_,
new detail::CmdlineParseArgument<TArg>(
std::move(argument_info_),
std::move(save_value_),
std::move(load_value_)));
}
friend struct CmdlineParser;
friend struct CmdlineParser::Builder;
friend struct CmdlineParser::UntypedArgumentBuilder;
ArgumentBuilder(CmdlineParser::Builder& parser,
std::shared_ptr<SaveDestination> save_destination)
: parent_(parser),
save_value_specified_(false),
load_value_specified_(false),
save_destination_(save_destination) {
save_value_ = [](TArg&) {
assert(false && "No save value function defined");
};
load_value_ = []() -> TArg& {
assert(false && "No load value function defined");
__builtin_trap(); // Blow up.
};
}
CmdlineParser::Builder& parent_;
std::function<void(TArg&)> save_value_;
std::function<TArg&(void)> load_value_;
bool save_value_specified_;
bool load_value_specified_;
detail::CmdlineParserArgumentInfo<TArg> argument_info_;
std::shared_ptr<SaveDestination> save_destination_;
};
struct UntypedArgumentBuilder {
// Set a type for this argument. The specific subcommand parser is looked up by the type.
template <typename TArg>
ArgumentBuilder<TArg> WithType() {
return CreateTypedBuilder<TArg>();
}
// When used with multiple aliases, map the position of the alias to the value position.
template <typename TArg>
ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
auto&& a = CreateTypedBuilder<TArg>();
a.WithValues(values);
return std::move(a);
}
// When used with a single alias, map the alias into this value.
// Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
template <typename TArg>
ArgumentBuilder<TArg> WithValue(const TArg& value) {
return WithValues({ value });
}
// Set the current building argument to target this key.
// When this command line argument is parsed, it can be fetched with this key.
Builder& IntoKey(const TVariantMapKey<Unit>& key) {
return CreateTypedBuilder<Unit>().IntoKey(key);
}
// Ensure we always move this when returning a new builder.
UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
protected:
void SetNames(std::vector<const char*>&& names) {
names_ = std::move(names);
}
void SetNames(std::initializer_list<const char*> names) {
names_ = names;
}
private:
// No copying. Move instead.
UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
template <typename TArg>
ArgumentBuilder<TArg> CreateTypedBuilder() {
auto&& b = CreateArgumentBuilder<TArg>(parent_);
InitializeTypedBuilder(&b); // Type-specific initialization
b.SetNames(std::move(names_));
return std::move(b);
}
template <typename TArg = Unit>
typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
// Every Unit argument implicitly maps to a runtime value of Unit{}
std::vector<Unit> values(names_.size(), Unit{});
arg_builder->SetValuesInternal(std::move(values));
}
// No extra work for all other types
void InitializeTypedBuilder(void*) {}
template <typename TArg>
friend struct ArgumentBuilder;
friend struct Builder;
explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
// UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
CmdlineParser::Builder& parent_;
std::vector<const char*> names_;
};
// Build a new parser given a chain of calls to define arguments.
struct Builder {
Builder() : save_destination_(new SaveDestination()) {}
// Define a single argument. The default type is Unit.
UntypedArgumentBuilder Define(const char* name) {
return Define({name});
}
// Define a single argument with multiple aliases.
UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
auto&& b = UntypedArgumentBuilder(*this);
b.SetNames(names);
return std::move(b);
}
// Whether the parser should give up on unrecognized arguments. Not recommended.
Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
ignore_unrecognized_ = ignore_unrecognized;
return *this;
}
// Provide a list of arguments to ignore for backwards compatibility.
Builder& Ignore(std::initializer_list<const char*> ignore_list) {
for (auto&& ignore_name : ignore_list) {
std::string ign = ignore_name;
// Ignored arguments are just like a regular definition which have very
// liberal parsing requirements (no range checks, no value checks).
// Unlike regular argument definitions, when a value gets parsed into its
// stronger type, we just throw it away.
if (ign.find('_') != std::string::npos) { // Does the arg-def have a wildcard?
// pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
assert(&builder == this);
(void)builder; // Ignore pointless unused warning, it's used in the assert.
} else {
// pretend this is a unit, e.g. -Xjitblocking
auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
assert(&builder == this);
(void)builder; // Ignore pointless unused warning, it's used in the assert.
}
}
ignore_list_ = ignore_list;
return *this;
}
// Finish building the parser; performs sanity checks. Return value is moved, not copied.
// Do not call this more than once.
CmdlineParser Build() {
assert(!built_);
built_ = true;
auto&& p = CmdlineParser(ignore_unrecognized_,
std::move(ignore_list_),
save_destination_,
std::move(completed_arguments_));
return std::move(p);
}
protected:
void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
completed_arguments_.push_back(std::move(smart_ptr));
}
private:
// No copying now!
Builder(const Builder& other) = delete;
template <typename TArg>
friend struct ArgumentBuilder;
friend struct UntypedArgumentBuilder;
friend struct CmdlineParser;
bool built_ = false;
bool ignore_unrecognized_ = false;
std::vector<const char*> ignore_list_;
std::shared_ptr<SaveDestination> save_destination_;
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
};
CmdlineResult Parse(const std::string& argv) {
std::vector<std::string> tokenized;
Split(argv, ' ', &tokenized);
return Parse(TokenRange(std::move(tokenized)));
}
// Parse the arguments; storing results into the arguments map. Returns success value.
CmdlineResult Parse(const char* argv) {
return Parse(std::string(argv));
}
// Parse the arguments; storing the results into the arguments map. Returns success value.
// Assumes that argv[0] is a valid argument (i.e. not the program name).
CmdlineResult Parse(const std::vector<const char*>& argv) {
return Parse(TokenRange(argv.begin(), argv.end()));
}
// Parse the arguments; storing the results into the arguments map. Returns success value.
// Assumes that argv[0] is a valid argument (i.e. not the program name).
CmdlineResult Parse(const std::vector<std::string>& argv) {
return Parse(TokenRange(argv.begin(), argv.end()));
}
// Parse the arguments (directly from an int main(argv,argc)). Returns success value.
// Assumes that argv[0] is the program name, and ignores it.
CmdlineResult Parse(const char* argv[], int argc) {
return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name
}
// Look up the arguments that have been parsed; use the target keys to lookup individual args.
const TVariantMap& GetArgumentsMap() const {
return save_destination_->GetMap();
}
// Release the arguments map that has been parsed; useful for move semantics.
TVariantMap&& ReleaseArgumentsMap() {
return save_destination_->ReleaseMap();
}
// How many arguments were defined?
size_t CountDefinedArguments() const {
return completed_arguments_.size();
}
// Ensure we have a default move constructor.
CmdlineParser(CmdlineParser&&) = default;
// Ensure we have a default move assignment operator.
CmdlineParser& operator=(CmdlineParser&&) = default;
private:
friend struct Builder;
// Construct a new parser from the builder. Move all the arguments.
CmdlineParser(bool ignore_unrecognized,
std::vector<const char*>&& ignore_list,
std::shared_ptr<SaveDestination> save_destination,
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments)
: ignore_unrecognized_(ignore_unrecognized),
ignore_list_(std::move(ignore_list)),
save_destination_(save_destination),
completed_arguments_(std::move(completed_arguments)) {
assert(save_destination != nullptr);
}
// Parse the arguments; storing results into the arguments map. Returns success value.
// The parsing will fail on the first non-success parse result and return that error.
//
// All previously-parsed arguments are cleared out.
// Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
// A partial parse will result only in a partial save of the arguments.
CmdlineResult Parse(TokenRange&& arguments_list) {
save_destination_->Clear();
for (size_t i = 0; i < arguments_list.Size(); ) {
TokenRange possible_name = arguments_list.Slice(i);
size_t best_match_size = 0; // How many tokens were matched in the best case.
size_t best_match_arg_idx = 0;
bool matched = false; // At least one argument definition has been matched?
// Find the closest argument definition for the remaining token range.
size_t arg_idx = 0;
for (auto&& arg : completed_arguments_) {
size_t local_match = arg->MaybeMatches(possible_name);
if (local_match > best_match_size) {
best_match_size = local_match;
best_match_arg_idx = arg_idx;
matched = true;
}
arg_idx++;
}
// Saw some kind of unknown argument
if (matched == false) {
if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI.
// Consume 1 token and keep going, hopefully the next token is a good one.
++i;
continue;
}
// Common case:
// Bail out on the first unknown argument with an error.
return CmdlineResult(CmdlineResult::kUnknown,
std::string("Unknown argument: ") + possible_name[0]);
}
// Look at the best-matched argument definition and try to parse against that.
auto&& arg = completed_arguments_[best_match_arg_idx];
assert(arg->MaybeMatches(possible_name) == best_match_size);
// Try to parse the argument now, if we have enough tokens.
std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
size_t min_tokens;
size_t max_tokens;
std::tie(min_tokens, max_tokens) = num_tokens;
if ((i + min_tokens) > arguments_list.Size()) {
// expected longer command line but it was too short
// e.g. if the argv was only "-Xms" without specifying a memory option
CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
" num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
return CmdlineResult(CmdlineResult::kFailure,
std::string("Argument ") +
possible_name[0] + ": incomplete command line arguments, expected "
+ std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
" more tokens");
}
if (best_match_size > max_tokens || best_match_size < min_tokens) {
// Even our best match was out of range, so parsing would fail instantly.
return CmdlineResult(CmdlineResult::kFailure,
std::string("Argument ") + possible_name[0] + ": too few tokens "
"matched " + std::to_string(best_match_size)
+ " but wanted " + std::to_string(num_tokens.first));
}
// We have enough tokens to begin exact parsing.
TokenRange exact_range = possible_name.Slice(0, max_tokens);
size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error
CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
if (parse_attempt.IsError()) {
// We may also want to continue parsing the other tokens to gather more errors.
return parse_attempt;
} // else the value has been successfully stored into the map
assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse
i += consumed_tokens;
// TODO: also handle ignoring arguments for backwards compatibility
} // for
return CmdlineResult(CmdlineResult::kSuccess);
}
bool ignore_unrecognized_ = false;
std::vector<const char*> ignore_list_;
std::shared_ptr<SaveDestination> save_destination_;
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
};
// This has to be defined after everything else, since we want the builders to call this.
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
template <typename TArg>
typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
parent, parent.save_destination_);
}
// This has to be defined after everything else, since we want the builders to call this.
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
detail::CmdlineParseArgumentAny* arg) {
builder.AppendCompletedArgument(arg);
}
} // namespace art
#endif // ART_CMDLINE_CMDLINE_PARSER_H_