xref: /aosp_15_r20/external/swiftshader/third_party/SPIRV-Tools/tools/util/flags.h (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1 // Copyright (c) 2023 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
16 #define INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
17 
18 #include <stdint.h>
19 
20 #include <functional>
21 #include <string>
22 #include <variant>
23 #include <vector>
24 
25 // This file provides some utils to define a command-line interface with
26 // required and optional flags.
27 //  - Flag order is not checked.
28 //  - Currently supported flag types: BOOLEAN, STRING
29 //  - As with most nix tools, using '--' in the command-line means all following
30 //  tokens will be considered positional
31 //    arguments.
32 //    Example: binary -g -- -g --some-other-flag
33 //      - the first `-g` is a flag.
34 //      - the second `-g` is not a flag.
35 //      - `--some-other-flag` is not a flag.
36 //  - Both long-form and short-form flags are supported, but boolean flags don't
37 //    support split boolean literals (short and long form).
38 //    Example:
39 //        -g              : allowed, sets g to true.
40 //        --my-flag       : allowed, sets --my-flag to true.
41 //        --my-flag=true  : allowed, sets --my-flag to true.
42 //        --my-flag true  : NOT allowed.
43 //        -g true         : NOT allowed.
44 //        --my-flag=TRUE  : NOT allowed.
45 //
46 //  - This implementation also supports string flags:
47 //        -o myfile.spv       : allowed, sets -o to `myfile.spv`.
48 //        --output=myfile.spv : allowed, sets --output to `myfile.spv`.
49 //        --output myfile.spv : allowd, sets --output to `myfile.spv`.
50 //
51 //    Note: then second token is NOT checked for hyphens.
52 //          --output -file.spv
53 //          flag name:  `output`
54 //          flag value: `-file.spv`
55 //
56 //  - This implementation generates flag at compile time. Meaning flag names
57 //  must be valid C++ identifiers.
58 //    However, flags are usually using hyphens for word separation. Hence
59 //    renaming is done behind the scenes. Example:
60 //      // Declaring a long-form flag.
61 //      FLAG_LONG_bool(my_flag, [...])
62 //
63 //      ->  in the code: flags::my_flag.value()
64 //      -> command-line: --my-flag
65 //
66 //  - The only additional lexing done is around '='. Otherwise token list is
67 //  processed as received in the Parse()
68 //    function.
69 //    Lexing the '=' sign:
70 //      - This is only done when parsing a long-form flag name.
71 //      - the first '=' found is considered a marker for long-form, splitting
72 //      the token into 2.
73 //        Example: --option=value=abc -> [--option, value=abc]
74 //
75 // In most cases, you want to define some flags, parse them, and query them.
76 // Here is a small code sample:
77 //
78 // ```c
79 //  // Defines a '-h' boolean flag for help printing, optional.
80 //  FLAG_SHORT_bool(h, /*default=*/ false, "Print the help.", false);
81 //  // Defines a '--my-flag' string flag, required.
82 //  FLAG_LONG_string(my_flag, /*default=*/ "", "A magic flag!", true);
83 //
84 //  int main(int argc, const char** argv) {
85 //    if (!flags::Parse(argv)) {
86 //      return -1;
87 //    }
88 //
89 //    if (flags::h.value()) {
90 //      printf("usage: my-bin --my-flag=<value>\n");
91 //      return 0;
92 //    }
93 //
94 //    printf("flag value: %s\n", flags::my_flag.value().c_str());
95 //    for (const std::string& arg : flags::positional_arguments) {
96 //      printf("arg: %s\n", arg.c_str());
97 //    }
98 //    return 0;
99 //  }
100 // ```c
101 
102 // Those macros can be used to define flags.
103 // - They should be used in the global scope.
104 // - Underscores in the flag variable name are replaced with hyphens ('-').
105 //
106 // Example:
107 //  FLAG_SHORT_bool(my_flag, false, "some help", false);
108 //    -  in the code: flags::my_flag
109 //    - command line: --my-flag=true
110 //
111 #define FLAG_LONG_string(Name, Default, Required) \
112   UTIL_FLAGS_FLAG_LONG(std::string, Name, Default, Required)
113 #define FLAG_LONG_bool(Name, Default, Required) \
114   UTIL_FLAGS_FLAG_LONG(bool, Name, Default, Required)
115 #define FLAG_LONG_uint(Name, Default, Required) \
116   UTIL_FLAGS_FLAG_LONG(uint32_t, Name, Default, Required)
117 
118 #define FLAG_SHORT_string(Name, Default, Required) \
119   UTIL_FLAGS_FLAG_SHORT(std::string, Name, Default, Required)
120 #define FLAG_SHORT_bool(Name, Default, Required) \
121   UTIL_FLAGS_FLAG_SHORT(bool, Name, Default, Required)
122 #define FLAG_SHORT_uint(Name, Default, Required) \
123   UTIL_FLAGS_FLAG_SHORT(uint32_t, Name, Default, Required)
124 
125 namespace flags {
126 
127 // Parse the command-line arguments, checking flags, and separating positional
128 // arguments from flags.
129 //
130 // * argv: the argv array received in the main function. This utility expects
131 // the last pointer to
132 //         be NULL, as it should if coming from the main() function.
133 //
134 // Returns `true` if the parsing succeeds, `false` otherwise.
135 bool Parse(const char** argv);
136 
137 }  // namespace flags
138 
139 // ===================== BEGIN NON-PUBLIC SECTION =============================
140 // All the code below belongs to the implementation, and there is no guaranteed
141 // around the API stability. Please do not use it directly.
142 
143 // Defines the static variable holding the flag, allowing access like
144 // flags::my_flag.
145 // By creating the FlagRegistration object, the flag can be added to
146 // the global list.
147 // The final `extern` definition is ONLY useful for clang-format:
148 //  - if the macro doesn't ends with a semicolon, clang-format goes wild.
149 //  - cannot disable clang-format for those macros on clang < 16.
150 //    (https://github.com/llvm/llvm-project/issues/54522)
151 //  - cannot allow trailing semi (-Wextra-semi).
152 #define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort)     \
153   namespace flags {                                                         \
154   Flag<Type> Name(Default);                                                 \
155   namespace {                                                               \
156   static FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
157                                               IsShort);                     \
158   }                                                                         \
159   }                                                                         \
160   extern flags::Flag<Type> flags::Name
161 
162 #define UTIL_FLAGS_FLAG_LONG(Type, Name, Default, Required) \
163   UTIL_FLAGS_FLAG(Type, "--", Name, Default, Required, false)
164 #define UTIL_FLAGS_FLAG_SHORT(Type, Name, Default, Required) \
165   UTIL_FLAGS_FLAG(Type, "-", Name, Default, Required, true)
166 
167 namespace flags {
168 
169 // Just a wrapper around the flag value.
170 template <typename T>
171 struct Flag {
172  public:
FlagFlag173   Flag(T&& default_value) : value_(default_value) {}
174   Flag(Flag&& other) = delete;
175   Flag(const Flag& other) = delete;
176 
valueFlag177   const T& value() const { return value_; }
valueFlag178   T& value() { return value_; }
179 
180  private:
181   T value_;
182 };
183 
184 // To add support for new flag-types, this needs to be extended, and the visitor
185 // below.
186 using FlagType = std::variant<std::reference_wrapper<Flag<std::string>>,
187                               std::reference_wrapper<Flag<bool>>,
188                               std::reference_wrapper<Flag<uint32_t>>>;
189 
190 template <class>
191 inline constexpr bool always_false_v = false;
192 
193 extern std::vector<std::string> positional_arguments;
194 
195 // Static class keeping track of the flags/arguments values.
196 class FlagList {
197   struct FlagInfo {
FlagInfoFlagInfo198     FlagInfo(FlagType&& flag_, std::string&& name_, bool required_,
199              bool is_short_)
200         : flag(std::move(flag_)),
201           name(std::move(name_)),
202           required(required_),
203           is_short(is_short_) {}
204 
205     FlagType flag;
206     std::string name;
207     bool required;
208     bool is_short;
209   };
210 
211  public:
212   template <typename T>
register_flag(Flag<T> & flag,std::string && name,bool required,bool is_short)213   static void register_flag(Flag<T>& flag, std::string&& name, bool required,
214                             bool is_short) {
215     get_flags().emplace_back(flag, std::move(name), required, is_short);
216   }
217 
218   static bool parse(const char** argv);
219 
220 #ifdef TESTING
221   // Flags are supposed to be constant for the whole app execution, hence the
222   // static storage. Gtest doesn't fork before running a test, meaning we have
223   // to manually clear the context at teardown.
reset()224   static void reset() {
225     get_flags().clear();
226     positional_arguments.clear();
227   }
228 #endif
229 
230  private:
get_flags()231   static std::vector<FlagInfo>& get_flags() {
232     static std::vector<FlagInfo> flags;
233     return flags;
234   }
235 
236   static bool parse_flag_info(FlagInfo& info, const char*** iterator);
237   static void print_usage(const char* binary_name,
238                           const std::string& usage_format);
239 };
240 
241 template <typename T>
242 struct FlagRegistration {
FlagRegistrationFlagRegistration243   FlagRegistration(Flag<T>& flag, std::string&& name, bool required,
244                    bool is_short) {
245     std::string fixed_name = name;
246     for (auto& c : fixed_name) {
247       if (c == '_') {
248         c = '-';
249       }
250     }
251 
252     FlagList::register_flag(flag, std::move(fixed_name), required, is_short);
253   }
254 };
255 
256 // Explicit deduction guide to avoid `-Wctad-maybe-unsupported`.
257 template <typename T>
258 FlagRegistration(Flag<T>&, std::string&&, bool, bool) -> FlagRegistration<T>;
259 
260 }  // namespace flags
261 
262 #endif  // INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
263