proxygen
ProgramOptions.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <unordered_map>
20 #include <unordered_set>
21 
22 #include <boost/version.hpp>
23 #include <glog/logging.h>
24 
25 #include <folly/Conv.h>
26 #include <folly/Portability.h>
28 
29 namespace po = ::boost::program_options;
30 
31 namespace folly {
32 
33 namespace {
34 
35 // Information about one GFlag. Handled via shared_ptr, as, in the case
36 // of boolean flags, two boost::program_options options (--foo and --nofoo)
37 // may share the same GFlag underneath.
38 //
39 // We're slightly abusing the boost::program_options interface; the first
40 // time we (successfully) parse a value that matches this GFlag, we'll set
41 // it and remember not to set it again; this prevents, for example, the
42 // default value of --foo from overwriting the GFlag if --nofoo is set.
43 template <class T>
44 class GFlagInfo {
45  public:
46  explicit GFlagInfo(gflags::CommandLineFlagInfo info)
47  : info_(std::move(info)), isSet_(false) {}
48 
49  void set(const T& value) {
50  if (isSet_) {
51  return;
52  }
53 
54  auto strValue = folly::to<std::string>(value);
55  auto msg =
56  gflags::SetCommandLineOption(info_.name.c_str(), strValue.c_str());
57  if (msg.empty()) {
58  throw po::invalid_option_value(strValue);
59  }
60  isSet_ = true;
61  }
62 
63  T get() const {
64  std::string str;
65  CHECK(gflags::GetCommandLineOption(info_.name.c_str(), &str));
66  return folly::to<T>(str);
67  }
68 
69  const gflags::CommandLineFlagInfo& info() const {
70  return info_;
71  }
72 
73  private:
74  gflags::CommandLineFlagInfo info_;
75  bool isSet_;
76 };
77 
78 template <class T>
79 class GFlagValueSemanticBase : public po::value_semantic {
80  public:
81  explicit GFlagValueSemanticBase(std::shared_ptr<GFlagInfo<T>> info)
82  : info_(std::move(info)) {}
83 
84  std::string name() const override {
85  return "arg";
86  }
87 #if BOOST_VERSION >= 105900 && BOOST_VERSION <= 106400
88  bool adjacent_tokens_only() const override {
89  return false;
90  }
91 #endif
92  bool is_composing() const override {
93  return false;
94  }
95  bool is_required() const override {
96  return false;
97  }
98  // We handle setting the GFlags from parse(), so notify() does nothing.
99  void notify(const boost::any& /* valueStore */) const override {}
100  bool apply_default(boost::any& valueStore) const override {
101  // We're using the *current* rather than *default* value here, and
102  // this is intentional; GFlags-using programs assign to FLAGS_foo
103  // before ParseCommandLineFlags() in order to change the default value,
104  // and we obey that.
105  auto val = info_->get();
106  this->transform(val);
107  valueStore = val;
108  return true;
109  }
110 
111  void parse(
112  boost::any& valueStore,
113  const std::vector<std::string>& tokens,
114  bool /* utf8 */) const override;
115 
116  private:
117  virtual T parseValue(const std::vector<std::string>& tokens) const = 0;
118  virtual void transform(T& /* val */) const {}
119 
120  mutable std::shared_ptr<GFlagInfo<T>> info_;
121 };
122 
123 template <class T>
125  boost::any& valueStore,
126  const std::vector<std::string>& tokens,
127  bool /* utf8 */) const {
128  T val;
129  try {
130  val = this->parseValue(tokens);
131  this->transform(val);
132  } catch (const std::exception&) {
133  throw po::invalid_option_value(
134  tokens.empty() ? std::string() : tokens.front());
135  }
136  this->info_->set(val);
137  valueStore = val;
138 }
139 
140 template <class T>
141 class GFlagValueSemantic : public GFlagValueSemanticBase<T> {
142  public:
143  explicit GFlagValueSemantic(std::shared_ptr<GFlagInfo<T>> info)
144  : GFlagValueSemanticBase<T>(std::move(info)) {}
145 
146  unsigned min_tokens() const override {
147  return 1;
148  }
149  unsigned max_tokens() const override {
150  return 1;
151  }
152 
153  T parseValue(const std::vector<std::string>& tokens) const override {
154  DCHECK(tokens.size() == 1);
155  return folly::to<T>(tokens.front());
156  }
157 };
158 
159 class BoolGFlagValueSemantic : public GFlagValueSemanticBase<bool> {
160  public:
161  explicit BoolGFlagValueSemantic(std::shared_ptr<GFlagInfo<bool>> info)
162  : GFlagValueSemanticBase<bool>(std::move(info)) {}
163 
164  unsigned min_tokens() const override {
165  return 0;
166  }
167  unsigned max_tokens() const override {
168  return 0;
169  }
170 
171  bool parseValue(const std::vector<std::string>& tokens) const override {
172  DCHECK(tokens.empty());
173  return true;
174  }
175 };
176 
177 class NegativeBoolGFlagValueSemantic : public BoolGFlagValueSemantic {
178  public:
179  explicit NegativeBoolGFlagValueSemantic(std::shared_ptr<GFlagInfo<bool>> info)
180  : BoolGFlagValueSemantic(std::move(info)) {}
181 
182  private:
183  void transform(bool& val) const override {
184  val = !val;
185  }
186 };
187 
188 const std::string& getName(const std::string& name) {
189  static const std::unordered_map<std::string, std::string> gFlagOverrides{
190  // Allow -v in addition to --v
191  {"v", "v,v"},
192  };
193  auto pos = gFlagOverrides.find(name);
194  return pos != gFlagOverrides.end() ? pos->second : name;
195 }
196 
197 template <class T>
198 void addGFlag(
199  gflags::CommandLineFlagInfo&& flag,
200  po::options_description& desc,
201  ProgramOptionsStyle style) {
202  auto gflagInfo = std::make_shared<GFlagInfo<T>>(std::move(flag));
203  auto& info = gflagInfo->info();
204  auto name = getName(info.name);
205 
206  switch (style) {
208  break;
210  std::replace(name.begin(), name.end(), '_', '-');
211  break;
212  }
213  desc.add_options()(
214  name.c_str(),
215  new GFlagValueSemantic<T>(gflagInfo),
216  info.description.c_str());
217 }
218 
219 template <>
220 void addGFlag<bool>(
221  gflags::CommandLineFlagInfo&& flag,
222  po::options_description& desc,
223  ProgramOptionsStyle style) {
224  auto gflagInfo = std::make_shared<GFlagInfo<bool>>(std::move(flag));
225  auto& info = gflagInfo->info();
226  auto name = getName(info.name);
227  std::string negationPrefix;
228 
229  switch (style) {
231  negationPrefix = "no";
232  break;
234  std::replace(name.begin(), name.end(), '_', '-');
235  negationPrefix = "no-";
236  break;
237  }
238 
239  // clang-format off
240  desc.add_options()
241  (name.c_str(),
242  new BoolGFlagValueSemantic(gflagInfo),
243  info.description.c_str())
244  ((negationPrefix + name).c_str(),
245  new NegativeBoolGFlagValueSemantic(gflagInfo),
246  folly::to<std::string>("(no) ", info.description).c_str());
247  // clang-format on
248 }
249 
250 typedef void (*FlagAdder)(
251  gflags::CommandLineFlagInfo&&,
252  po::options_description&,
254 
255 const std::unordered_map<std::string, FlagAdder> gFlagAdders = {
256 #define X(NAME, TYPE) \
257  { NAME, addGFlag<TYPE> }
258  X("bool", bool),
259  X("int32", int32_t),
260  X("int64", int64_t),
261  X("uint32", uint32_t),
262  X("uint64", uint64_t),
263  X("double", double),
264  X("string", std::string),
265 #undef X
266 };
267 
268 } // namespace
269 
270 po::options_description getGFlags(ProgramOptionsStyle style) {
271  static const std::unordered_set<std::string> gSkipFlags{
272  "flagfile",
273  "fromenv",
274  "tryfromenv",
275  "undefok",
276  "help",
277  "helpfull",
278  "helpshort",
279  "helpon",
280  "helpmatch",
281  "helppackage",
282  "helpxml",
283  "version",
284  "tab_completion_columns",
285  "tab_completion_word",
286  };
287 
288  po::options_description desc("GFlags");
289 
290  std::vector<gflags::CommandLineFlagInfo> allFlags;
291  gflags::GetAllFlags(&allFlags);
292 
293  for (auto& f : allFlags) {
294  if (gSkipFlags.count(f.name)) {
295  continue;
296  }
297  auto pos = gFlagAdders.find(f.type);
298  CHECK(pos != gFlagAdders.end()) << "Invalid flag type: " << f.type;
299  (*pos->second)(std::move(f), desc, style);
300  }
301 
302  return desc;
303 }
304 
305 namespace {
306 
307 NestedCommandLineParseResult doParseNestedCommandLine(
308  po::command_line_parser&& parser,
309  const po::options_description& desc) {
311 
312  result.options = parser.options(desc).allow_unregistered().run();
313 
314  bool setCommand = true;
315  for (auto& opt : result.options.options) {
316  auto& tokens = opt.original_tokens;
317  auto tokensStart = tokens.begin();
318 
319  if (setCommand && opt.position_key != -1) {
320  DCHECK(tokensStart != tokens.end());
321  result.command = *(tokensStart++);
322  }
323 
324  if (opt.position_key != -1 || opt.unregistered) {
325  // If we see an unrecognized option before the first positional
326  // argument, assume we don't have a valid command name, because
327  // we don't know how to parse it otherwise.
328  //
329  // program --wtf foo bar
330  //
331  // Is "foo" an argument to "--wtf", or the command name?
332  setCommand = false;
333  result.rest.insert(result.rest.end(), tokensStart, tokens.end());
334  }
335  }
336 
337  return result;
338 }
339 
340 } // namespace
341 
343  int argc,
344  const char* const argv[],
345  const po::options_description& desc) {
346  return doParseNestedCommandLine(po::command_line_parser(argc, argv), desc);
347 }
348 
350  const std::vector<std::string>& cmdline,
351  const po::options_description& desc) {
352  return doParseNestedCommandLine(po::command_line_parser(cmdline), desc);
353 }
354 
355 } // namespace folly
size_t parse(const char *buf, size_t len)
Definition: test.c:1591
def info()
Definition: deadlock.py:447
auto f
NestedCommandLineParseResult parseNestedCommandLine(int argc, const char *const argv[], const po::options_description &desc)
program_options
Definition: CMakeCache.txt:862
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
STL namespace.
double val
Definition: String.cpp:273
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static once_flag flag
Definition: Random.cpp:75
static http_parser * parser
Definition: test.c:40
PUSHMI_INLINE_VAR constexpr detail::transform_fn transform
Definition: transform.h:158
Composed any(Predicate pred=Predicate())
Definition: Base.h:758
void BENCHFUN() replace(size_t iters, size_t arg)
const char * name
Definition: http_parser.c:437
char ** argv
#define X(NAME, TYPE)
std::vector< std::string > rest
bool isSet_
ProgramOptionsStyle
gflags::CommandLineFlagInfo info_
const char * string
Definition: Conv.cpp:212
Optional< std::string > command
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
po::options_description getGFlags(ProgramOptionsStyle style)
static const char tokens[256]
Definition: http_parser.c:184
boost::program_options::parsed_options options