mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-25 21:10:04 +00:00
690a9a4060
This fixes a -Wctad-maybe-unsupported error.
263 lines
9.2 KiB
C++
263 lines
9.2 KiB
C++
// 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_
|