1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef ART_CMDLINE_CMDLINE_PARSER_H_
18 #define ART_CMDLINE_CMDLINE_PARSER_H_
19
20 #define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
21
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <string_view>
26 #include <tuple>
27 #include <unordered_map>
28 #include <unordered_set>
29 #include <vector>
30
31 #include "base/indenter.h"
32 #include "base/variant_map.h"
33 #include "cmdline_parse_result.h"
34 #include "cmdline_result.h"
35 #include "cmdline_type_parser.h"
36 #include "cmdline_types.h"
37 #include "detail/cmdline_debug_detail.h"
38 #include "detail/cmdline_parse_argument_detail.h"
39 #include "detail/cmdline_parser_detail.h"
40 #include "token_range.h"
41
42 namespace art {
43 // Build a parser for command line arguments with a small domain specific language.
44 // Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
45 // Each argument must also have a VariantMap::Key<T> in order to do the T storage.
46 template <typename TVariantMap,
47 template <typename TKeyValue> class TVariantMapKey>
48 struct CmdlineParser {
49 template <typename TArg>
50 struct ArgumentBuilder;
51
52 struct Builder; // Build the parser.
53 struct UntypedArgumentBuilder; // Build arguments which weren't yet given a type.
54
55 private:
56 // Forward declare some functions that we need to use before fully-defining structs.
57 template <typename TArg>
58 static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
59 static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
60
61 // Allow argument definitions to save their values when they are parsed,
62 // without having a dependency on CmdlineParser or any of the builders.
63 //
64 // A shared pointer to the save destination is saved into the load/save argument callbacks.
65 //
66 // This also allows the underlying storage (i.e. a variant map) to be released
67 // to the user, without having to recreate all of the callbacks.
68 struct SaveDestination {
SaveDestinationCmdlineParser::SaveDestination69 SaveDestination() : variant_map_(new TVariantMap()) {}
70
71 // Save value to the variant map.
72 template <typename TArg>
SaveToMapCmdlineParser::SaveDestination73 void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
74 variant_map_->Set(key, value);
75 }
76
77 // Get the existing value from a map, creating the value if it did not already exist.
78 template <typename TArg>
GetOrCreateFromMapCmdlineParser::SaveDestination79 TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
80 auto* ptr = variant_map_->Get(key);
81 if (ptr == nullptr) {
82 variant_map_->Set(key, TArg());
83 ptr = variant_map_->Get(key);
84 assert(ptr != nullptr);
85 }
86
87 return *ptr;
88 }
89
90 protected:
91 // Release the map, clearing it as a side-effect.
92 // Future saves will be distinct from previous saves.
ReleaseMapCmdlineParser::SaveDestination93 TVariantMap&& ReleaseMap() {
94 return std::move(*variant_map_);
95 }
96
97 // Get a read-only reference to the variant map.
GetMapCmdlineParser::SaveDestination98 const TVariantMap& GetMap() {
99 return *variant_map_;
100 }
101
102 // Clear all potential save targets.
ClearCmdlineParser::SaveDestination103 void Clear() {
104 variant_map_->Clear();
105 }
106
107 private:
108 // Don't try to copy or move this. Just don't.
109 SaveDestination(const SaveDestination&) = delete;
110 SaveDestination(SaveDestination&&) = delete;
111 SaveDestination& operator=(const SaveDestination&) = delete;
112 SaveDestination& operator=(SaveDestination&&) = delete;
113
114 std::shared_ptr<TVariantMap> variant_map_;
115
116 // Allow the parser to change the underlying pointers when we release the underlying storage.
117 friend struct CmdlineParser;
118 };
119
120 public:
121 // Builder for the argument definition of type TArg. Do not use this type directly,
122 // it is only a separate type to provide compile-time enforcement against doing
123 // illegal builds.
124 template <typename TArg>
125 struct ArgumentBuilder {
126 // Add a range check to this argument.
WithRangeCmdlineParser::ArgumentBuilder127 ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
128 argument_info_.has_range_ = true;
129 argument_info_.min_ = min;
130 argument_info_.max_ = max;
131
132 return *this;
133 }
134
135 // Map the list of names into the list of values. List of names must not have
136 // any wildcards '_' in it.
137 //
138 // Do not use if a value map has already been set.
WithValuesCmdlineParser::ArgumentBuilder139 ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
140 SetValuesInternal(value_list);
141 return *this;
142 }
143
144 // When used with a single alias, map the alias into this value.
145 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
WithValueCmdlineParser::ArgumentBuilder146 ArgumentBuilder<TArg> WithValue(const TArg& value) {
147 return WithValues({ value });
148 }
149
150 // Map the parsed string values (from _) onto a concrete value. If no wildcard
151 // has been specified, then map the value directly from the arg name (i.e.
152 // if there are multiple aliases, then use the alias to do the mapping).
153 //
154 // Do not use if a values list has already been set.
WithValueMapCmdlineParser::ArgumentBuilder155 ArgumentBuilder<TArg>& WithValueMap(
156 std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
157 assert(!argument_info_.has_value_list_);
158
159 argument_info_.has_value_map_ = true;
160 argument_info_.value_map_ = key_value_list;
161
162 return *this;
163 }
164
165 // If this argument is seen multiple times, successive arguments mutate the same value
166 // instead of replacing it with a new value.
AppendValuesCmdlineParser::ArgumentBuilder167 ArgumentBuilder<TArg>& AppendValues() {
168 argument_info_.appending_values_ = true;
169
170 return *this;
171 }
172
WithMetavarCmdlineParser::ArgumentBuilder173 ArgumentBuilder<TArg>& WithMetavar(const char* sv) {
174 argument_info_.metavar_ = sv;
175 return *this;
176 }
177
WithHelpCmdlineParser::ArgumentBuilder178 ArgumentBuilder<TArg>& WithHelp(const char* sv) {
179 argument_info_.help_ = sv;
180 return *this;
181 }
182
183 // Convenience type alias for the variant map key type definition.
184 using MapKey = TVariantMapKey<TArg>;
185
186 // Write the results of this argument into the key.
187 // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
IntoKeyCmdlineParser::ArgumentBuilder188 CmdlineParser::Builder& IntoKey(const MapKey& key) {
189 // Only capture save destination as a pointer.
190 // This allows the parser to later on change the specific save targets.
191 auto save_destination = save_destination_;
192 save_value_ = [save_destination, &key](TArg& value) {
193 save_destination->SaveToMap(key, value);
194 CMDLINE_DEBUG_LOG << "Saved value into map '"
195 << detail::ToStringAny(value) << "'" << std::endl;
196 };
197
198 load_value_ = [save_destination, &key]() -> TArg& {
199 TArg& value = save_destination->GetOrCreateFromMap(key);
200 CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
201 << std::endl;
202
203 return value;
204 };
205
206 save_value_specified_ = true;
207 load_value_specified_ = true;
208
209 CompleteArgument();
210 return parent_;
211 }
212
213 // Ensure we always move this when returning a new builder.
214 ArgumentBuilder(ArgumentBuilder&&) = default;
215
216 protected:
217 // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
IntoIgnoreCmdlineParser::ArgumentBuilder218 CmdlineParser::Builder& IntoIgnore() {
219 save_value_ = [](TArg& value) {
220 CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
221 };
222 load_value_ = []() -> TArg& {
223 assert(false && "Should not be appending values to ignored arguments");
224 __builtin_trap(); // Blow up.
225 };
226
227 save_value_specified_ = true;
228 load_value_specified_ = true;
229
230 CompleteArgument();
231 return parent_;
232 }
233
SetValuesInternalCmdlineParser::ArgumentBuilder234 void SetValuesInternal(const std::vector<TArg>&& value_list) {
235 assert(!argument_info_.has_value_map_);
236
237 argument_info_.has_value_list_ = true;
238 argument_info_.value_list_ = value_list;
239 }
240
SetNamesCmdlineParser::ArgumentBuilder241 void SetNames(std::vector<const char*>&& names) {
242 argument_info_.names_ = names;
243 }
244
SetNamesCmdlineParser::ArgumentBuilder245 void SetNames(std::initializer_list<const char*> names) {
246 argument_info_.names_ = names;
247 }
248
SetHelpCmdlineParser::ArgumentBuilder249 void SetHelp(std::optional<const char*>&& val) {
250 argument_info_.help_ = val;
251 }
252
SetCategoryCmdlineParser::ArgumentBuilder253 void SetCategory(std::optional<const char*>&& val) {
254 argument_info_.category_ = val;
255 }
SetMetavarCmdlineParser::ArgumentBuilder256 void SetMetavar(std::optional<const char*>&& val) {
257 argument_info_.metavar_ = val;
258 }
259
260 private:
261 // Copying is bad. Move only.
262 ArgumentBuilder(const ArgumentBuilder&) = delete;
263
264 // Called by any function that doesn't chain back into this builder.
265 // Completes the argument builder and save the information into the main builder.
CompleteArgumentCmdlineParser::ArgumentBuilder266 void CompleteArgument() {
267 assert(save_value_specified_ &&
268 "No Into... function called, nowhere to save parsed values to");
269 assert(load_value_specified_ &&
270 "No Into... function called, nowhere to load parsed values from");
271
272 argument_info_.CompleteArgument();
273
274 // Appending the completed argument is destructive. The object is no longer
275 // usable since all the useful information got moved out of it.
276 AppendCompletedArgument(parent_,
277 new detail::CmdlineParseArgument<TArg>(
278 std::move(argument_info_),
279 std::move(save_value_),
280 std::move(load_value_)));
281 }
282
283 friend struct CmdlineParser;
284 friend struct CmdlineParser::Builder;
285 friend struct CmdlineParser::UntypedArgumentBuilder;
286
ArgumentBuilderCmdlineParser::ArgumentBuilder287 ArgumentBuilder(CmdlineParser::Builder& parser,
288 std::shared_ptr<SaveDestination> save_destination)
289 : parent_(parser),
290 save_value_specified_(false),
291 load_value_specified_(false),
292 save_destination_(save_destination) {
293 save_value_ = [](TArg&) {
294 assert(false && "No save value function defined");
295 };
296
297 load_value_ = []() -> TArg& {
298 assert(false && "No load value function defined");
299 __builtin_trap(); // Blow up.
300 };
301 }
302
303 CmdlineParser::Builder& parent_;
304 std::function<void(TArg&)> save_value_;
305 std::function<TArg&(void)> load_value_;
306 bool save_value_specified_;
307 bool load_value_specified_;
308 detail::CmdlineParserArgumentInfo<TArg> argument_info_;
309
310 std::shared_ptr<SaveDestination> save_destination_;
311 };
312
313 struct UntypedArgumentBuilder {
314 // Set a type for this argument. The specific subcommand parser is looked up by the type.
315 template <typename TArg>
WithTypeCmdlineParser::UntypedArgumentBuilder316 ArgumentBuilder<TArg> WithType() {
317 return CreateTypedBuilder<TArg>();
318 }
319
WithHelpCmdlineParser::UntypedArgumentBuilder320 UntypedArgumentBuilder& WithHelp(const char* sv) {
321 SetHelp(sv);
322 return *this;
323 }
324
WithCategoryCmdlineParser::UntypedArgumentBuilder325 UntypedArgumentBuilder& WithCategory(const char* sv) {
326 SetCategory(sv);
327 return *this;
328 }
329
WithMetavarCmdlineParser::UntypedArgumentBuilder330 UntypedArgumentBuilder& WithMetavar(const char* sv) {
331 SetMetavar(sv);
332 return *this;
333 }
334
335 // When used with multiple aliases, map the position of the alias to the value position.
336 template <typename TArg>
WithValuesCmdlineParser::UntypedArgumentBuilder337 ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
338 auto&& a = CreateTypedBuilder<TArg>();
339 a.WithValues(values);
340 return std::move(a);
341 }
342
343 // When used with a single alias, map the alias into this value.
344 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
345 template <typename TArg>
WithValueCmdlineParser::UntypedArgumentBuilder346 ArgumentBuilder<TArg> WithValue(const TArg& value) {
347 return WithValues({ value });
348 }
349
350 // Set the current building argument to target this key.
351 // When this command line argument is parsed, it can be fetched with this key.
IntoKeyCmdlineParser::UntypedArgumentBuilder352 Builder& IntoKey(const TVariantMapKey<Unit>& key) {
353 return CreateTypedBuilder<Unit>().IntoKey(key);
354 }
355
356 // Ensure we always move this when returning a new builder.
357 UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
358
359 protected:
SetNamesCmdlineParser::UntypedArgumentBuilder360 void SetNames(std::vector<const char*>&& names) {
361 names_ = std::move(names);
362 }
363
SetNamesCmdlineParser::UntypedArgumentBuilder364 void SetNames(std::initializer_list<const char*> names) {
365 names_ = names;
366 }
367
SetHelpCmdlineParser::UntypedArgumentBuilder368 void SetHelp(std::optional<const char*> sv) {
369 help_.swap(sv);
370 }
371
SetMetavarCmdlineParser::UntypedArgumentBuilder372 void SetMetavar(std::optional<const char*> sv) {
373 metavar_.swap(sv);
374 }
375
SetCategoryCmdlineParser::UntypedArgumentBuilder376 void SetCategory(std::optional<const char*> sv) {
377 category_.swap(sv);
378 }
379
380 private:
381 // No copying. Move instead.
382 UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
383
384 template <typename TArg>
CreateTypedBuilderCmdlineParser::UntypedArgumentBuilder385 ArgumentBuilder<TArg> CreateTypedBuilder() {
386 auto&& b = CreateArgumentBuilder<TArg>(parent_);
387 InitializeTypedBuilder(&b); // Type-specific initialization
388 b.SetNames(std::move(names_));
389 b.SetHelp(std::move(help_));
390 b.SetCategory(std::move(category_));
391 b.SetMetavar(std::move(metavar_));
392 return std::move(b);
393 }
394
395 template <typename TArg = Unit>
396 typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder397 InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
398 // Every Unit argument implicitly maps to a runtime value of Unit{}
399 std::vector<Unit> values(names_.size(), Unit{});
400 arg_builder->SetValuesInternal(std::move(values));
401 }
402
403 // No extra work for all other types
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder404 void InitializeTypedBuilder(void*) {}
405
406 template <typename TArg>
407 friend struct ArgumentBuilder;
408 friend struct Builder;
409
UntypedArgumentBuilderCmdlineParser::UntypedArgumentBuilder410 explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
411 // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
412
413 CmdlineParser::Builder& parent_;
414 std::vector<const char*> names_;
415 std::optional<const char*> category_;
416 std::optional<const char*> help_;
417 std::optional<const char*> metavar_;
418 };
419
420 // Build a new parser given a chain of calls to define arguments.
421 struct Builder {
BuilderCmdlineParser::Builder422 Builder() : save_destination_(new SaveDestination()) {}
423
424 // Define a single argument. The default type is Unit.
DefineCmdlineParser::Builder425 UntypedArgumentBuilder Define(const char* name) {
426 return Define({name});
427 }
428
ClearCategoryCmdlineParser::Builder429 Builder& ClearCategory() {
430 default_category_.reset();
431 return *this;
432 }
433
SetCategoryCmdlineParser::Builder434 Builder& SetCategory(const char* sv) {
435 default_category_ = sv;
436 return *this;
437 }
438
OrderCategoriesCmdlineParser::Builder439 Builder& OrderCategories(std::vector<const char*> categories) {
440 category_order_.swap(categories);
441 return *this;
442 }
443
444 // Define a single argument with multiple aliases.
DefineCmdlineParser::Builder445 UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
446 auto&& b = UntypedArgumentBuilder(*this);
447 b.SetNames(names);
448 b.SetCategory(default_category_);
449 return std::move(b);
450 }
451
452 // Whether the parser should give up on unrecognized arguments. Not recommended.
IgnoreUnrecognizedCmdlineParser::Builder453 Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
454 ignore_unrecognized_ = ignore_unrecognized;
455 return *this;
456 }
457
458 // Provide a list of arguments to ignore for backwards compatibility.
IgnoreCmdlineParser::Builder459 Builder& Ignore(std::initializer_list<const char*> ignore_list) {
460 auto current_cat = default_category_;
461 default_category_ = "Ignored";
462 for (auto&& ignore_name : ignore_list) {
463 std::string ign = ignore_name;
464
465 // Ignored arguments are just like a regular definition which have very
466 // liberal parsing requirements (no range checks, no value checks).
467 // Unlike regular argument definitions, when a value gets parsed into its
468 // stronger type, we just throw it away.
469
470 if (ign.find('_') != std::string::npos) { // Does the arg-def have a wildcard?
471 // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
472 auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
473 assert(&builder == this);
474 (void)builder; // Ignore pointless unused warning, it's used in the assert.
475 } else {
476 // pretend this is a unit, e.g. -Xjitblocking
477 auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
478 assert(&builder == this);
479 (void)builder; // Ignore pointless unused warning, it's used in the assert.
480 }
481 }
482 ignore_list_ = ignore_list;
483 default_category_ = current_cat;
484 return *this;
485 }
486
487 // Finish building the parser; performs a check of the validity. Return value is moved, not
488 // copied. Do not call this more than once.
BuildCmdlineParser::Builder489 CmdlineParser Build() {
490 assert(!built_);
491 built_ = true;
492
493 auto&& p = CmdlineParser(ignore_unrecognized_,
494 std::move(ignore_list_),
495 save_destination_,
496 std::move(completed_arguments_),
497 std::move(category_order_));
498
499 return std::move(p);
500 }
501
502 protected:
AppendCompletedArgumentCmdlineParser::Builder503 void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
504 auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
505 completed_arguments_.push_back(std::move(smart_ptr));
506 }
507
508 private:
509 // No copying now!
510 Builder(const Builder& other) = delete;
511
512 template <typename TArg>
513 friend struct ArgumentBuilder;
514 friend struct UntypedArgumentBuilder;
515 friend struct CmdlineParser;
516
517 bool built_ = false;
518 bool ignore_unrecognized_ = false;
519 std::vector<const char*> ignore_list_;
520 std::shared_ptr<SaveDestination> save_destination_;
521 std::optional<const char*> default_category_;
522 std::vector<const char*> category_order_;
523
524 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
525 };
526
527 void DumpHelp(VariableIndentationOutputStream& vios);
528
ParseCmdlineParser529 CmdlineResult Parse(const std::string& argv) {
530 std::vector<std::string> tokenized;
531 Split(argv, ' ', &tokenized);
532
533 return Parse(TokenRange(std::move(tokenized)));
534 }
535
536 // Parse the arguments; storing results into the arguments map. Returns success value.
ParseCmdlineParser537 CmdlineResult Parse(const char* argv) {
538 return Parse(std::string(argv));
539 }
540
541 // Parse the arguments; storing the results into the arguments map. Returns success value.
542 // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser543 CmdlineResult Parse(const std::vector<const char*>& argv) {
544 return Parse(TokenRange(argv.begin(), argv.end()));
545 }
546
547 // Parse the arguments; storing the results into the arguments map. Returns success value.
548 // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser549 CmdlineResult Parse(const std::vector<std::string>& argv) {
550 return Parse(TokenRange(argv.begin(), argv.end()));
551 }
552
553 // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
554 // Assumes that argv[0] is the program name, and ignores it.
ParseCmdlineParser555 CmdlineResult Parse(const char* argv[], int argc) {
556 return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name
557 }
558
559 // Look up the arguments that have been parsed; use the target keys to lookup individual args.
GetArgumentsMapCmdlineParser560 const TVariantMap& GetArgumentsMap() const {
561 return save_destination_->GetMap();
562 }
563
564 // Release the arguments map that has been parsed; useful for move semantics.
ReleaseArgumentsMapCmdlineParser565 TVariantMap&& ReleaseArgumentsMap() {
566 return save_destination_->ReleaseMap();
567 }
568
569 // How many arguments were defined?
CountDefinedArgumentsCmdlineParser570 size_t CountDefinedArguments() const {
571 return completed_arguments_.size();
572 }
573
574 // Ensure we have a default move constructor.
575 CmdlineParser(CmdlineParser&&) = default;
576 // Ensure we have a default move assignment operator.
577 CmdlineParser& operator=(CmdlineParser&&) = default;
578
579 private:
580 friend struct Builder;
581
582 // Construct a new parser from the builder. Move all the arguments.
CmdlineParserCmdlineParser583 CmdlineParser(bool ignore_unrecognized,
584 std::vector<const char*>&& ignore_list,
585 std::shared_ptr<SaveDestination> save_destination,
586 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments,
587 std::vector<const char*>&& category_order)
588 : ignore_unrecognized_(ignore_unrecognized),
589 ignore_list_(std::move(ignore_list)),
590 save_destination_(save_destination),
591 completed_arguments_(std::move(completed_arguments)),
592 category_order_(category_order) {
593 assert(save_destination != nullptr);
594 }
595
596 // Parse the arguments; storing results into the arguments map. Returns success value.
597 // The parsing will fail on the first non-success parse result and return that error.
598 //
599 // All previously-parsed arguments are cleared out.
600 // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
601 // A partial parse will result only in a partial save of the arguments.
ParseCmdlineParser602 CmdlineResult Parse(TokenRange&& arguments_list) {
603 save_destination_->Clear();
604
605 for (size_t i = 0; i < arguments_list.Size(); ) {
606 TokenRange possible_name = arguments_list.Slice(i);
607
608 size_t best_match_size = 0; // How many tokens were matched in the best case.
609 size_t best_match_arg_idx = 0;
610 bool matched = false; // At least one argument definition has been matched?
611
612 // Find the closest argument definition for the remaining token range.
613 size_t arg_idx = 0;
614 for (auto&& arg : completed_arguments_) {
615 size_t local_match = arg->MaybeMatches(possible_name);
616
617 if (local_match > best_match_size) {
618 best_match_size = local_match;
619 best_match_arg_idx = arg_idx;
620 matched = true;
621 }
622 arg_idx++;
623 }
624
625 // Saw some kind of unknown argument
626 if (matched == false) {
627 if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI.
628 // Consume 1 token and keep going, hopefully the next token is a good one.
629 ++i;
630 continue;
631 }
632 // Common case:
633 // Bail out on the first unknown argument with an error.
634 return CmdlineResult(CmdlineResult::kUnknown,
635 std::string("Unknown argument: ") + possible_name[0]);
636 }
637
638 // Look at the best-matched argument definition and try to parse against that.
639 auto&& arg = completed_arguments_[best_match_arg_idx];
640
641 assert(arg->MaybeMatches(possible_name) == best_match_size);
642
643 // Try to parse the argument now, if we have enough tokens.
644 std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
645 size_t min_tokens;
646 size_t max_tokens;
647
648 std::tie(min_tokens, max_tokens) = num_tokens;
649
650 if ((i + min_tokens) > arguments_list.Size()) {
651 // expected longer command line but it was too short
652 // e.g. if the argv was only "-Xms" without specifying a memory option
653 CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
654 " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
655 return CmdlineResult(CmdlineResult::kFailure,
656 std::string("Argument ") +
657 possible_name[0] + ": incomplete command line arguments, expected "
658 + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
659 " more tokens");
660 }
661
662 if (best_match_size > max_tokens || best_match_size < min_tokens) {
663 // Even our best match was out of range, so parsing would fail instantly.
664 return CmdlineResult(CmdlineResult::kFailure,
665 std::string("Argument ") + possible_name[0] + ": too few tokens "
666 "matched " + std::to_string(best_match_size)
667 + " but wanted " + std::to_string(num_tokens.first));
668 }
669
670 // We have enough tokens to begin exact parsing.
671 TokenRange exact_range = possible_name.Slice(0, max_tokens);
672
673 size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error
674 CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
675
676 if (parse_attempt.IsError()) {
677 // We may also want to continue parsing the other tokens to gather more errors.
678 return parse_attempt;
679 } // else the value has been successfully stored into the map
680
681 assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse
682 i += consumed_tokens;
683
684 // TODO: also handle ignoring arguments for backwards compatibility
685 } // for
686
687 return CmdlineResult(CmdlineResult::kSuccess);
688 }
689
690 bool ignore_unrecognized_ = false;
691 std::vector<const char*> ignore_list_;
692 std::shared_ptr<SaveDestination> save_destination_;
693 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
694 std::vector<const char*> category_order_;
695 };
696
697 // This has to be defined after everything else, since we want the builders to call this.
698 template <typename TVariantMap,
699 template <typename TKeyValue> class TVariantMapKey>
700 template <typename TArg>
701 typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
CreateArgumentBuilder(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & parent)702 CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
703 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
704 return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
705 parent, parent.save_destination_);
706 }
707
708 // This has to be defined after everything else, since we want the builders to call this.
709 template <typename TVariantMap,
710 template <typename TKeyValue> class TVariantMapKey>
AppendCompletedArgument(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & builder,detail::CmdlineParseArgumentAny * arg)711 void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
712 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
713 detail::CmdlineParseArgumentAny* arg) {
714 builder.AppendCompletedArgument(arg);
715 }
716
717 template <typename TVariantMap,
718 template <typename TKeyValue> class TVariantMapKey>
DumpHelp(VariableIndentationOutputStream & vios)719 void CmdlineParser<TVariantMap, TVariantMapKey>::DumpHelp(VariableIndentationOutputStream& vios) {
720 std::vector<detail::CmdlineParseArgumentAny*> uncat;
721 std::unordered_map<std::string, std::vector<detail::CmdlineParseArgumentAny*>> args;
722 for (const std::unique_ptr<detail::CmdlineParseArgumentAny>& it : completed_arguments_) {
723 auto cat = it->GetCategory();
724 if (cat) {
725 if (args.find(cat.value()) == args.end()) {
726 args[cat.value()] = {};
727 }
728 args.at(cat.value()).push_back(it.get());
729 } else {
730 uncat.push_back(it.get());
731 }
732 }
733 args.erase("Ignored");
734 for (auto arg : uncat) {
735 arg->DumpHelp(vios);
736 vios.Stream();
737 }
738 for (auto it : category_order_) {
739 auto cur = args.find(it);
740 if (cur != args.end() && !cur->second.empty()) {
741 vios.Stream() << "The following " << it << " arguments are supported:" << std::endl;
742 ScopedIndentation si(&vios);
743 for (detail::CmdlineParseArgumentAny* arg : cur->second) {
744 arg->DumpHelp(vios);
745 vios.Stream();
746 }
747 args.erase(cur->first);
748 }
749 }
750 for (auto [cat, lst] : args) {
751 vios.Stream() << "The following " << cat << " arguments are supported:" << std::endl;
752 ScopedIndentation si(&vios);
753 for (auto& arg : completed_arguments_) {
754 arg->DumpHelp(vios);
755 vios.Stream();
756 }
757 }
758 vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl;
759 ScopedIndentation si(&vios);
760 for (auto ign : ignore_list_) {
761 vios.Stream() << ign << std::endl;
762 }
763 }
764
765 } // namespace art
766
767 #endif // ART_CMDLINE_CMDLINE_PARSER_H_
768