chromium/third_party/spirv-tools/src/tools/util/flags.h

// 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_