blob: 22eb44c2118fc88c24131740baab07b3812abe41 [file] [log] [blame]
Igor Murashkinaaebaa02015-01-26 10:55:53 -08001/*
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
Alex Lighteb9da9e2020-07-13 16:10:03 -070022#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>
Igor Murashkinaaebaa02015-01-26 10:55:53 -080030
Alex Lighteb9da9e2020-07-13 16:10:03 -070031#include "base/indenter.h"
32#include "base/variant_map.h"
Igor Murashkinaaebaa02015-01-26 10:55:53 -080033#include "cmdline_parse_result.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070034#include "cmdline_result.h"
35#include "cmdline_type_parser.h"
36#include "cmdline_types.h"
Alex Lighteb9da9e2020-07-13 16:10:03 -070037#include "detail/cmdline_debug_detail.h"
38#include "detail/cmdline_parse_argument_detail.h"
39#include "detail/cmdline_parser_detail.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070040#include "token_range.h"
Igor Murashkinaaebaa02015-01-26 10:55:53 -080041
Igor Murashkinaaebaa02015-01-26 10:55:53 -080042namespace 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.
46template <typename TVariantMap,
47 template <typename TKeyValue> class TVariantMapKey>
48struct 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 {
69 SaveDestination() : variant_map_(new TVariantMap()) {}
70
71 // Save value to the variant map.
72 template <typename TArg>
73 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>
79 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.
93 TVariantMap&& ReleaseMap() {
94 return std::move(*variant_map_);
95 }
96
97 // Get a read-only reference to the variant map.
98 const TVariantMap& GetMap() {
99 return *variant_map_;
100 }
101
102 // Clear all potential save targets.
103 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.
127 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.
139 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 {}.
146 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.
155 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.
167 ArgumentBuilder<TArg>& AppendValues() {
168 argument_info_.appending_values_ = true;
169
170 return *this;
171 }
172
Alex Lighteb9da9e2020-07-13 16:10:03 -0700173 ArgumentBuilder<TArg>& WithMetavar(const char* sv) {
174 argument_info_.metavar_ = sv;
175 return *this;
176 }
177
178 ArgumentBuilder<TArg>& WithHelp(const char* sv) {
179 argument_info_.help_ = sv;
180 return *this;
181 }
182
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800183 // 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
188 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.
218 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");
Yi Kong7b814f32018-12-17 16:13:58 -0800224 __builtin_trap(); // Blow up.
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800225 };
226
227 save_value_specified_ = true;
228 load_value_specified_ = true;
229
230 CompleteArgument();
231 return parent_;
232 }
233
234 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
241 void SetNames(std::vector<const char*>&& names) {
242 argument_info_.names_ = names;
243 }
244
245 void SetNames(std::initializer_list<const char*> names) {
246 argument_info_.names_ = names;
247 }
248
Alex Lighteb9da9e2020-07-13 16:10:03 -0700249 void SetHelp(std::optional<const char*>&& val) {
250 argument_info_.help_ = val;
251 }
252
253 void SetCategory(std::optional<const char*>&& val) {
254 argument_info_.category_ = val;
255 }
256 void SetMetavar(std::optional<const char*>&& val) {
257 argument_info_.metavar_ = val;
258 }
259
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800260 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.
266 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
287 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");
Yi Kong7b814f32018-12-17 16:13:58 -0800299 __builtin_trap(); // Blow up.
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800300 };
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>
316 ArgumentBuilder<TArg> WithType() {
317 return CreateTypedBuilder<TArg>();
318 }
319
Alex Lighteb9da9e2020-07-13 16:10:03 -0700320 UntypedArgumentBuilder& WithHelp(const char* sv) {
321 SetHelp(sv);
322 return *this;
323 }
324
325 UntypedArgumentBuilder& WithCategory(const char* sv) {
326 SetCategory(sv);
327 return *this;
328 }
329
330 UntypedArgumentBuilder& WithMetavar(const char* sv) {
331 SetMetavar(sv);
332 return *this;
333 }
334
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800335 // When used with multiple aliases, map the position of the alias to the value position.
336 template <typename TArg>
337 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>
346 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.
352 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:
360 void SetNames(std::vector<const char*>&& names) {
361 names_ = std::move(names);
362 }
363
364 void SetNames(std::initializer_list<const char*> names) {
365 names_ = names;
366 }
367
Alex Lighteb9da9e2020-07-13 16:10:03 -0700368 void SetHelp(std::optional<const char*> sv) {
369 help_.swap(sv);
370 }
371
372 void SetMetavar(std::optional<const char*> sv) {
373 metavar_.swap(sv);
374 }
375
376 void SetCategory(std::optional<const char*> sv) {
377 category_.swap(sv);
378 }
379
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800380 private:
381 // No copying. Move instead.
382 UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
383
384 template <typename TArg>
385 ArgumentBuilder<TArg> CreateTypedBuilder() {
386 auto&& b = CreateArgumentBuilder<TArg>(parent_);
387 InitializeTypedBuilder(&b); // Type-specific initialization
388 b.SetNames(std::move(names_));
Alex Lighteb9da9e2020-07-13 16:10:03 -0700389 b.SetHelp(std::move(help_));
390 b.SetCategory(std::move(category_));
391 b.SetMetavar(std::move(metavar_));
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800392 return std::move(b);
393 }
394
395 template <typename TArg = Unit>
396 typename std::enable_if<std::is_same<TArg, Unit>::value>::type
397 InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
398 // Every Unit argument implicitly maps to a runtime value of Unit{}
Igor Murashkin5573c372017-11-16 13:34:30 -0800399 std::vector<Unit> values(names_.size(), Unit{});
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800400 arg_builder->SetValuesInternal(std::move(values));
401 }
402
403 // No extra work for all other types
404 void InitializeTypedBuilder(void*) {}
405
406 template <typename TArg>
407 friend struct ArgumentBuilder;
408 friend struct Builder;
409
410 explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
411 // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
412
413 CmdlineParser::Builder& parent_;
414 std::vector<const char*> names_;
Alex Lighteb9da9e2020-07-13 16:10:03 -0700415 std::optional<const char*> category_;
416 std::optional<const char*> help_;
417 std::optional<const char*> metavar_;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800418 };
419
420 // Build a new parser given a chain of calls to define arguments.
421 struct Builder {
422 Builder() : save_destination_(new SaveDestination()) {}
423
424 // Define a single argument. The default type is Unit.
425 UntypedArgumentBuilder Define(const char* name) {
426 return Define({name});
427 }
428
Alex Lighteb9da9e2020-07-13 16:10:03 -0700429 Builder& ClearCategory() {
430 default_category_.reset();
431 return *this;
432 }
433
434 Builder& SetCategory(const char* sv) {
435 default_category_ = sv;
436 return *this;
437 }
438
439 Builder& OrderCategories(std::vector<const char*> categories) {
440 category_order_.swap(categories);
441 return *this;
442 }
443
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800444 // Define a single argument with multiple aliases.
445 UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
446 auto&& b = UntypedArgumentBuilder(*this);
447 b.SetNames(names);
Alex Lighteb9da9e2020-07-13 16:10:03 -0700448 b.SetCategory(default_category_);
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800449 return std::move(b);
450 }
451
452 // Whether the parser should give up on unrecognized arguments. Not recommended.
453 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.
459 Builder& Ignore(std::initializer_list<const char*> ignore_list) {
Alex Lighteb9da9e2020-07-13 16:10:03 -0700460 auto current_cat = default_category_;
461 default_category_ = "Ignored";
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800462 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
Andreas Gampeca620d72016-11-08 08:09:33 -0800470 if (ign.find('_') != std::string::npos) { // Does the arg-def have a wildcard?
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800471 // 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;
Alex Lighteb9da9e2020-07-13 16:10:03 -0700483 default_category_ = current_cat;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800484 return *this;
485 }
486
Ian Pedowitz2d536432020-07-22 14:33:00 -0700487 // Finish building the parser; performs a check of the validity. Return value is moved, not
488 // copied. Do not call this more than once.
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800489 CmdlineParser Build() {
490 assert(!built_);
491 built_ = true;
492
493 auto&& p = CmdlineParser(ignore_unrecognized_,
494 std::move(ignore_list_),
495 save_destination_,
Alex Lighteb9da9e2020-07-13 16:10:03 -0700496 std::move(completed_arguments_),
497 std::move(category_order_));
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800498
499 return std::move(p);
500 }
501
502 protected:
503 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_;
Alex Lighteb9da9e2020-07-13 16:10:03 -0700521 std::optional<const char*> default_category_;
522 std::vector<const char*> category_order_;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800523
524 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
525 };
526
Alex Lighteb9da9e2020-07-13 16:10:03 -0700527 void DumpHelp(VariableIndentationOutputStream& vios);
528
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800529 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.
537 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).
543 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).
549 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.
555 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.
560 const TVariantMap& GetArgumentsMap() const {
561 return save_destination_->GetMap();
562 }
563
564 // Release the arguments map that has been parsed; useful for move semantics.
565 TVariantMap&& ReleaseArgumentsMap() {
566 return save_destination_->ReleaseMap();
567 }
568
569 // How many arguments were defined?
570 size_t CountDefinedArguments() const {
571 return completed_arguments_.size();
572 }
573
574 // Ensure we have a default move constructor.
Andreas Gampec801f0d2015-02-24 20:55:16 -0800575 CmdlineParser(CmdlineParser&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800576 // Ensure we have a default move assignment operator.
Andreas Gampec801f0d2015-02-24 20:55:16 -0800577 CmdlineParser& operator=(CmdlineParser&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800578
579 private:
580 friend struct Builder;
581
582 // Construct a new parser from the builder. Move all the arguments.
Roland Levillain3887c462015-08-12 18:15:42 +0100583 CmdlineParser(bool ignore_unrecognized,
584 std::vector<const char*>&& ignore_list,
585 std::shared_ptr<SaveDestination> save_destination,
Alex Lighteb9da9e2020-07-13 16:10:03 -0700586 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments,
587 std::vector<const char*>&& category_order)
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800588 : ignore_unrecognized_(ignore_unrecognized),
589 ignore_list_(std::move(ignore_list)),
590 save_destination_(save_destination),
Alex Lighteb9da9e2020-07-13 16:10:03 -0700591 completed_arguments_(std::move(completed_arguments)),
592 category_order_(category_order) {
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800593 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.
602 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_;
Alex Lighteb9da9e2020-07-13 16:10:03 -0700694 std::vector<const char*> category_order_;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800695};
696
697// This has to be defined after everything else, since we want the builders to call this.
698template <typename TVariantMap,
699 template <typename TKeyValue> class TVariantMapKey>
700template <typename TArg>
Yi Kong88307ed2017-04-25 22:33:06 -0700701typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800702CmdlineParser<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.
709template <typename TVariantMap,
710 template <typename TKeyValue> class TVariantMapKey>
711void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
712 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
713 detail::CmdlineParseArgumentAny* arg) {
714 builder.AppendCompletedArgument(arg);
715}
716
Alex Lighteb9da9e2020-07-13 16:10:03 -0700717template <typename TVariantMap,
718 template <typename TKeyValue> class TVariantMapKey>
719void 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 }
Alex Light0d47a822020-08-25 09:16:34 -0700758 if (!ignore_list_.empty()) {
759 vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl;
760 ScopedIndentation si(&vios);
761 for (auto ign : ignore_list_) {
762 vios.Stream() << ign << std::endl;
763 }
Alex Lighteb9da9e2020-07-13 16:10:03 -0700764 }
765}
766
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800767} // namespace art
768
769#endif // ART_CMDLINE_CMDLINE_PARSER_H_