| /* |
| * 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 "cmdline/detail/cmdline_parser_detail.h" |
| #include "cmdline/detail/cmdline_parse_argument_detail.h" |
| #include "cmdline/detail/cmdline_debug_detail.h" |
| |
| #include "cmdline_type_parser.h" |
| #include "token_range.h" |
| #include "cmdline_types.h" |
| #include "cmdline_result.h" |
| #include "cmdline_parse_result.h" |
| |
| #include "runtime/base/variant_map.h" |
| |
| #include <vector> |
| #include <memory> |
| |
| 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"); |
| return *reinterpret_cast<TArg*>(0); // 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"); |
| return *reinterpret_cast<TArg*>(0); // 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{}); // NOLINT [whitespace/braces] [5] |
| 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_ |