1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker #ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_ 18*795d594fSAndroid Build Coastguard Worker #define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_ 19*795d594fSAndroid Build Coastguard Worker 20*795d594fSAndroid Build Coastguard Worker #include <assert.h> 21*795d594fSAndroid Build Coastguard Worker #include <algorithm> 22*795d594fSAndroid Build Coastguard Worker #include <functional> 23*795d594fSAndroid Build Coastguard Worker #include <memory> 24*795d594fSAndroid Build Coastguard Worker #include <numeric> 25*795d594fSAndroid Build Coastguard Worker #include <string_view> 26*795d594fSAndroid Build Coastguard Worker #include <type_traits> 27*795d594fSAndroid Build Coastguard Worker #include <vector> 28*795d594fSAndroid Build Coastguard Worker 29*795d594fSAndroid Build Coastguard Worker #include "android-base/strings.h" 30*795d594fSAndroid Build Coastguard Worker 31*795d594fSAndroid Build Coastguard Worker #include "base/indenter.h" 32*795d594fSAndroid Build Coastguard Worker #include "cmdline_parse_result.h" 33*795d594fSAndroid Build Coastguard Worker #include "cmdline_types.h" 34*795d594fSAndroid Build Coastguard Worker #include "token_range.h" 35*795d594fSAndroid Build Coastguard Worker #include "unit.h" 36*795d594fSAndroid Build Coastguard Worker 37*795d594fSAndroid Build Coastguard Worker namespace art { 38*795d594fSAndroid Build Coastguard Worker // Implementation details for the parser. Do not look inside if you hate templates. 39*795d594fSAndroid Build Coastguard Worker namespace detail { 40*795d594fSAndroid Build Coastguard Worker 41*795d594fSAndroid Build Coastguard Worker // A non-templated base class for argument parsers. Used by the general parser 42*795d594fSAndroid Build Coastguard Worker // to parse arguments, without needing to know the argument type at compile time. 43*795d594fSAndroid Build Coastguard Worker // 44*795d594fSAndroid Build Coastguard Worker // This is an application of the type erasure idiom. 45*795d594fSAndroid Build Coastguard Worker struct CmdlineParseArgumentAny { ~CmdlineParseArgumentAnyCmdlineParseArgumentAny46*795d594fSAndroid Build Coastguard Worker virtual ~CmdlineParseArgumentAny() {} 47*795d594fSAndroid Build Coastguard Worker 48*795d594fSAndroid Build Coastguard Worker // Attempt to parse this argument starting at arguments[position]. 49*795d594fSAndroid Build Coastguard Worker // If the parsing succeeds, the parsed value will be saved as a side-effect. 50*795d594fSAndroid Build Coastguard Worker // 51*795d594fSAndroid Build Coastguard Worker // In most situations, the parsing will not match by returning kUnknown. In this case, 52*795d594fSAndroid Build Coastguard Worker // no tokens were consumed and the position variable will not be updated. 53*795d594fSAndroid Build Coastguard Worker // 54*795d594fSAndroid Build Coastguard Worker // At other times, parsing may fail due to validation but the initial token was still matched 55*795d594fSAndroid Build Coastguard Worker // (for example an out of range value, or passing in a string where an int was expected). 56*795d594fSAndroid Build Coastguard Worker // In this case the tokens are still consumed, and the position variable will get incremented 57*795d594fSAndroid Build Coastguard Worker // by all the consumed tokens. 58*795d594fSAndroid Build Coastguard Worker // 59*795d594fSAndroid Build Coastguard Worker // The # of tokens consumed by the parse attempt will be set as an out-parameter into 60*795d594fSAndroid Build Coastguard Worker // consumed_tokens. The parser should skip this many tokens before parsing the next 61*795d594fSAndroid Build Coastguard Worker // argument. 62*795d594fSAndroid Build Coastguard Worker virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0; 63*795d594fSAndroid Build Coastguard Worker // How many tokens should be taken off argv for parsing this argument. 64*795d594fSAndroid Build Coastguard Worker // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space). 65*795d594fSAndroid Build Coastguard Worker // 66*795d594fSAndroid Build Coastguard Worker // A [min,max] range is returned to represent argument definitions with multiple 67*795d594fSAndroid Build Coastguard Worker // value tokens. (e.g. {"-h", "-h " } would return [1,2]). 68*795d594fSAndroid Build Coastguard Worker virtual std::pair<size_t, size_t> GetNumTokens() const = 0; 69*795d594fSAndroid Build Coastguard Worker // Get the run-time typename of the argument type. 70*795d594fSAndroid Build Coastguard Worker virtual const char* GetTypeName() const = 0; 71*795d594fSAndroid Build Coastguard Worker // Try to do a close match, returning how many tokens were matched against this argument 72*795d594fSAndroid Build Coastguard Worker // definition. More tokens is better. 73*795d594fSAndroid Build Coastguard Worker // 74*795d594fSAndroid Build Coastguard Worker // Do a quick match token-by-token, and see if they match. 75*795d594fSAndroid Build Coastguard Worker // Any tokens with a wildcard in them are only matched up until the wildcard. 76*795d594fSAndroid Build Coastguard Worker // If this is true, then the wildcard matching later on can still fail, so this is not 77*795d594fSAndroid Build Coastguard Worker // a guarantee that the argument is correct, it's more of a strong hint that the 78*795d594fSAndroid Build Coastguard Worker // user-provided input *probably* was trying to match this argument. 79*795d594fSAndroid Build Coastguard Worker // 80*795d594fSAndroid Build Coastguard Worker // Returns how many tokens were either matched (or ignored because there was a 81*795d594fSAndroid Build Coastguard Worker // wildcard present). 0 means no match. If the Size() tokens are returned. 82*795d594fSAndroid Build Coastguard Worker virtual size_t MaybeMatches(const TokenRange& tokens) = 0; 83*795d594fSAndroid Build Coastguard Worker 84*795d594fSAndroid Build Coastguard Worker virtual void DumpHelp(VariableIndentationOutputStream& os) = 0; 85*795d594fSAndroid Build Coastguard Worker 86*795d594fSAndroid Build Coastguard Worker virtual const std::optional<const char*>& GetCategory() = 0; 87*795d594fSAndroid Build Coastguard Worker }; 88*795d594fSAndroid Build Coastguard Worker 89*795d594fSAndroid Build Coastguard Worker template <typename T> 90*795d594fSAndroid Build Coastguard Worker using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>; 91*795d594fSAndroid Build Coastguard Worker 92*795d594fSAndroid Build Coastguard Worker template <typename T> 93*795d594fSAndroid Build Coastguard Worker using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>; 94*795d594fSAndroid Build Coastguard Worker 95*795d594fSAndroid Build Coastguard Worker // Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder. 96*795d594fSAndroid Build Coastguard Worker template <typename TArg> 97*795d594fSAndroid Build Coastguard Worker struct CmdlineParserArgumentInfo { 98*795d594fSAndroid Build Coastguard Worker // This version will only be used if TArg is arithmetic and thus has the <= operators. 99*795d594fSAndroid Build Coastguard Worker template <typename T = TArg> // Necessary to get SFINAE to kick in. 100*795d594fSAndroid Build Coastguard Worker bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = nullptr) { 101*795d594fSAndroid Build Coastguard Worker if (has_range_) { 102*795d594fSAndroid Build Coastguard Worker return min_ <= value && value <= max_; 103*795d594fSAndroid Build Coastguard Worker } 104*795d594fSAndroid Build Coastguard Worker return true; 105*795d594fSAndroid Build Coastguard Worker } 106*795d594fSAndroid Build Coastguard Worker 107*795d594fSAndroid Build Coastguard Worker // This version will be used at other times when TArg is not arithmetic. 108*795d594fSAndroid Build Coastguard Worker template <typename T = TArg> 109*795d594fSAndroid Build Coastguard Worker bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = nullptr) { 110*795d594fSAndroid Build Coastguard Worker assert(!has_range_); 111*795d594fSAndroid Build Coastguard Worker return true; 112*795d594fSAndroid Build Coastguard Worker } 113*795d594fSAndroid Build Coastguard Worker 114*795d594fSAndroid Build Coastguard Worker // Do a quick match token-by-token, and see if they match. 115*795d594fSAndroid Build Coastguard Worker // Any tokens with a wildcard in them only match the prefix up until the wildcard. 116*795d594fSAndroid Build Coastguard Worker // 117*795d594fSAndroid Build Coastguard Worker // If this is true, then the wildcard matching later on can still fail, so this is not 118*795d594fSAndroid Build Coastguard Worker // a guarantee that the argument is correct, it's more of a strong hint that the 119*795d594fSAndroid Build Coastguard Worker // user-provided input *probably* was trying to match this argument. MaybeMatchesCmdlineParserArgumentInfo120*795d594fSAndroid Build Coastguard Worker size_t MaybeMatches(const TokenRange& token_list) const { 121*795d594fSAndroid Build Coastguard Worker auto best_match = FindClosestMatch(token_list); 122*795d594fSAndroid Build Coastguard Worker 123*795d594fSAndroid Build Coastguard Worker return best_match.second; 124*795d594fSAndroid Build Coastguard Worker } 125*795d594fSAndroid Build Coastguard Worker 126*795d594fSAndroid Build Coastguard Worker // Attempt to find the closest match (see MaybeMatches). 127*795d594fSAndroid Build Coastguard Worker // 128*795d594fSAndroid Build Coastguard Worker // Returns the token range that was the closest match and the # of tokens that 129*795d594fSAndroid Build Coastguard Worker // this range was matched up until. FindClosestMatchCmdlineParserArgumentInfo130*795d594fSAndroid Build Coastguard Worker std::pair<const TokenRange*, size_t> FindClosestMatch(const TokenRange& token_list) const { 131*795d594fSAndroid Build Coastguard Worker const TokenRange* best_match_ptr = nullptr; 132*795d594fSAndroid Build Coastguard Worker 133*795d594fSAndroid Build Coastguard Worker size_t best_match = 0; 134*795d594fSAndroid Build Coastguard Worker for (auto&& token_range : tokenized_names_) { 135*795d594fSAndroid Build Coastguard Worker size_t this_match = token_range.MaybeMatches(token_list, std::string("_")); 136*795d594fSAndroid Build Coastguard Worker 137*795d594fSAndroid Build Coastguard Worker if (this_match > best_match) { 138*795d594fSAndroid Build Coastguard Worker best_match_ptr = &token_range; 139*795d594fSAndroid Build Coastguard Worker best_match = this_match; 140*795d594fSAndroid Build Coastguard Worker } 141*795d594fSAndroid Build Coastguard Worker } 142*795d594fSAndroid Build Coastguard Worker 143*795d594fSAndroid Build Coastguard Worker return std::make_pair(best_match_ptr, best_match); 144*795d594fSAndroid Build Coastguard Worker } 145*795d594fSAndroid Build Coastguard Worker 146*795d594fSAndroid Build Coastguard Worker template <typename T = TArg> // Necessary to get SFINAE to kick in. DumpHelpCmdlineParserArgumentInfo147*795d594fSAndroid Build Coastguard Worker void DumpHelp(VariableIndentationOutputStream& vios) { 148*795d594fSAndroid Build Coastguard Worker // Separate arguments 149*795d594fSAndroid Build Coastguard Worker vios.Stream() << std::endl; 150*795d594fSAndroid Build Coastguard Worker for (auto cname : names_) { 151*795d594fSAndroid Build Coastguard Worker std::string_view name = cname; 152*795d594fSAndroid Build Coastguard Worker if (using_blanks_) { 153*795d594fSAndroid Build Coastguard Worker name = name.substr(0, name.find('_')); 154*795d594fSAndroid Build Coastguard Worker } 155*795d594fSAndroid Build Coastguard Worker auto& os = vios.Stream(); 156*795d594fSAndroid Build Coastguard Worker auto print_once = [&]() { 157*795d594fSAndroid Build Coastguard Worker os << name; 158*795d594fSAndroid Build Coastguard Worker if (using_blanks_) { 159*795d594fSAndroid Build Coastguard Worker if (has_value_map_) { 160*795d594fSAndroid Build Coastguard Worker bool first = true; 161*795d594fSAndroid Build Coastguard Worker for (auto [val, unused] : value_map_) { 162*795d594fSAndroid Build Coastguard Worker os << (first ? "{" : "|") << val; 163*795d594fSAndroid Build Coastguard Worker first = false; 164*795d594fSAndroid Build Coastguard Worker } 165*795d594fSAndroid Build Coastguard Worker os << "}"; 166*795d594fSAndroid Build Coastguard Worker } else if (metavar_.has_value()) { 167*795d594fSAndroid Build Coastguard Worker os << *metavar_; 168*795d594fSAndroid Build Coastguard Worker } else { 169*795d594fSAndroid Build Coastguard Worker os << "{" << CmdlineType<T>::DescribeType() << "}"; 170*795d594fSAndroid Build Coastguard Worker } 171*795d594fSAndroid Build Coastguard Worker } 172*795d594fSAndroid Build Coastguard Worker }; 173*795d594fSAndroid Build Coastguard Worker print_once(); 174*795d594fSAndroid Build Coastguard Worker if (appending_values_) { 175*795d594fSAndroid Build Coastguard Worker os << " ["; 176*795d594fSAndroid Build Coastguard Worker print_once(); 177*795d594fSAndroid Build Coastguard Worker os << "...]"; 178*795d594fSAndroid Build Coastguard Worker } 179*795d594fSAndroid Build Coastguard Worker os << std::endl; 180*795d594fSAndroid Build Coastguard Worker } 181*795d594fSAndroid Build Coastguard Worker if (help_.has_value()) { 182*795d594fSAndroid Build Coastguard Worker ScopedIndentation si(&vios); 183*795d594fSAndroid Build Coastguard Worker vios.Stream() << *help_ << std::endl; 184*795d594fSAndroid Build Coastguard Worker } 185*795d594fSAndroid Build Coastguard Worker } 186*795d594fSAndroid Build Coastguard Worker 187*795d594fSAndroid Build Coastguard Worker 188*795d594fSAndroid Build Coastguard Worker // Mark the argument definition as completed, do not mutate the object anymore after this 189*795d594fSAndroid Build Coastguard Worker // call is done. 190*795d594fSAndroid Build Coastguard Worker // 191*795d594fSAndroid Build Coastguard Worker // Performs several checks of the validity and token calculations. CompleteArgumentCmdlineParserArgumentInfo192*795d594fSAndroid Build Coastguard Worker void CompleteArgument() { 193*795d594fSAndroid Build Coastguard Worker assert(names_.size() >= 1); 194*795d594fSAndroid Build Coastguard Worker assert(!is_completed_); 195*795d594fSAndroid Build Coastguard Worker 196*795d594fSAndroid Build Coastguard Worker is_completed_ = true; 197*795d594fSAndroid Build Coastguard Worker 198*795d594fSAndroid Build Coastguard Worker size_t blank_count = 0; 199*795d594fSAndroid Build Coastguard Worker size_t token_count = 0; 200*795d594fSAndroid Build Coastguard Worker 201*795d594fSAndroid Build Coastguard Worker size_t global_blank_count = 0; 202*795d594fSAndroid Build Coastguard Worker size_t global_token_count = 0; 203*795d594fSAndroid Build Coastguard Worker for (auto&& name : names_) { 204*795d594fSAndroid Build Coastguard Worker std::string s(name); 205*795d594fSAndroid Build Coastguard Worker 206*795d594fSAndroid Build Coastguard Worker size_t local_blank_count = std::count(s.begin(), s.end(), '_'); 207*795d594fSAndroid Build Coastguard Worker size_t local_token_count = std::count(s.begin(), s.end(), ' '); 208*795d594fSAndroid Build Coastguard Worker 209*795d594fSAndroid Build Coastguard Worker if (global_blank_count != 0) { 210*795d594fSAndroid Build Coastguard Worker assert(local_blank_count == global_blank_count 211*795d594fSAndroid Build Coastguard Worker && "Every argument descriptor string must have same amount of blanks (_)"); 212*795d594fSAndroid Build Coastguard Worker } 213*795d594fSAndroid Build Coastguard Worker 214*795d594fSAndroid Build Coastguard Worker if (local_blank_count != 0) { 215*795d594fSAndroid Build Coastguard Worker global_blank_count = local_blank_count; 216*795d594fSAndroid Build Coastguard Worker blank_count++; 217*795d594fSAndroid Build Coastguard Worker 218*795d594fSAndroid Build Coastguard Worker assert(local_blank_count == 1 && "More than one blank is not supported"); 219*795d594fSAndroid Build Coastguard Worker assert(s.back() == '_' && "The blank character must only be at the end of the string"); 220*795d594fSAndroid Build Coastguard Worker } 221*795d594fSAndroid Build Coastguard Worker 222*795d594fSAndroid Build Coastguard Worker if (global_token_count != 0) { 223*795d594fSAndroid Build Coastguard Worker assert(local_token_count == global_token_count 224*795d594fSAndroid Build Coastguard Worker && "Every argument descriptor string must have same amount of tokens (spaces)"); 225*795d594fSAndroid Build Coastguard Worker } 226*795d594fSAndroid Build Coastguard Worker 227*795d594fSAndroid Build Coastguard Worker if (local_token_count != 0) { 228*795d594fSAndroid Build Coastguard Worker global_token_count = local_token_count; 229*795d594fSAndroid Build Coastguard Worker token_count++; 230*795d594fSAndroid Build Coastguard Worker } 231*795d594fSAndroid Build Coastguard Worker 232*795d594fSAndroid Build Coastguard Worker // Tokenize every name, turning it from a string to a token list. 233*795d594fSAndroid Build Coastguard Worker tokenized_names_.clear(); 234*795d594fSAndroid Build Coastguard Worker for (auto&& name1 : names_) { 235*795d594fSAndroid Build Coastguard Worker // Split along ' ' only, removing any duplicated spaces. 236*795d594fSAndroid Build Coastguard Worker tokenized_names_.push_back( 237*795d594fSAndroid Build Coastguard Worker TokenRange::Split(name1, {' '}).RemoveToken(" ")); 238*795d594fSAndroid Build Coastguard Worker } 239*795d594fSAndroid Build Coastguard Worker 240*795d594fSAndroid Build Coastguard Worker // remove the _ character from each of the token ranges 241*795d594fSAndroid Build Coastguard Worker // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""] 242*795d594fSAndroid Build Coastguard Worker // and this is OK because we still need an empty token to simplify 243*795d594fSAndroid Build Coastguard Worker // range comparisons 244*795d594fSAndroid Build Coastguard Worker simple_names_.clear(); 245*795d594fSAndroid Build Coastguard Worker 246*795d594fSAndroid Build Coastguard Worker for (auto&& tokenized_name : tokenized_names_) { 247*795d594fSAndroid Build Coastguard Worker simple_names_.push_back(tokenized_name.RemoveCharacter('_')); 248*795d594fSAndroid Build Coastguard Worker } 249*795d594fSAndroid Build Coastguard Worker } 250*795d594fSAndroid Build Coastguard Worker 251*795d594fSAndroid Build Coastguard Worker if (token_count != 0) { 252*795d594fSAndroid Build Coastguard Worker assert(("Every argument descriptor string must have equal amount of tokens (spaces)" && 253*795d594fSAndroid Build Coastguard Worker token_count == names_.size())); 254*795d594fSAndroid Build Coastguard Worker } 255*795d594fSAndroid Build Coastguard Worker 256*795d594fSAndroid Build Coastguard Worker if (blank_count != 0) { 257*795d594fSAndroid Build Coastguard Worker assert(("Every argument descriptor string must have an equal amount of blanks (_)" && 258*795d594fSAndroid Build Coastguard Worker blank_count == names_.size())); 259*795d594fSAndroid Build Coastguard Worker } 260*795d594fSAndroid Build Coastguard Worker 261*795d594fSAndroid Build Coastguard Worker using_blanks_ = blank_count > 0; 262*795d594fSAndroid Build Coastguard Worker { 263*795d594fSAndroid Build Coastguard Worker size_t smallest_name_token_range_size = 264*795d594fSAndroid Build Coastguard Worker std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u), 265*795d594fSAndroid Build Coastguard Worker [](size_t min, const TokenRange& cur) { 266*795d594fSAndroid Build Coastguard Worker return std::min(min, cur.Size()); 267*795d594fSAndroid Build Coastguard Worker }); 268*795d594fSAndroid Build Coastguard Worker size_t largest_name_token_range_size = 269*795d594fSAndroid Build Coastguard Worker std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u, 270*795d594fSAndroid Build Coastguard Worker [](size_t max, const TokenRange& cur) { 271*795d594fSAndroid Build Coastguard Worker return std::max(max, cur.Size()); 272*795d594fSAndroid Build Coastguard Worker }); 273*795d594fSAndroid Build Coastguard Worker 274*795d594fSAndroid Build Coastguard Worker token_range_size_ = std::make_pair(smallest_name_token_range_size, 275*795d594fSAndroid Build Coastguard Worker largest_name_token_range_size); 276*795d594fSAndroid Build Coastguard Worker } 277*795d594fSAndroid Build Coastguard Worker 278*795d594fSAndroid Build Coastguard Worker if (has_value_list_) { 279*795d594fSAndroid Build Coastguard Worker assert(names_.size() == value_list_.size() 280*795d594fSAndroid Build Coastguard Worker && "Number of arg descriptors must match number of values"); 281*795d594fSAndroid Build Coastguard Worker assert(!has_value_map_); 282*795d594fSAndroid Build Coastguard Worker } 283*795d594fSAndroid Build Coastguard Worker if (has_value_map_) { 284*795d594fSAndroid Build Coastguard Worker if (!using_blanks_) { 285*795d594fSAndroid Build Coastguard Worker assert(names_.size() == value_map_.size() && 286*795d594fSAndroid Build Coastguard Worker "Since no blanks were specified, each arg is mapped directly into a mapped " 287*795d594fSAndroid Build Coastguard Worker "value without parsing; sizes must match"); 288*795d594fSAndroid Build Coastguard Worker } 289*795d594fSAndroid Build Coastguard Worker 290*795d594fSAndroid Build Coastguard Worker assert(!has_value_list_); 291*795d594fSAndroid Build Coastguard Worker } 292*795d594fSAndroid Build Coastguard Worker 293*795d594fSAndroid Build Coastguard Worker if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) { 294*795d594fSAndroid Build Coastguard Worker assert((has_value_map_ || has_value_list_) && 295*795d594fSAndroid Build Coastguard Worker "Arguments without a blank (_) must provide either a value map or a value list"); 296*795d594fSAndroid Build Coastguard Worker } 297*795d594fSAndroid Build Coastguard Worker 298*795d594fSAndroid Build Coastguard Worker TypedCheck(); 299*795d594fSAndroid Build Coastguard Worker } 300*795d594fSAndroid Build Coastguard Worker 301*795d594fSAndroid Build Coastguard Worker // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}. 302*795d594fSAndroid Build Coastguard Worker std::vector<const char*> names_; 303*795d594fSAndroid Build Coastguard Worker // Is there at least 1 wildcard '_' in the argument definition? 304*795d594fSAndroid Build Coastguard Worker bool using_blanks_ = false; 305*795d594fSAndroid Build Coastguard Worker // [min, max] token counts in each arg def 306*795d594fSAndroid Build Coastguard Worker std::pair<size_t, size_t> token_range_size_; 307*795d594fSAndroid Build Coastguard Worker 308*795d594fSAndroid Build Coastguard Worker // contains all the names in a tokenized form, i.e. as a space-delimited list 309*795d594fSAndroid Build Coastguard Worker std::vector<TokenRange> tokenized_names_; 310*795d594fSAndroid Build Coastguard Worker 311*795d594fSAndroid Build Coastguard Worker // contains the tokenized names, but with the _ character stripped 312*795d594fSAndroid Build Coastguard Worker std::vector<TokenRange> simple_names_; 313*795d594fSAndroid Build Coastguard Worker 314*795d594fSAndroid Build Coastguard Worker // For argument definitions created with '.AppendValues()' 315*795d594fSAndroid Build Coastguard Worker // Meaning that parsing should mutate the existing value in-place if possible. 316*795d594fSAndroid Build Coastguard Worker bool appending_values_ = false; 317*795d594fSAndroid Build Coastguard Worker 318*795d594fSAndroid Build Coastguard Worker // For argument definitions created with '.WithRange(min, max)' 319*795d594fSAndroid Build Coastguard Worker bool has_range_ = false; 320*795d594fSAndroid Build Coastguard Worker TArg min_; 321*795d594fSAndroid Build Coastguard Worker TArg max_; 322*795d594fSAndroid Build Coastguard Worker 323*795d594fSAndroid Build Coastguard Worker // For argument definitions created with '.WithValueMap' 324*795d594fSAndroid Build Coastguard Worker bool has_value_map_ = false; 325*795d594fSAndroid Build Coastguard Worker std::vector<std::pair<const char*, TArg>> value_map_; 326*795d594fSAndroid Build Coastguard Worker 327*795d594fSAndroid Build Coastguard Worker // For argument definitions created with '.WithValues' 328*795d594fSAndroid Build Coastguard Worker bool has_value_list_ = false; 329*795d594fSAndroid Build Coastguard Worker std::vector<TArg> value_list_; 330*795d594fSAndroid Build Coastguard Worker 331*795d594fSAndroid Build Coastguard Worker std::optional<const char*> help_; 332*795d594fSAndroid Build Coastguard Worker std::optional<const char*> category_; 333*795d594fSAndroid Build Coastguard Worker std::optional<const char*> metavar_; 334*795d594fSAndroid Build Coastguard Worker 335*795d594fSAndroid Build Coastguard Worker // Make sure there's a default constructor. 336*795d594fSAndroid Build Coastguard Worker CmdlineParserArgumentInfo() = default; 337*795d594fSAndroid Build Coastguard Worker 338*795d594fSAndroid Build Coastguard Worker // Ensure there's a default move constructor. 339*795d594fSAndroid Build Coastguard Worker CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) noexcept = default; 340*795d594fSAndroid Build Coastguard Worker 341*795d594fSAndroid Build Coastguard Worker private: 342*795d594fSAndroid Build Coastguard Worker // Perform type-specific checks at runtime. 343*795d594fSAndroid Build Coastguard Worker template <typename T = TArg> 344*795d594fSAndroid Build Coastguard Worker void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) { 345*795d594fSAndroid Build Coastguard Worker assert(!using_blanks_ && 346*795d594fSAndroid Build Coastguard Worker "Blanks are not supported in Unit arguments; since a Unit has no parse-able value"); 347*795d594fSAndroid Build Coastguard Worker } 348*795d594fSAndroid Build Coastguard Worker TypedCheckCmdlineParserArgumentInfo349*795d594fSAndroid Build Coastguard Worker void TypedCheck() {} 350*795d594fSAndroid Build Coastguard Worker 351*795d594fSAndroid Build Coastguard Worker bool is_completed_ = false; 352*795d594fSAndroid Build Coastguard Worker }; 353*795d594fSAndroid Build Coastguard Worker 354*795d594fSAndroid Build Coastguard Worker // A virtual-implementation of the necessary argument information in order to 355*795d594fSAndroid Build Coastguard Worker // be able to parse arguments. 356*795d594fSAndroid Build Coastguard Worker template <typename TArg> 357*795d594fSAndroid Build Coastguard Worker struct CmdlineParseArgument : CmdlineParseArgumentAny { CmdlineParseArgumentCmdlineParseArgument358*795d594fSAndroid Build Coastguard Worker CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info, 359*795d594fSAndroid Build Coastguard Worker std::function<void(TArg&)>&& save_argument, 360*795d594fSAndroid Build Coastguard Worker std::function<TArg&(void)>&& load_argument) 361*795d594fSAndroid Build Coastguard Worker : argument_info_(std::forward<decltype(argument_info)>(argument_info)), 362*795d594fSAndroid Build Coastguard Worker save_argument_(std::forward<decltype(save_argument)>(save_argument)), 363*795d594fSAndroid Build Coastguard Worker load_argument_(std::forward<decltype(load_argument)>(load_argument)) { 364*795d594fSAndroid Build Coastguard Worker } 365*795d594fSAndroid Build Coastguard Worker 366*795d594fSAndroid Build Coastguard Worker using UserTypeInfo = CmdlineType<TArg>; 367*795d594fSAndroid Build Coastguard Worker ParseArgumentCmdlineParseArgument368*795d594fSAndroid Build Coastguard Worker virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) { 369*795d594fSAndroid Build Coastguard Worker assert(arguments.Size() > 0); 370*795d594fSAndroid Build Coastguard Worker assert(consumed_tokens != nullptr); 371*795d594fSAndroid Build Coastguard Worker 372*795d594fSAndroid Build Coastguard Worker auto closest_match_res = argument_info_.FindClosestMatch(arguments); 373*795d594fSAndroid Build Coastguard Worker size_t best_match_size = closest_match_res.second; 374*795d594fSAndroid Build Coastguard Worker const TokenRange* best_match_arg_def = closest_match_res.first; 375*795d594fSAndroid Build Coastguard Worker 376*795d594fSAndroid Build Coastguard Worker if (best_match_size > arguments.Size()) { 377*795d594fSAndroid Build Coastguard Worker // The best match has more tokens than were provided. 378*795d594fSAndroid Build Coastguard Worker // Shouldn't happen in practice since the outer parser does this check. 379*795d594fSAndroid Build Coastguard Worker return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch"); 380*795d594fSAndroid Build Coastguard Worker } 381*795d594fSAndroid Build Coastguard Worker 382*795d594fSAndroid Build Coastguard Worker assert(best_match_arg_def != nullptr); 383*795d594fSAndroid Build Coastguard Worker *consumed_tokens = best_match_arg_def->Size(); 384*795d594fSAndroid Build Coastguard Worker 385*795d594fSAndroid Build Coastguard Worker if (!argument_info_.using_blanks_) { 386*795d594fSAndroid Build Coastguard Worker return ParseArgumentSingle(arguments.Join(' ')); 387*795d594fSAndroid Build Coastguard Worker } 388*795d594fSAndroid Build Coastguard Worker 389*795d594fSAndroid Build Coastguard Worker // Extract out the blank value from arguments 390*795d594fSAndroid Build Coastguard Worker // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar" 391*795d594fSAndroid Build Coastguard Worker std::string blank_value = ""; 392*795d594fSAndroid Build Coastguard Worker size_t idx = 0; 393*795d594fSAndroid Build Coastguard Worker for (auto&& def_token : *best_match_arg_def) { 394*795d594fSAndroid Build Coastguard Worker auto&& arg_token = arguments[idx]; 395*795d594fSAndroid Build Coastguard Worker 396*795d594fSAndroid Build Coastguard Worker // Does this definition-token have a wildcard in it? 397*795d594fSAndroid Build Coastguard Worker if (def_token.find('_') == std::string::npos) { 398*795d594fSAndroid Build Coastguard Worker // No, regular token. Match 1:1 against the argument token. 399*795d594fSAndroid Build Coastguard Worker bool token_match = def_token == arg_token; 400*795d594fSAndroid Build Coastguard Worker 401*795d594fSAndroid Build Coastguard Worker if (!token_match) { 402*795d594fSAndroid Build Coastguard Worker return CmdlineResult(CmdlineResult::kFailure, 403*795d594fSAndroid Build Coastguard Worker std::string("Failed to parse ") + best_match_arg_def->GetToken(0) 404*795d594fSAndroid Build Coastguard Worker + " at token " + std::to_string(idx)); 405*795d594fSAndroid Build Coastguard Worker } 406*795d594fSAndroid Build Coastguard Worker } else { 407*795d594fSAndroid Build Coastguard Worker // This is a wild-carded token. 408*795d594fSAndroid Build Coastguard Worker TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'}); 409*795d594fSAndroid Build Coastguard Worker 410*795d594fSAndroid Build Coastguard Worker // Extract the wildcard contents out of the user-provided arg_token. 411*795d594fSAndroid Build Coastguard Worker std::unique_ptr<TokenRange> arg_matches = 412*795d594fSAndroid Build Coastguard Worker def_split_wildcards.MatchSubstrings(arg_token, "_"); 413*795d594fSAndroid Build Coastguard Worker if (arg_matches == nullptr) { 414*795d594fSAndroid Build Coastguard Worker return CmdlineResult(CmdlineResult::kFailure, 415*795d594fSAndroid Build Coastguard Worker std::string("Failed to parse ") + best_match_arg_def->GetToken(0) 416*795d594fSAndroid Build Coastguard Worker + ", with a wildcard pattern " + def_token 417*795d594fSAndroid Build Coastguard Worker + " at token " + std::to_string(idx)); 418*795d594fSAndroid Build Coastguard Worker } 419*795d594fSAndroid Build Coastguard Worker 420*795d594fSAndroid Build Coastguard Worker // Get the corresponding wildcard tokens from arg_matches, 421*795d594fSAndroid Build Coastguard Worker // and concatenate it to blank_value. 422*795d594fSAndroid Build Coastguard Worker for (size_t sub_idx = 0; 423*795d594fSAndroid Build Coastguard Worker sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) { 424*795d594fSAndroid Build Coastguard Worker if (def_split_wildcards[sub_idx] == "_") { 425*795d594fSAndroid Build Coastguard Worker blank_value += arg_matches->GetToken(sub_idx); 426*795d594fSAndroid Build Coastguard Worker } 427*795d594fSAndroid Build Coastguard Worker } 428*795d594fSAndroid Build Coastguard Worker } 429*795d594fSAndroid Build Coastguard Worker 430*795d594fSAndroid Build Coastguard Worker ++idx; 431*795d594fSAndroid Build Coastguard Worker } 432*795d594fSAndroid Build Coastguard Worker 433*795d594fSAndroid Build Coastguard Worker return ParseArgumentSingle(blank_value); 434*795d594fSAndroid Build Coastguard Worker } 435*795d594fSAndroid Build Coastguard Worker DumpHelpCmdlineParseArgument436*795d594fSAndroid Build Coastguard Worker virtual void DumpHelp(VariableIndentationOutputStream& os) { 437*795d594fSAndroid Build Coastguard Worker argument_info_.DumpHelp(os); 438*795d594fSAndroid Build Coastguard Worker } 439*795d594fSAndroid Build Coastguard Worker GetCategoryCmdlineParseArgument440*795d594fSAndroid Build Coastguard Worker virtual const std::optional<const char*>& GetCategory() { 441*795d594fSAndroid Build Coastguard Worker return argument_info_.category_; 442*795d594fSAndroid Build Coastguard Worker } 443*795d594fSAndroid Build Coastguard Worker 444*795d594fSAndroid Build Coastguard Worker private: ParseArgumentSingleCmdlineParseArgument445*795d594fSAndroid Build Coastguard Worker virtual CmdlineResult ParseArgumentSingle(const std::string& argument) { 446*795d594fSAndroid Build Coastguard Worker // TODO: refactor to use LookupValue for the value lists/maps 447*795d594fSAndroid Build Coastguard Worker 448*795d594fSAndroid Build Coastguard Worker // Handle the 'WithValueMap(...)' argument definition 449*795d594fSAndroid Build Coastguard Worker if (argument_info_.has_value_map_) { 450*795d594fSAndroid Build Coastguard Worker for (auto&& value_pair : argument_info_.value_map_) { 451*795d594fSAndroid Build Coastguard Worker const char* name = value_pair.first; 452*795d594fSAndroid Build Coastguard Worker 453*795d594fSAndroid Build Coastguard Worker if (argument == name) { 454*795d594fSAndroid Build Coastguard Worker return SaveArgument(value_pair.second); 455*795d594fSAndroid Build Coastguard Worker } 456*795d594fSAndroid Build Coastguard Worker } 457*795d594fSAndroid Build Coastguard Worker 458*795d594fSAndroid Build Coastguard Worker // Error case: Fail, telling the user what the allowed values were. 459*795d594fSAndroid Build Coastguard Worker std::vector<std::string> allowed_values; 460*795d594fSAndroid Build Coastguard Worker for (auto&& value_pair : argument_info_.value_map_) { 461*795d594fSAndroid Build Coastguard Worker const char* name = value_pair.first; 462*795d594fSAndroid Build Coastguard Worker allowed_values.push_back(name); 463*795d594fSAndroid Build Coastguard Worker } 464*795d594fSAndroid Build Coastguard Worker 465*795d594fSAndroid Build Coastguard Worker std::string allowed_values_flat = android::base::Join(allowed_values, ','); 466*795d594fSAndroid Build Coastguard Worker return CmdlineResult(CmdlineResult::kFailure, 467*795d594fSAndroid Build Coastguard Worker "Argument value '" + argument + "' does not match any of known valid " 468*795d594fSAndroid Build Coastguard Worker "values: {" + allowed_values_flat + "}"); 469*795d594fSAndroid Build Coastguard Worker } 470*795d594fSAndroid Build Coastguard Worker 471*795d594fSAndroid Build Coastguard Worker // Handle the 'WithValues(...)' argument definition 472*795d594fSAndroid Build Coastguard Worker if (argument_info_.has_value_list_) { 473*795d594fSAndroid Build Coastguard Worker size_t arg_def_idx = 0; 474*795d594fSAndroid Build Coastguard Worker for (auto&& value : argument_info_.value_list_) { 475*795d594fSAndroid Build Coastguard Worker auto&& arg_def_token = argument_info_.names_[arg_def_idx]; 476*795d594fSAndroid Build Coastguard Worker 477*795d594fSAndroid Build Coastguard Worker if (arg_def_token == argument) { 478*795d594fSAndroid Build Coastguard Worker return SaveArgument(value); 479*795d594fSAndroid Build Coastguard Worker } 480*795d594fSAndroid Build Coastguard Worker ++arg_def_idx; 481*795d594fSAndroid Build Coastguard Worker } 482*795d594fSAndroid Build Coastguard Worker 483*795d594fSAndroid Build Coastguard Worker assert(arg_def_idx + 1 == argument_info_.value_list_.size() && 484*795d594fSAndroid Build Coastguard Worker "Number of named argument definitions must match number of values defined"); 485*795d594fSAndroid Build Coastguard Worker 486*795d594fSAndroid Build Coastguard Worker // Error case: Fail, telling the user what the allowed values were. 487*795d594fSAndroid Build Coastguard Worker std::vector<std::string> allowed_values; 488*795d594fSAndroid Build Coastguard Worker allowed_values.reserve(argument_info_.names_.size()); 489*795d594fSAndroid Build Coastguard Worker for (auto&& arg_name : argument_info_.names_) { 490*795d594fSAndroid Build Coastguard Worker allowed_values.push_back(arg_name); 491*795d594fSAndroid Build Coastguard Worker } 492*795d594fSAndroid Build Coastguard Worker 493*795d594fSAndroid Build Coastguard Worker std::string allowed_values_flat = android::base::Join(allowed_values, ','); 494*795d594fSAndroid Build Coastguard Worker return CmdlineResult(CmdlineResult::kFailure, 495*795d594fSAndroid Build Coastguard Worker "Argument value '" + argument + "' does not match any of known valid" 496*795d594fSAndroid Build Coastguard Worker "values: {" + allowed_values_flat + "}"); 497*795d594fSAndroid Build Coastguard Worker } 498*795d594fSAndroid Build Coastguard Worker 499*795d594fSAndroid Build Coastguard Worker // Handle the regular case where we parsed an unknown value from a blank. 500*795d594fSAndroid Build Coastguard Worker UserTypeInfo type_parser; 501*795d594fSAndroid Build Coastguard Worker 502*795d594fSAndroid Build Coastguard Worker if (argument_info_.appending_values_) { 503*795d594fSAndroid Build Coastguard Worker TArg& existing = load_argument_(); 504*795d594fSAndroid Build Coastguard Worker CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing); 505*795d594fSAndroid Build Coastguard Worker 506*795d594fSAndroid Build Coastguard Worker assert(!argument_info_.has_range_); 507*795d594fSAndroid Build Coastguard Worker 508*795d594fSAndroid Build Coastguard Worker return std::move(result); 509*795d594fSAndroid Build Coastguard Worker } 510*795d594fSAndroid Build Coastguard Worker 511*795d594fSAndroid Build Coastguard Worker CmdlineParseResult<TArg> result = type_parser.Parse(argument); 512*795d594fSAndroid Build Coastguard Worker 513*795d594fSAndroid Build Coastguard Worker if (result.IsSuccess()) { 514*795d594fSAndroid Build Coastguard Worker TArg& value = result.GetValue(); 515*795d594fSAndroid Build Coastguard Worker 516*795d594fSAndroid Build Coastguard Worker // Do a range check for 'WithRange(min,max)' argument definition. 517*795d594fSAndroid Build Coastguard Worker if (!argument_info_.CheckRange(value)) { 518*795d594fSAndroid Build Coastguard Worker return CmdlineParseResult<TArg>::OutOfRange( 519*795d594fSAndroid Build Coastguard Worker value, argument_info_.min_, argument_info_.max_); 520*795d594fSAndroid Build Coastguard Worker } 521*795d594fSAndroid Build Coastguard Worker 522*795d594fSAndroid Build Coastguard Worker return SaveArgument(value); 523*795d594fSAndroid Build Coastguard Worker } 524*795d594fSAndroid Build Coastguard Worker 525*795d594fSAndroid Build Coastguard Worker // Some kind of type-specific parse error. Pass the result as-is. 526*795d594fSAndroid Build Coastguard Worker CmdlineResult raw_result = std::move(result); 527*795d594fSAndroid Build Coastguard Worker return raw_result; 528*795d594fSAndroid Build Coastguard Worker } 529*795d594fSAndroid Build Coastguard Worker 530*795d594fSAndroid Build Coastguard Worker public: GetTypeNameCmdlineParseArgument531*795d594fSAndroid Build Coastguard Worker virtual const char* GetTypeName() const { 532*795d594fSAndroid Build Coastguard Worker // TODO: Obviate the need for each type specialization to hardcode the type name 533*795d594fSAndroid Build Coastguard Worker return UserTypeInfo::Name(); 534*795d594fSAndroid Build Coastguard Worker } 535*795d594fSAndroid Build Coastguard Worker 536*795d594fSAndroid Build Coastguard Worker // How many tokens should be taken off argv for parsing this argument. 537*795d594fSAndroid Build Coastguard Worker // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space). 538*795d594fSAndroid Build Coastguard Worker // 539*795d594fSAndroid Build Coastguard Worker // A [min,max] range is returned to represent argument definitions with multiple 540*795d594fSAndroid Build Coastguard Worker // value tokens. (e.g. {"-h", "-h " } would return [1,2]). GetNumTokensCmdlineParseArgument541*795d594fSAndroid Build Coastguard Worker virtual std::pair<size_t, size_t> GetNumTokens() const { 542*795d594fSAndroid Build Coastguard Worker return argument_info_.token_range_size_; 543*795d594fSAndroid Build Coastguard Worker } 544*795d594fSAndroid Build Coastguard Worker 545*795d594fSAndroid Build Coastguard Worker // See if this token range might begin the same as the argument definition. MaybeMatchesCmdlineParseArgument546*795d594fSAndroid Build Coastguard Worker virtual size_t MaybeMatches(const TokenRange& tokens) { 547*795d594fSAndroid Build Coastguard Worker return argument_info_.MaybeMatches(tokens); 548*795d594fSAndroid Build Coastguard Worker } 549*795d594fSAndroid Build Coastguard Worker 550*795d594fSAndroid Build Coastguard Worker private: SaveArgumentCmdlineParseArgument551*795d594fSAndroid Build Coastguard Worker CmdlineResult SaveArgument(const TArg& value) { 552*795d594fSAndroid Build Coastguard Worker assert(!argument_info_.appending_values_ 553*795d594fSAndroid Build Coastguard Worker && "If the values are being appended, then the updated parse value is " 554*795d594fSAndroid Build Coastguard Worker "updated by-ref as a side effect and shouldn't be stored directly"); 555*795d594fSAndroid Build Coastguard Worker TArg val = value; 556*795d594fSAndroid Build Coastguard Worker save_argument_(val); 557*795d594fSAndroid Build Coastguard Worker return CmdlineResult(CmdlineResult::kSuccess); 558*795d594fSAndroid Build Coastguard Worker } 559*795d594fSAndroid Build Coastguard Worker 560*795d594fSAndroid Build Coastguard Worker CmdlineParserArgumentInfo<TArg> argument_info_; 561*795d594fSAndroid Build Coastguard Worker std::function<void(TArg&)> save_argument_; 562*795d594fSAndroid Build Coastguard Worker std::function<TArg&(void)> load_argument_; 563*795d594fSAndroid Build Coastguard Worker }; 564*795d594fSAndroid Build Coastguard Worker } // namespace detail // NOLINT [readability/namespace] [5] 565*795d594fSAndroid Build Coastguard Worker } // namespace art 566*795d594fSAndroid Build Coastguard Worker 567*795d594fSAndroid Build Coastguard Worker #endif // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_ 568