// Copyright (c) 2023 Google LLC. // // 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 INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_ #define INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_ #include <stdint.h> #include <functional> #include <string> #include <variant> #include <vector> // This file provides some utils to define a command-line interface with // required and optional flags. // - Flag order is not checked. // - Currently supported flag types: BOOLEAN, STRING // - As with most nix tools, using '--' in the command-line means all following // tokens will be considered positional // arguments. // Example: binary -g -- -g --some-other-flag // - the first `-g` is a flag. // - the second `-g` is not a flag. // - `--some-other-flag` is not a flag. // - Both long-form and short-form flags are supported, but boolean flags don't // support split boolean literals (short and long form). // Example: // -g : allowed, sets g to true. // --my-flag : allowed, sets --my-flag to true. // --my-flag=true : allowed, sets --my-flag to true. // --my-flag true : NOT allowed. // -g true : NOT allowed. // --my-flag=TRUE : NOT allowed. // // - This implementation also supports string flags: // -o myfile.spv : allowed, sets -o to `myfile.spv`. // --output=myfile.spv : allowed, sets --output to `myfile.spv`. // --output myfile.spv : allowd, sets --output to `myfile.spv`. // // Note: then second token is NOT checked for hyphens. // --output -file.spv // flag name: `output` // flag value: `-file.spv` // // - This implementation generates flag at compile time. Meaning flag names // must be valid C++ identifiers. // However, flags are usually using hyphens for word separation. Hence // renaming is done behind the scenes. Example: // // Declaring a long-form flag. // FLAG_LONG_bool(my_flag, [...]) // // -> in the code: flags::my_flag.value() // -> command-line: --my-flag // // - The only additional lexing done is around '='. Otherwise token list is // processed as received in the Parse() // function. // Lexing the '=' sign: // - This is only done when parsing a long-form flag name. // - the first '=' found is considered a marker for long-form, splitting // the token into 2. // Example: --option=value=abc -> [--option, value=abc] // // In most cases, you want to define some flags, parse them, and query them. // Here is a small code sample: // // ```c // // Defines a '-h' boolean flag for help printing, optional. // FLAG_SHORT_bool(h, /*default=*/ false, "Print the help.", false); // // Defines a '--my-flag' string flag, required. // FLAG_LONG_string(my_flag, /*default=*/ "", "A magic flag!", true); // // int main(int argc, const char** argv) { // if (!flags::Parse(argv)) { // return -1; // } // // if (flags::h.value()) { // printf("usage: my-bin --my-flag=<value>\n"); // return 0; // } // // printf("flag value: %s\n", flags::my_flag.value().c_str()); // for (const std::string& arg : flags::positional_arguments) { // printf("arg: %s\n", arg.c_str()); // } // return 0; // } // ```c // Those macros can be used to define flags. // - They should be used in the global scope. // - Underscores in the flag variable name are replaced with hyphens ('-'). // // Example: // FLAG_SHORT_bool(my_flag, false, "some help", false); // - in the code: flags::my_flag // - command line: --my-flag=true // #define FLAG_LONG_string(Name, Default, Required) … #define FLAG_LONG_bool(Name, Default, Required) … #define FLAG_LONG_uint(Name, Default, Required) … #define FLAG_SHORT_string(Name, Default, Required) … #define FLAG_SHORT_bool(Name, Default, Required) … #define FLAG_SHORT_uint(Name, Default, Required) … namespace flags { // Parse the command-line arguments, checking flags, and separating positional // arguments from flags. // // * argv: the argv array received in the main function. This utility expects // the last pointer to // be NULL, as it should if coming from the main() function. // // Returns `true` if the parsing succeeds, `false` otherwise. bool Parse(const char** argv); } // namespace flags // ===================== BEGIN NON-PUBLIC SECTION ============================= // All the code below belongs to the implementation, and there is no guaranteed // around the API stability. Please do not use it directly. // Defines the static variable holding the flag, allowing access like // flags::my_flag. // By creating the FlagRegistration object, the flag can be added to // the global list. // The final `extern` definition is ONLY useful for clang-format: // - if the macro doesn't ends with a semicolon, clang-format goes wild. // - cannot disable clang-format for those macros on clang < 16. // (https://github.com/llvm/llvm-project/issues/54522) // - cannot allow trailing semi (-Wextra-semi). #define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort) … #define UTIL_FLAGS_FLAG_LONG(Type, Name, Default, Required) … #define UTIL_FLAGS_FLAG_SHORT(Type, Name, Default, Required) … namespace flags { // Just a wrapper around the flag value. template <typename T> struct Flag { … }; // To add support for new flag-types, this needs to be extended, and the visitor // below. FlagType; always_false_v; extern std::vector<std::string> positional_arguments; // Static class keeping track of the flags/arguments values. class FlagList { … }; template <typename T> struct FlagRegistration { … }; // Explicit deduction guide to avoid `-Wctad-maybe-unsupported`. template <typename T> FlagRegistration(Flag<T>&, std::string&&, bool, bool) -> FlagRegistration<T>; } // namespace flags #endif // INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_