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 // Write the results of this argument into a variable pointed to by destination.
214 // An optional is used to tell whether the command line argument was present.
IntoLocationCmdlineParser::ArgumentBuilder215 CmdlineParser::Builder& IntoLocation(std::optional<TArg>* destination) {
216 save_value_ = [destination](TArg& value) {
217 *destination = value;
218 };
219
220 load_value_ = [destination]() -> TArg& {
221 return destination->value();
222 };
223
224 save_value_specified_ = true;
225 load_value_specified_ = true;
226
227 CompleteArgument();
228 return parent_;
229 }
230
231 // Ensure we always move this when returning a new builder.
232 ArgumentBuilder(ArgumentBuilder&&) noexcept = default;
233
234 protected:
235 // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
IntoIgnoreCmdlineParser::ArgumentBuilder236 CmdlineParser::Builder& IntoIgnore() {
237 save_value_ = [](TArg& value) {
238 CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
239 };
240 load_value_ = []() -> TArg& {
241 assert(false && "Should not be appending values to ignored arguments");
242 __builtin_trap(); // Blow up.
243 };
244
245 save_value_specified_ = true;
246 load_value_specified_ = true;
247
248 CompleteArgument();
249 return parent_;
250 }
251
SetValuesInternalCmdlineParser::ArgumentBuilder252 void SetValuesInternal(const std::vector<TArg>&& value_list) {
253 assert(!argument_info_.has_value_map_);
254
255 argument_info_.has_value_list_ = true;
256 argument_info_.value_list_ = value_list;
257 }
258
SetNamesCmdlineParser::ArgumentBuilder259 void SetNames(std::vector<const char*>&& names) {
260 argument_info_.names_ = names;
261 }
262
SetNamesCmdlineParser::ArgumentBuilder263 void SetNames(std::initializer_list<const char*> names) {
264 argument_info_.names_ = names;
265 }
266
SetHelpCmdlineParser::ArgumentBuilder267 void SetHelp(std::optional<const char*>&& val) {
268 argument_info_.help_ = val;
269 }
270
SetCategoryCmdlineParser::ArgumentBuilder271 void SetCategory(std::optional<const char*>&& val) {
272 argument_info_.category_ = val;
273 }
SetMetavarCmdlineParser::ArgumentBuilder274 void SetMetavar(std::optional<const char*>&& val) {
275 argument_info_.metavar_ = val;
276 }
277
278 private:
279 // Copying is bad. Move only.
280 ArgumentBuilder(const ArgumentBuilder&) = delete;
281
282 // Called by any function that doesn't chain back into this builder.
283 // Completes the argument builder and save the information into the main builder.
CompleteArgumentCmdlineParser::ArgumentBuilder284 void CompleteArgument() {
285 assert(save_value_specified_ &&
286 "No Into... function called, nowhere to save parsed values to");
287 assert(load_value_specified_ &&
288 "No Into... function called, nowhere to load parsed values from");
289
290 argument_info_.CompleteArgument();
291
292 // Appending the completed argument is destructive. The object is no longer
293 // usable since all the useful information got moved out of it.
294 AppendCompletedArgument(parent_,
295 new detail::CmdlineParseArgument<TArg>(
296 std::move(argument_info_),
297 std::move(save_value_),
298 std::move(load_value_)));
299 }
300
301 friend struct CmdlineParser;
302 friend struct CmdlineParser::Builder;
303 friend struct CmdlineParser::UntypedArgumentBuilder;
304
ArgumentBuilderCmdlineParser::ArgumentBuilder305 ArgumentBuilder(CmdlineParser::Builder& parser,
306 std::shared_ptr<SaveDestination> save_destination)
307 : parent_(parser),
308 save_value_specified_(false),
309 load_value_specified_(false),
310 save_destination_(save_destination) {
311 save_value_ = [](TArg&) {
312 assert(false && "No save value function defined");
313 };
314
315 load_value_ = []() -> TArg& {
316 assert(false && "No load value function defined");
317 __builtin_trap(); // Blow up.
318 };
319 }
320
321 CmdlineParser::Builder& parent_;
322 std::function<void(TArg&)> save_value_;
323 std::function<TArg&(void)> load_value_;
324 bool save_value_specified_;
325 bool load_value_specified_;
326 detail::CmdlineParserArgumentInfo<TArg> argument_info_;
327
328 std::shared_ptr<SaveDestination> save_destination_;
329 };
330
331 struct UntypedArgumentBuilder {
332 // Set a type for this argument. The specific subcommand parser is looked up by the type.
333 template <typename TArg>
WithTypeCmdlineParser::UntypedArgumentBuilder334 ArgumentBuilder<TArg> WithType() {
335 return CreateTypedBuilder<TArg>();
336 }
337
WithHelpCmdlineParser::UntypedArgumentBuilder338 UntypedArgumentBuilder& WithHelp(const char* sv) {
339 SetHelp(sv);
340 return *this;
341 }
342
WithCategoryCmdlineParser::UntypedArgumentBuilder343 UntypedArgumentBuilder& WithCategory(const char* sv) {
344 SetCategory(sv);
345 return *this;
346 }
347
WithMetavarCmdlineParser::UntypedArgumentBuilder348 UntypedArgumentBuilder& WithMetavar(const char* sv) {
349 SetMetavar(sv);
350 return *this;
351 }
352
353 // When used with multiple aliases, map the position of the alias to the value position.
354 template <typename TArg>
WithValuesCmdlineParser::UntypedArgumentBuilder355 ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
356 auto&& a = CreateTypedBuilder<TArg>();
357 a.WithValues(values);
358 return std::move(a);
359 }
360
361 // When used with a single alias, map the alias into this value.
362 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
363 template <typename TArg>
WithValueCmdlineParser::UntypedArgumentBuilder364 ArgumentBuilder<TArg> WithValue(const TArg& value) {
365 return WithValues({ value });
366 }
367
368 // Set the current building argument to target this key.
369 // When this command line argument is parsed, it can be fetched with this key.
IntoKeyCmdlineParser::UntypedArgumentBuilder370 Builder& IntoKey(const TVariantMapKey<Unit>& key) {
371 return CreateTypedBuilder<Unit>().IntoKey(key);
372 }
373
374 // Ensure we always move this when returning a new builder.
375 UntypedArgumentBuilder(UntypedArgumentBuilder&&) noexcept = default;
376
377 protected:
SetNamesCmdlineParser::UntypedArgumentBuilder378 void SetNames(std::vector<const char*>&& names) {
379 names_ = std::move(names);
380 }
381
SetNamesCmdlineParser::UntypedArgumentBuilder382 void SetNames(std::initializer_list<const char*> names) {
383 names_ = names;
384 }
385
SetHelpCmdlineParser::UntypedArgumentBuilder386 void SetHelp(std::optional<const char*> sv) {
387 help_.swap(sv);
388 }
389
SetMetavarCmdlineParser::UntypedArgumentBuilder390 void SetMetavar(std::optional<const char*> sv) {
391 metavar_.swap(sv);
392 }
393
SetCategoryCmdlineParser::UntypedArgumentBuilder394 void SetCategory(std::optional<const char*> sv) {
395 category_.swap(sv);
396 }
397
398 private:
399 // No copying. Move instead.
400 UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
401
402 template <typename TArg>
CreateTypedBuilderCmdlineParser::UntypedArgumentBuilder403 ArgumentBuilder<TArg> CreateTypedBuilder() {
404 auto&& b = CreateArgumentBuilder<TArg>(parent_);
405 InitializeTypedBuilder(&b); // Type-specific initialization
406 b.SetNames(std::move(names_));
407 b.SetHelp(std::move(help_));
408 b.SetCategory(std::move(category_));
409 b.SetMetavar(std::move(metavar_));
410 return std::move(b);
411 }
412
413 template <typename TArg = Unit>
414 typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder415 InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
416 // Every Unit argument implicitly maps to a runtime value of Unit{}
417 std::vector<Unit> values(names_.size(), Unit{});
418 arg_builder->SetValuesInternal(std::move(values));
419 }
420
421 // No extra work for all other types
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder422 void InitializeTypedBuilder(void*) {}
423
424 template <typename TArg>
425 friend struct ArgumentBuilder;
426 friend struct Builder;
427
UntypedArgumentBuilderCmdlineParser::UntypedArgumentBuilder428 explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
429 // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
430
431 CmdlineParser::Builder& parent_;
432 std::vector<const char*> names_;
433 std::optional<const char*> category_;
434 std::optional<const char*> help_;
435 std::optional<const char*> metavar_;
436 };
437
438 // Build a new parser given a chain of calls to define arguments.
439 struct Builder {
BuilderCmdlineParser::Builder440 Builder() : save_destination_(new SaveDestination()) {}
441
442 // Define a single argument. The default type is Unit.
DefineCmdlineParser::Builder443 UntypedArgumentBuilder Define(const char* name) {
444 return Define({name});
445 }
446
ClearCategoryCmdlineParser::Builder447 Builder& ClearCategory() {
448 default_category_.reset();
449 return *this;
450 }
451
SetCategoryCmdlineParser::Builder452 Builder& SetCategory(const char* sv) {
453 default_category_ = sv;
454 return *this;
455 }
456
OrderCategoriesCmdlineParser::Builder457 Builder& OrderCategories(std::vector<const char*> categories) {
458 category_order_.swap(categories);
459 return *this;
460 }
461
462 // Define a single argument with multiple aliases.
DefineCmdlineParser::Builder463 UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
464 auto&& b = UntypedArgumentBuilder(*this);
465 b.SetNames(names);
466 b.SetCategory(default_category_);
467 return std::move(b);
468 }
469
470 // Whether the parser should give up on unrecognized arguments. Not recommended.
IgnoreUnrecognizedCmdlineParser::Builder471 Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
472 ignore_unrecognized_ = ignore_unrecognized;
473 return *this;
474 }
475
476 // Provide a list of arguments to ignore for backwards compatibility.
IgnoreCmdlineParser::Builder477 Builder& Ignore(std::initializer_list<const char*> ignore_list) {
478 auto current_cat = default_category_;
479 default_category_ = "Ignored";
480 for (auto&& ignore_name : ignore_list) {
481 std::string ign = ignore_name;
482
483 // Ignored arguments are just like a regular definition which have very
484 // liberal parsing requirements (no range checks, no value checks).
485 // Unlike regular argument definitions, when a value gets parsed into its
486 // stronger type, we just throw it away.
487
488 if (ign.find('_') != std::string::npos) { // Does the arg-def have a wildcard?
489 // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
490 auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
491 assert(&builder == this);
492 (void)builder; // Ignore pointless unused warning, it's used in the assert.
493 } else {
494 // pretend this is a unit, e.g. -Xjitblocking
495 auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
496 assert(&builder == this);
497 (void)builder; // Ignore pointless unused warning, it's used in the assert.
498 }
499 }
500 ignore_list_ = ignore_list;
501 default_category_ = current_cat;
502 return *this;
503 }
504
505 // Finish building the parser; performs a check of the validity. Return value is moved, not
506 // copied. Do not call this more than once.
BuildCmdlineParser::Builder507 CmdlineParser Build() {
508 assert(!built_);
509 built_ = true;
510
511 auto&& p = CmdlineParser(ignore_unrecognized_,
512 std::move(ignore_list_),
513 save_destination_,
514 std::move(completed_arguments_),
515 std::move(category_order_));
516
517 return std::move(p);
518 }
519
520 protected:
AppendCompletedArgumentCmdlineParser::Builder521 void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
522 auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
523 completed_arguments_.push_back(std::move(smart_ptr));
524 }
525
526 private:
527 // No copying now!
528 Builder(const Builder& other) = delete;
529
530 template <typename TArg>
531 friend struct ArgumentBuilder;
532 friend struct UntypedArgumentBuilder;
533 friend struct CmdlineParser;
534
535 bool built_ = false;
536 bool ignore_unrecognized_ = false;
537 std::vector<const char*> ignore_list_;
538 std::shared_ptr<SaveDestination> save_destination_;
539 std::optional<const char*> default_category_;
540 std::vector<const char*> category_order_;
541
542 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
543 };
544
545 void DumpHelp(VariableIndentationOutputStream& vios);
546
ParseCmdlineParser547 CmdlineResult Parse(const std::string& argv) {
548 std::vector<std::string> tokenized;
549 Split(argv, ' ', &tokenized);
550
551 return Parse(TokenRange(std::move(tokenized)));
552 }
553
554 // Parse the arguments; storing results into the arguments map. Returns success value.
ParseCmdlineParser555 CmdlineResult Parse(const char* argv) {
556 return Parse(std::string(argv));
557 }
558
559 // Parse the arguments; storing the results into the arguments map. Returns success value.
560 // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser561 CmdlineResult Parse(const std::vector<const char*>& argv) {
562 return Parse(TokenRange(argv.begin(), argv.end()));
563 }
564
565 // Parse the arguments; storing the results into the arguments map. Returns success value.
566 // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser567 CmdlineResult Parse(const std::vector<std::string>& argv) {
568 return Parse(TokenRange(argv.begin(), argv.end()));
569 }
570
571 // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
572 // Assumes that argv[0] is the program name, and ignores it.
ParseCmdlineParser573 CmdlineResult Parse(const char* argv[], int argc) {
574 return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name
575 }
576
577 // Look up the arguments that have been parsed; use the target keys to lookup individual args.
GetArgumentsMapCmdlineParser578 const TVariantMap& GetArgumentsMap() const {
579 return save_destination_->GetMap();
580 }
581
582 // Release the arguments map that has been parsed; useful for move semantics.
ReleaseArgumentsMapCmdlineParser583 TVariantMap&& ReleaseArgumentsMap() {
584 return save_destination_->ReleaseMap();
585 }
586
587 // How many arguments were defined?
CountDefinedArgumentsCmdlineParser588 size_t CountDefinedArguments() const {
589 return completed_arguments_.size();
590 }
591
592 // Ensure we have a default move constructor.
593 CmdlineParser(CmdlineParser&&) noexcept = default;
594 // Ensure we have a default move assignment operator.
595 CmdlineParser& operator=(CmdlineParser&&) noexcept = default;
596
597 private:
598 friend struct Builder;
599
600 // Construct a new parser from the builder. Move all the arguments.
CmdlineParserCmdlineParser601 CmdlineParser(bool ignore_unrecognized,
602 std::vector<const char*>&& ignore_list,
603 std::shared_ptr<SaveDestination> save_destination,
604 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments,
605 std::vector<const char*>&& category_order)
606 : ignore_unrecognized_(ignore_unrecognized),
607 ignore_list_(std::move(ignore_list)),
608 save_destination_(save_destination),
609 completed_arguments_(std::move(completed_arguments)),
610 category_order_(category_order) {
611 assert(save_destination != nullptr);
612 }
613
614 // Parse the arguments; storing results into the arguments map. Returns success value.
615 // The parsing will fail on the first non-success parse result and return that error.
616 //
617 // All previously-parsed arguments are cleared out.
618 // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
619 // A partial parse will result only in a partial save of the arguments.
ParseCmdlineParser620 CmdlineResult Parse(TokenRange&& arguments_list) {
621 save_destination_->Clear();
622
623 for (size_t i = 0; i < arguments_list.Size(); ) {
624 TokenRange possible_name = arguments_list.Slice(i);
625
626 size_t best_match_size = 0; // How many tokens were matched in the best case.
627 size_t best_match_arg_idx = 0;
628 bool matched = false; // At least one argument definition has been matched?
629
630 // Find the closest argument definition for the remaining token range.
631 size_t arg_idx = 0;
632 for (auto&& arg : completed_arguments_) {
633 size_t local_match = arg->MaybeMatches(possible_name);
634
635 if (local_match > best_match_size) {
636 best_match_size = local_match;
637 best_match_arg_idx = arg_idx;
638 matched = true;
639 }
640 arg_idx++;
641 }
642
643 // Saw some kind of unknown argument
644 if (matched == false) {
645 if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI.
646 // Consume 1 token and keep going, hopefully the next token is a good one.
647 ++i;
648 continue;
649 }
650 // Common case:
651 // Bail out on the first unknown argument with an error.
652 return CmdlineResult(CmdlineResult::kUnknown,
653 std::string("Unknown argument: ") + possible_name[0]);
654 }
655
656 // Look at the best-matched argument definition and try to parse against that.
657 auto&& arg = completed_arguments_[best_match_arg_idx];
658
659 assert(arg->MaybeMatches(possible_name) == best_match_size);
660
661 // Try to parse the argument now, if we have enough tokens.
662 std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
663 size_t min_tokens;
664 size_t max_tokens;
665
666 std::tie(min_tokens, max_tokens) = num_tokens;
667
668 if ((i + min_tokens) > arguments_list.Size()) {
669 // expected longer command line but it was too short
670 // e.g. if the argv was only "-Xms" without specifying a memory option
671 CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
672 " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
673 return CmdlineResult(CmdlineResult::kFailure,
674 std::string("Argument ") +
675 possible_name[0] + ": incomplete command line arguments, expected "
676 + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
677 " more tokens");
678 }
679
680 if (best_match_size > max_tokens || best_match_size < min_tokens) {
681 // Even our best match was out of range, so parsing would fail instantly.
682 return CmdlineResult(CmdlineResult::kFailure,
683 std::string("Argument ") + possible_name[0] + ": too few tokens "
684 "matched " + std::to_string(best_match_size)
685 + " but wanted " + std::to_string(num_tokens.first));
686 }
687
688 // We have enough tokens to begin exact parsing.
689 TokenRange exact_range = possible_name.Slice(0, max_tokens);
690
691 size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error
692 CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
693
694 if (parse_attempt.IsError()) {
695 // We may also want to continue parsing the other tokens to gather more errors.
696 return parse_attempt;
697 } // else the value has been successfully stored into the map
698
699 assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse
700 i += consumed_tokens;
701
702 // TODO: also handle ignoring arguments for backwards compatibility
703 } // for
704
705 return CmdlineResult(CmdlineResult::kSuccess);
706 }
707
708 bool ignore_unrecognized_ = false;
709 std::vector<const char*> ignore_list_;
710 std::shared_ptr<SaveDestination> save_destination_;
711 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
712 std::vector<const char*> category_order_;
713 };
714
715 // This has to be defined after everything else, since we want the builders to call this.
716 template <typename TVariantMap,
717 template <typename TKeyValue> class TVariantMapKey>
718 template <typename TArg>
719 typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
CreateArgumentBuilder(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & parent)720 CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
721 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
722 return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
723 parent, parent.save_destination_);
724 }
725
726 // This has to be defined after everything else, since we want the builders to call this.
727 template <typename TVariantMap,
728 template <typename TKeyValue> class TVariantMapKey>
AppendCompletedArgument(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & builder,detail::CmdlineParseArgumentAny * arg)729 void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
730 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
731 detail::CmdlineParseArgumentAny* arg) {
732 builder.AppendCompletedArgument(arg);
733 }
734
735 template <typename TVariantMap,
736 template <typename TKeyValue> class TVariantMapKey>
DumpHelp(VariableIndentationOutputStream & vios)737 void CmdlineParser<TVariantMap, TVariantMapKey>::DumpHelp(VariableIndentationOutputStream& vios) {
738 std::vector<detail::CmdlineParseArgumentAny*> uncat;
739 std::unordered_map<std::string, std::vector<detail::CmdlineParseArgumentAny*>> args;
740 for (const std::unique_ptr<detail::CmdlineParseArgumentAny>& it : completed_arguments_) {
741 auto cat = it->GetCategory();
742 if (cat.has_value()) {
743 if (args.find(*cat) == args.end()) {
744 args[*cat] = {};
745 }
746 args.at(*cat).push_back(it.get());
747 } else {
748 uncat.push_back(it.get());
749 }
750 }
751 args.erase("Ignored");
752 for (auto arg : uncat) {
753 arg->DumpHelp(vios);
754 vios.Stream();
755 }
756 for (auto it : category_order_) {
757 auto cur = args.find(it);
758 if (cur != args.end() && !cur->second.empty()) {
759 vios.Stream() << "The following " << it << " arguments are supported:" << std::endl;
760 ScopedIndentation si(&vios);
761 for (detail::CmdlineParseArgumentAny* arg : cur->second) {
762 arg->DumpHelp(vios);
763 vios.Stream();
764 }
765 args.erase(cur->first);
766 }
767 }
768 for (auto [cat, lst] : args) {
769 vios.Stream() << "The following " << cat << " arguments are supported:" << std::endl;
770 ScopedIndentation si(&vios);
771 for (auto& arg : completed_arguments_) {
772 arg->DumpHelp(vios);
773 vios.Stream();
774 }
775 }
776 if (!ignore_list_.empty()) {
777 vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl;
778 ScopedIndentation si(&vios);
779 for (auto ign : ignore_list_) {
780 vios.Stream() << ign << std::endl;
781 }
782 }
783 }
784
785 } // namespace art
786
787 #endif // ART_CMDLINE_CMDLINE_PARSER_H_
788