| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- // 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) \
- UTIL_FLAGS_FLAG_LONG(std::string, Name, Default, Required)
- #define FLAG_LONG_bool(Name, Default, Required) \
- UTIL_FLAGS_FLAG_LONG(bool, Name, Default, Required)
- #define FLAG_LONG_uint(Name, Default, Required) \
- UTIL_FLAGS_FLAG_LONG(uint32_t, Name, Default, Required)
- #define FLAG_SHORT_string(Name, Default, Required) \
- UTIL_FLAGS_FLAG_SHORT(std::string, Name, Default, Required)
- #define FLAG_SHORT_bool(Name, Default, Required) \
- UTIL_FLAGS_FLAG_SHORT(bool, Name, Default, Required)
- #define FLAG_SHORT_uint(Name, Default, Required) \
- UTIL_FLAGS_FLAG_SHORT(uint32_t, 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) \
- namespace flags { \
- Flag<Type> Name(Default); \
- namespace { \
- static FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
- IsShort); \
- } \
- } \
- extern flags::Flag<Type> flags::Name
- #define UTIL_FLAGS_FLAG_LONG(Type, Name, Default, Required) \
- UTIL_FLAGS_FLAG(Type, "--", Name, Default, Required, false)
- #define UTIL_FLAGS_FLAG_SHORT(Type, Name, Default, Required) \
- UTIL_FLAGS_FLAG(Type, "-", Name, Default, Required, true)
- namespace flags {
- // Just a wrapper around the flag value.
- template <typename T>
- struct Flag {
- public:
- Flag(T&& default_value) : value_(default_value) {}
- Flag(Flag&& other) = delete;
- Flag(const Flag& other) = delete;
- const T& value() const { return value_; }
- T& value() { return value_; }
- private:
- T value_;
- };
- // To add support for new flag-types, this needs to be extended, and the visitor
- // below.
- using FlagType = std::variant<std::reference_wrapper<Flag<std::string>>,
- std::reference_wrapper<Flag<bool>>,
- std::reference_wrapper<Flag<uint32_t>>>;
- template <class>
- inline constexpr bool always_false_v = false;
- extern std::vector<std::string> positional_arguments;
- // Static class keeping track of the flags/arguments values.
- class FlagList {
- struct FlagInfo {
- FlagInfo(FlagType&& flag_, std::string&& name_, bool required_,
- bool is_short_)
- : flag(std::move(flag_)),
- name(std::move(name_)),
- required(required_),
- is_short(is_short_) {}
- FlagType flag;
- std::string name;
- bool required;
- bool is_short;
- };
- public:
- template <typename T>
- static void register_flag(Flag<T>& flag, std::string&& name, bool required,
- bool is_short) {
- get_flags().emplace_back(flag, std::move(name), required, is_short);
- }
- static bool parse(const char** argv);
- #ifdef TESTING
- // Flags are supposed to be constant for the whole app execution, hence the
- // static storage. Gtest doesn't fork before running a test, meaning we have
- // to manually clear the context at teardown.
- static void reset() {
- get_flags().clear();
- positional_arguments.clear();
- }
- #endif
- private:
- static std::vector<FlagInfo>& get_flags() {
- static std::vector<FlagInfo> flags;
- return flags;
- }
- static bool parse_flag_info(FlagInfo& info, const char*** iterator);
- static void print_usage(const char* binary_name,
- const std::string& usage_format);
- };
- template <typename T>
- struct FlagRegistration {
- FlagRegistration(Flag<T>& flag, std::string&& name, bool required,
- bool is_short) {
- std::string fixed_name = name;
- for (auto& c : fixed_name) {
- if (c == '_') {
- c = '-';
- }
- }
- FlagList::register_flag(flag, std::move(fixed_name), required, is_short);
- }
- };
- // 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_
|