1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SK_COMMAND_LINE_FLAGS_H 9 #define SK_COMMAND_LINE_FLAGS_H 10 11 #include "include/core/SkString.h" 12 #include "include/private/base/SkTArray.h" 13 #include "include/private/base/SkTDArray.h" 14 #include "src/core/SkTHash.h" 15 16 /** 17 * Including this file (and compiling CommandLineFlags.cpp) provides command line 18 * parsing. In order to use it, use the following macros in global 19 * namespace: 20 * 21 * DEFINE_bool(name, defaultValue, helpString); 22 * DEFINE_string(name, defaultValue, helpString); 23 * DEFINE_int(name, defaultValue, helpString); 24 * DEFINE_double(name, defaultValue, helpString); 25 * 26 * Then, in main, call CommandLineFlags::SetUsage() to describe usage and call 27 * CommandLineFlags::Parse() to parse the flags. Henceforth, each flag can 28 * be referenced using 29 * 30 * FLAGS_name 31 * 32 * For example, the line 33 * 34 * DEFINE_bool(boolean, false, "The variable boolean does such and such"); 35 * 36 * will create the following variable: 37 * 38 * bool FLAGS_boolean; 39 * 40 * which will initially be set to false, and can be set to true by using the 41 * flag "--boolean" on the commandline. "--noboolean" will set FLAGS_boolean 42 * to false. FLAGS_boolean can also be set using "--boolean=true" or 43 * "--boolean true" (where "true" can be replaced by "false", "TRUE", "FALSE", 44 * "1" or "0"). 45 * 46 * The helpString will be printed if the help flag (-h or -help) is used. 47 * 48 * Similarly, the line 49 * 50 * DEFINE_int(integer, .., ..); 51 * 52 * will create 53 * 54 * int FLAGS_integer; 55 * 56 * and 57 * 58 * DEFINE_double(real, .., ..); 59 * 60 * will create 61 * 62 * double FLAGS_real; 63 * 64 * These flags can be set by specifying, for example, "--integer 7" and 65 * "--real 3.14" on the command line. Unsigned integers are parsed from the 66 * command line using strtoul() so will detect the base (0 for octal, and 67 * 0x or 0X for hex, otherwise assumes decimal). 68 * 69 * Unlike the others, the line 70 * 71 * DEFINE_string(args, .., ..); 72 * 73 * creates an array: 74 * 75 * CommandLineFlags::StringArray FLAGS_args; 76 * 77 * If the default value is the empty string, FLAGS_args will default to a size 78 * of zero. Otherwise it will default to a size of 1 with the default string 79 * as its value. All strings that follow the flag on the command line (until 80 * a string that begins with '-') will be entries in the array. 81 * 82 * DEFINE_extended_string(args, .., .., extendedHelpString); 83 * 84 * creates a similar string array flag as DEFINE_string. The flag will have extended help text 85 * (extendedHelpString) that can the user can see with '--help <args>' flag. 86 * 87 * Any flag can be referenced from another file after using the following: 88 * 89 * DECLARE_x(name); 90 * 91 * (where 'x' is the type specified in the DEFINE). 92 * 93 * Inspired by gflags (https://code.google.com/p/gflags/). Is not quite as 94 * robust as gflags, but suits our purposes. For example, allows creating 95 * a flag -h or -help which will never be used, since CommandLineFlags handles it. 96 * CommandLineFlags will also allow creating --flag and --noflag. Uses the same input 97 * format as gflags and creates similarly named variables (i.e. FLAGS_name). 98 * Strings are handled differently (resulting variable will be an array of 99 * strings) so that a flag can be followed by multiple parameters. 100 */ 101 102 class SkFlagInfo; 103 104 class CommandLineFlags { 105 public: 106 /** 107 * Call to set the help message to be displayed. Should be called before 108 * Parse. 109 */ 110 static void SetUsage(const char* usage); 111 112 /** 113 * Call this to display the help message. Should be called after SetUsage. 114 */ 115 static void PrintUsage(); 116 117 /** 118 * Call at the beginning of main to parse flags created by DEFINE_x, above. 119 * Must only be called once. 120 */ 121 static void Parse(int argc, const char* const* argv); 122 123 /** 124 * Custom class for holding the arguments for a string flag. 125 * Publicly only has accessors so the strings cannot be modified. 126 */ 127 class StringArray { 128 public: StringArray()129 StringArray() {} StringArray(const skia_private::TArray<SkString> & strings)130 explicit StringArray(const skia_private::TArray<SkString>& strings) : fStrings(strings) {} 131 const char* operator[](int i) const { 132 SkASSERT(i >= 0 && i < fStrings.size()); 133 return fStrings[i].c_str(); 134 } 135 size()136 int size() const { return fStrings.size(); } 137 isEmpty()138 bool isEmpty() const { return this->size() == 0; } 139 140 /** 141 * Returns true iff string is equal to one of the strings in this array. 142 */ contains(const char * string)143 bool contains(const char* string) const { 144 for (int i = 0; i < fStrings.size(); i++) { 145 if (fStrings[i].equals(string)) { 146 return true; 147 } 148 } 149 return false; 150 } 151 set(int i,const char * str)152 void set(int i, const char* str) { 153 if (i >= fStrings.size()) { 154 this->append(str); 155 return; 156 } 157 fStrings[i].set(str); 158 } 159 begin()160 const SkString* begin() const { return fStrings.begin(); } end()161 const SkString* end() const { return fStrings.end(); } 162 163 /** 164 * Parses and validates a string flag that requires exactly one value out of a set of 165 * possible values. Returns a non-empty message in the case of errors. 166 */ 167 template <class E> parseAndValidate(const char * name,const skia_private::THashMap<SkString,E> & possibleValues,E * out)168 SkString parseAndValidate(const char* name, 169 const skia_private::THashMap<SkString, E>& possibleValues, 170 E* out) const { 171 if (size() == 0) { 172 return SkStringPrintf("Flag %s is required.", name); 173 } 174 if (size() != 1) { 175 return SkStringPrintf("Flag %s takes 1 value, got %d.", name, size()); 176 } 177 E* found = possibleValues.find(SkString(operator[](0))); 178 if (found != nullptr) { 179 *out = *found; 180 return SkString(); 181 } 182 return SkStringPrintf("Unknown value for flag %s: %s.", name, operator[](0)); 183 } 184 185 private: reset()186 void reset() { fStrings.clear(); } 187 append(const char * string)188 void append(const char* string) { fStrings.push_back().set(string); } 189 append(const char * string,size_t length)190 void append(const char* string, size_t length) { fStrings.push_back().set(string, length); } 191 192 skia_private::TArray<SkString> fStrings; 193 194 friend class SkFlagInfo; 195 }; 196 197 /* Takes a list of the form [~][^]match[$] 198 ~ causes a matching test to always be skipped 199 ^ requires the start of the test to match 200 $ requires the end of the test to match 201 ^ and $ requires an exact match 202 If a test does not match any list entry, it is skipped unless some list entry starts with ~ 203 */ 204 static bool ShouldSkip(const SkTDArray<const char*>& strings, const char* name); 205 static bool ShouldSkip(const StringArray& strings, const char* name); 206 207 private: 208 static SkFlagInfo* gHead; 209 static SkString gUsage; 210 211 // For access to gHead. 212 friend class SkFlagInfo; 213 }; 214 215 #define TO_STRING2(s) #s 216 #define TO_STRING(s) TO_STRING2(s) 217 218 #define DEFINE_bool(name, defaultValue, helpString) \ 219 bool FLAGS_##name; \ 220 [[maybe_unused]] static bool unused_##name = SkFlagInfo::CreateBoolFlag( \ 221 TO_STRING(name), nullptr, &FLAGS_##name, defaultValue, helpString) 222 223 // bool 2 allows specifying a short name. No check is done to ensure that shortName 224 // is actually shorter than name. 225 #define DEFINE_bool2(name, shortName, defaultValue, helpString) \ 226 bool FLAGS_##name; \ 227 [[maybe_unused]] static bool unused_##name = SkFlagInfo::CreateBoolFlag( \ 228 TO_STRING(name), TO_STRING(shortName), &FLAGS_##name, defaultValue, helpString) 229 230 #define DECLARE_bool(name) extern bool FLAGS_##name; 231 232 #define DEFINE_string(name, defaultValue, helpString) \ 233 CommandLineFlags::StringArray FLAGS_##name; \ 234 [[maybe_unused]] static bool unused_##name = SkFlagInfo::CreateStringFlag( \ 235 TO_STRING(name), nullptr, &FLAGS_##name, defaultValue, helpString, nullptr) 236 #define DEFINE_extended_string(name, defaultValue, helpString, extendedHelpString) \ 237 CommandLineFlags::StringArray FLAGS_##name; \ 238 [[maybe_unused]] static bool unused_##name = SkFlagInfo::CreateStringFlag( \ 239 TO_STRING(name), nullptr, &FLAGS_##name, defaultValue, helpString, extendedHelpString) 240 241 // string2 allows specifying a short name. There is an assert that shortName 242 // is only 1 character. 243 #define DEFINE_string2(name, shortName, defaultValue, helpString) \ 244 CommandLineFlags::StringArray FLAGS_##name; \ 245 [[maybe_unused]] static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 246 TO_STRING(shortName),\ 247 &FLAGS_##name, \ 248 defaultValue, \ 249 helpString, \ 250 nullptr) 251 252 #define DECLARE_string(name) extern CommandLineFlags::StringArray FLAGS_##name; 253 254 #define DEFINE_int(name, defaultValue, helpString) \ 255 int FLAGS_##name; \ 256 [[maybe_unused]] static bool unused_##name = \ 257 SkFlagInfo::CreateIntFlag(TO_STRING(name), &FLAGS_##name, defaultValue, helpString) 258 259 #define DEFINE_int_2(name, shortName, defaultValue, helpString) \ 260 int FLAGS_##name; \ 261 [[maybe_unused]] static bool unused_##name = SkFlagInfo::CreateIntFlag( \ 262 TO_STRING(name), TO_STRING(shortName), &FLAGS_##name, defaultValue, helpString) 263 264 #define DECLARE_int(name) extern int FLAGS_##name; 265 266 #define DEFINE_double(name, defaultValue, helpString) \ 267 double FLAGS_##name; \ 268 [[maybe_unused]] static bool unused_##name = \ 269 SkFlagInfo::CreateDoubleFlag(TO_STRING(name), &FLAGS_##name, defaultValue, helpString) 270 271 #define DECLARE_double(name) extern double FLAGS_##name; 272 273 class SkFlagInfo { 274 public: 275 enum FlagTypes { 276 kBool_FlagType, 277 kString_FlagType, 278 kInt_FlagType, 279 kDouble_FlagType, 280 }; 281 282 /** 283 * Each Create<Type>Flag function creates an SkFlagInfo of the specified type. The SkFlagInfo 284 * object is appended to a list, which is deleted when CommandLineFlags::Parse is called. 285 * Therefore, each call should be made before the call to ::Parse. They are not intended 286 * to be called directly. Instead, use the macros described above. 287 * @param name Long version (at least 2 characters) of the name of the flag. This name can 288 * be referenced on the command line as "--name" to set the value of this flag. 289 * @param shortName Short version (one character) of the name of the flag. This name can 290 * be referenced on the command line as "-shortName" to set the value of this flag. 291 * @param p<Type> Pointer to a global variable which holds the value set by CommandLineFlags. 292 * @param defaultValue The default value of this flag. The variable pointed to by p<Type> will 293 * be set to this value initially. This is also displayed as part of the help output. 294 * @param helpString Explanation of what this flag changes in the program. 295 */ CreateBoolFlag(const char * name,const char * shortName,bool * pBool,bool defaultValue,const char * helpString)296 static bool CreateBoolFlag(const char* name, 297 const char* shortName, 298 bool* pBool, 299 bool defaultValue, 300 const char* helpString) { 301 SkFlagInfo* info = new SkFlagInfo(name, shortName, kBool_FlagType, helpString, nullptr); 302 info->fBoolValue = pBool; 303 *info->fBoolValue = info->fDefaultBool = defaultValue; 304 return true; 305 } 306 307 /** 308 * See comments for CreateBoolFlag. 309 * @param pStrings Unlike the others, this is a pointer to an array of values. 310 * @param defaultValue Thise default will be parsed so that strings separated by spaces 311 * will be added to pStrings. 312 */ 313 static bool CreateStringFlag(const char* name, 314 const char* shortName, 315 CommandLineFlags::StringArray* pStrings, 316 const char* defaultValue, 317 const char* helpString, 318 const char* extendedHelpString); 319 320 /** 321 * See comments for CreateBoolFlag. 322 */ CreateIntFlag(const char * name,int * pInt,int defaultValue,const char * helpString)323 static bool CreateIntFlag(const char* name, 324 int* pInt, 325 int defaultValue, 326 const char* helpString) { 327 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kInt_FlagType, helpString, nullptr); 328 info->fIntValue = pInt; 329 *info->fIntValue = info->fDefaultInt = defaultValue; 330 return true; 331 } 332 CreateIntFlag(const char * name,const char * shortName,int * pInt,int defaultValue,const char * helpString)333 static bool CreateIntFlag(const char* name, 334 const char* shortName, 335 int* pInt, 336 int defaultValue, 337 const char* helpString) { 338 SkFlagInfo* info = new SkFlagInfo(name, shortName, kInt_FlagType, helpString, nullptr); 339 info->fIntValue = pInt; 340 *info->fIntValue = info->fDefaultInt = defaultValue; 341 return true; 342 } 343 344 /** 345 * See comments for CreateBoolFlag. 346 */ CreateDoubleFlag(const char * name,double * pDouble,double defaultValue,const char * helpString)347 static bool CreateDoubleFlag(const char* name, 348 double* pDouble, 349 double defaultValue, 350 const char* helpString) { 351 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kDouble_FlagType, helpString, nullptr); 352 info->fDoubleValue = pDouble; 353 *info->fDoubleValue = info->fDefaultDouble = defaultValue; 354 return true; 355 } 356 357 /** 358 * Returns true if the string matches this flag. 359 * For a boolean flag, also sets the value, since a boolean flag can be set in a number of ways 360 * without looking at the following string: 361 * --name 362 * --noname 363 * --name=true 364 * --name=false 365 * --name=1 366 * --name=0 367 * --name=TRUE 368 * --name=FALSE 369 */ 370 bool match(const char* string); 371 getFlagType()372 FlagTypes getFlagType() const { return fFlagType; } 373 resetStrings()374 void resetStrings() { 375 if (kString_FlagType == fFlagType) { 376 fStrings->reset(); 377 } else { 378 SkDEBUGFAIL("Can only call resetStrings on kString_FlagType"); 379 } 380 } 381 append(const char * string)382 void append(const char* string) { 383 if (kString_FlagType == fFlagType) { 384 fStrings->append(string); 385 } else { 386 SkDEBUGFAIL("Can only append to kString_FlagType"); 387 } 388 } 389 setInt(int value)390 void setInt(int value) { 391 if (kInt_FlagType == fFlagType) { 392 *fIntValue = value; 393 } else { 394 SkDEBUGFAIL("Can only call setInt on kInt_FlagType"); 395 } 396 } 397 setDouble(double value)398 void setDouble(double value) { 399 if (kDouble_FlagType == fFlagType) { 400 *fDoubleValue = value; 401 } else { 402 SkDEBUGFAIL("Can only call setDouble on kDouble_FlagType"); 403 } 404 } 405 setBool(bool value)406 void setBool(bool value) { 407 if (kBool_FlagType == fFlagType) { 408 *fBoolValue = value; 409 } else { 410 SkDEBUGFAIL("Can only call setBool on kBool_FlagType"); 411 } 412 } 413 next()414 SkFlagInfo* next() { return fNext; } 415 name()416 const SkString& name() const { return fName; } 417 shortName()418 const SkString& shortName() const { return fShortName; } 419 help()420 const SkString& help() const { return fHelpString; } extendedHelp()421 const SkString& extendedHelp() const { return fExtendedHelpString; } 422 defaultValue()423 SkString defaultValue() const { 424 SkString result; 425 switch (fFlagType) { 426 case SkFlagInfo::kBool_FlagType: 427 result.printf("%s", fDefaultBool ? "true" : "false"); 428 break; 429 case SkFlagInfo::kString_FlagType: return fDefaultString; 430 case SkFlagInfo::kInt_FlagType: result.printf("%i", fDefaultInt); break; 431 case SkFlagInfo::kDouble_FlagType: result.printf("%2.2f", fDefaultDouble); break; 432 default: SkDEBUGFAIL("Invalid flag type"); 433 } 434 return result; 435 } 436 typeAsString()437 SkString typeAsString() const { 438 switch (fFlagType) { 439 case SkFlagInfo::kBool_FlagType: return SkString("bool"); 440 case SkFlagInfo::kString_FlagType: return SkString("string"); 441 case SkFlagInfo::kInt_FlagType: return SkString("int"); 442 case SkFlagInfo::kDouble_FlagType: return SkString("double"); 443 default: SkDEBUGFAIL("Invalid flag type"); return SkString(); 444 } 445 } 446 447 private: SkFlagInfo(const char * name,const char * shortName,FlagTypes type,const char * helpString,const char * extendedHelpString)448 SkFlagInfo(const char* name, 449 const char* shortName, 450 FlagTypes type, 451 const char* helpString, 452 const char* extendedHelpString) 453 : fName(name) 454 , fShortName(shortName) 455 , fFlagType(type) 456 , fHelpString(helpString) 457 , fExtendedHelpString(extendedHelpString) 458 , fBoolValue(nullptr) 459 , fDefaultBool(false) 460 , fIntValue(nullptr) 461 , fDefaultInt(0) 462 , fDoubleValue(nullptr) 463 , fDefaultDouble(0) 464 , fStrings(nullptr) { 465 fNext = CommandLineFlags::gHead; 466 CommandLineFlags::gHead = this; 467 SkASSERT(name && strlen(name) > 1); 468 SkASSERT(nullptr == shortName || 1 == strlen(shortName)); 469 } 470 471 /** 472 * Set a StringArray to hold the values stored in defaultStrings. 473 * @param array The StringArray to modify. 474 * @param defaultStrings Space separated list of strings that should be inserted into array 475 * individually. 476 */ 477 static void SetDefaultStrings(CommandLineFlags::StringArray* array, const char* defaultStrings); 478 479 // Name of the flag, without initial dashes 480 SkString fName; 481 SkString fShortName; 482 FlagTypes fFlagType; 483 SkString fHelpString; 484 SkString fExtendedHelpString; 485 bool* fBoolValue; 486 bool fDefaultBool; 487 int* fIntValue; 488 int fDefaultInt; 489 double* fDoubleValue; 490 double fDefaultDouble; 491 CommandLineFlags::StringArray* fStrings; 492 // Both for the help string and in case fStrings is empty. 493 SkString fDefaultString; 494 495 // In order to keep a linked list. 496 SkFlagInfo* fNext; 497 }; 498 #endif // SK_COMMAND_LINE_FLAGS_H 499