usage.cc
Go to the documentation of this file.
1 //
2 // Copyright 2019 The Abseil Authors.
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 // https://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 
17 
18 #include <map>
19 #include <string>
20 
21 #include "absl/flags/flag.h"
25 #include "absl/strings/ascii.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/str_split.h"
29 
30 ABSL_FLAG(bool, help, false,
31  "show help on important flags for this binary [tip: all flags can "
32  "have two dashes]");
33 ABSL_FLAG(bool, helpfull, false, "show help on all flags");
34 ABSL_FLAG(bool, helpshort, false,
35  "show help on only the main module for this program");
36 ABSL_FLAG(bool, helppackage, false,
37  "show help on all modules in the main package");
38 ABSL_FLAG(bool, version, false, "show version and build info and exit");
39 ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags");
40 ABSL_FLAG(std::string, helpon, "",
41  "show help on the modules named by this flag value");
42 ABSL_FLAG(std::string, helpmatch, "",
43  "show help on modules whose name contains the specified substr");
44 
45 namespace absl {
46 namespace flags_internal {
47 namespace {
48 
49 // This class is used to emit an XML element with `tag` and `text`.
50 // It adds opening and closing tags and escapes special characters in the text.
51 // For example:
52 // std::cout << XMLElement("title", "Milk & Cookies");
53 // prints "<title>Milk &amp; Cookies</title>"
54 class XMLElement {
55  public:
56  XMLElement(absl::string_view tag, absl::string_view txt)
57  : tag_(tag), txt_(txt) {}
58 
59  friend std::ostream& operator<<(std::ostream& out,
60  const XMLElement& xml_elem) {
61  out << "<" << xml_elem.tag_ << ">";
62 
63  for (auto c : xml_elem.txt_) {
64  switch (c) {
65  case '"':
66  out << "&quot;";
67  break;
68  case '\'':
69  out << "&apos;";
70  break;
71  case '&':
72  out << "&amp;";
73  break;
74  case '<':
75  out << "&lt;";
76  break;
77  case '>':
78  out << "&gt;";
79  break;
80  default:
81  out << c;
82  break;
83  }
84  }
85 
86  return out << "</" << xml_elem.tag_ << ">";
87  }
88 
89  private:
92 };
93 
94 // --------------------------------------------------------------------
95 // Helper class to pretty-print info about a flag.
96 
97 class FlagHelpPrettyPrinter {
98  public:
99  // Pretty printer holds on to the std::ostream& reference to direct an output
100  // to that stream.
101  FlagHelpPrettyPrinter(int max_line_len, std::ostream* out)
102  : out_(*out),
103  max_line_len_(max_line_len),
104  line_len_(0),
105  first_line_(true) {}
106 
107  void Write(absl::string_view str, bool wrap_line = false) {
108  // Empty std::string - do nothing.
109  if (str.empty()) return;
110 
111  std::vector<absl::string_view> tokens;
112  if (wrap_line) {
113  tokens = absl::StrSplit(str, absl::ByAnyChar(" \f\n\r\t\v"),
114  absl::SkipEmpty());
115  } else {
116  tokens.push_back(str);
117  }
118 
119  for (auto token : tokens) {
120  bool new_line = (line_len_ == 0);
121 
122  // Write the token, ending the std::string first if necessary/possible.
123  if (!new_line && (line_len_ + token.size() >= max_line_len_)) {
124  EndLine();
125  new_line = true;
126  }
127 
128  if (new_line) {
129  StartLine();
130  } else {
131  out_ << ' ';
132  ++line_len_;
133  }
134 
135  out_ << token;
136  line_len_ += token.size();
137  }
138  }
139 
140  void StartLine() {
141  if (first_line_) {
142  out_ << " ";
143  line_len_ = 4;
144  first_line_ = false;
145  } else {
146  out_ << " ";
147  line_len_ = 6;
148  }
149  }
150  void EndLine() {
151  out_ << '\n';
152  line_len_ = 0;
153  }
154 
155  private:
156  std::ostream& out_;
157  const int max_line_len_;
160 };
161 
162 void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
163  std::ostream* out) {
164  FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
165 
166  // Flag name.
167  printer.Write(absl::StrCat("-", flag.Name()));
168 
169  // Flag help.
170  printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
171 
172  // Flag data type (for V1 flags only).
173  if (!flag.IsAbseilFlag() && !flag.IsRetired()) {
174  printer.Write(absl::StrCat("type: ", flag.Typename(), ";"));
175  }
176 
177  // The listed default value will be the actual default from the flag
178  // definition in the originating source file, unless the value has
179  // subsequently been modified using SetCommandLineOption() with mode
180  // SET_FLAGS_DEFAULT.
181  std::string dflt_val = flag.DefaultValue();
182  if (flag.IsOfType<std::string>()) {
183  dflt_val = absl::StrCat("\"", dflt_val, "\"");
184  }
185  printer.Write(absl::StrCat("default: ", dflt_val, ";"));
186 
187  if (flag.modified) {
188  std::string curr_val = flag.CurrentValue();
189  if (flag.IsOfType<std::string>()) {
190  curr_val = absl::StrCat("\"", curr_val, "\"");
191  }
192  printer.Write(absl::StrCat("currently: ", curr_val, ";"));
193  }
194 
195  printer.EndLine();
196 }
197 
198 // Shows help for every filename which matches any of the filters
199 // If filters are empty, shows help for every file.
200 // If a flag's help message has been stripped (e.g. by adding '#define
201 // STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
202 // and its variants.
203 void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
208  } else {
209  // XML schema is not a part of our public API for now.
210  out << "<?xml version=\"1.0\"?>\n"
211  // The document.
212  << "<AllFlags>\n"
213  // The program name and usage.
214  << XMLElement("program", flags_internal::ShortProgramInvocationName())
215  << '\n'
216  << XMLElement("usage", flags_internal::ProgramUsageMessage()) << '\n';
217  }
218 
219  // Map of package name to
220  // map of file name to
221  // vector of flags in the file.
222  // This map is used to output matching flags grouped by package and file
223  // name.
224  std::map<std::string,
225  std::map<std::string,
226  std::vector<const flags_internal::CommandLineFlag*>>>
227  matching_flags;
228 
229  flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
231 
232  std::string flag_filename = flag->Filename();
233 
234  // Ignore retired flags.
235  if (flag->IsRetired()) return;
236 
237  // If the flag has been stripped, pretend that it doesn't exist.
238  if (flag->Help() == flags_internal::kStrippedFlagHelp) return;
239 
240  // Make sure flag satisfies the filter
241  if (!filter_cb || !filter_cb(flag_filename)) return;
242 
243  matching_flags[std::string(flags_internal::Package(flag_filename))]
244  [flag_filename]
245  .push_back(flag);
246  });
247 
249  package_separator; // controls blank lines between packages.
250  absl::string_view file_separator; // controls blank lines between files.
251  for (const auto& package : matching_flags) {
253  out << package_separator;
254  package_separator = "\n\n";
255  }
256 
257  file_separator = "";
258  for (const auto& flags_in_file : package.second) {
260  out << file_separator << " Flags from " << flags_in_file.first
261  << ":\n";
262  file_separator = "\n";
263  }
264 
265  for (const auto* flag : flags_in_file.second) {
266  flags_internal::FlagHelp(out, *flag, format);
267  }
268  }
269  }
270 
272  if (filter_cb && matching_flags.empty()) {
273  out << " No modules matched: use -helpfull\n";
274  }
275  } else {
276  // The end of the document.
277  out << "</AllFlags>\n";
278  }
279 }
280 
281 ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit);
282 ABSL_CONST_INIT std::string* program_usage_message
283  GUARDED_BY(usage_message_guard) = nullptr;
284 
285 } // namespace
286 
287 // --------------------------------------------------------------------
288 // Sets the "usage" message to be used by help reporting routines.
289 
290 void SetProgramUsageMessage(absl::string_view new_usage_message) {
291  absl::MutexLock l(&usage_message_guard);
292 
293  if (flags_internal::program_usage_message != nullptr) {
294  ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice.");
295  std::exit(1);
296  }
297 
298  program_usage_message = new std::string(new_usage_message);
299 }
300 
301 // --------------------------------------------------------------------
302 // Returns the usage message set by SetProgramUsageMessage().
303 // Note: We able to return string_view here only because calling
304 // SetProgramUsageMessage twice is prohibited.
306  absl::MutexLock l(&usage_message_guard);
307 
308  return program_usage_message != nullptr
309  ? absl::string_view(*program_usage_message)
310  : "Warning: SetProgramUsageMessage() never called";
311 }
312 
313 // --------------------------------------------------------------------
314 // Produces the help message describing specific flag.
315 void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
316  HelpFormat format) {
317  if (format == HelpFormat::kHumanReadable)
318  flags_internal::FlagHelpHumanReadable(flag, &out);
319 }
320 
321 // --------------------------------------------------------------------
322 // Produces the help messages for all flags matching the filter.
323 // If filter is empty produces help messages for all flags.
324 void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format) {
325  flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
326  return filter.empty() || filename.find(filter) != absl::string_view::npos;
327  };
328  flags_internal::FlagsHelpImpl(out, filter_cb, format);
329 }
330 
331 // --------------------------------------------------------------------
332 // Checks all the 'usage' command line flags to see if any have been set.
333 // If so, handles them appropriately.
334 int HandleUsageFlags(std::ostream& out) {
335  if (absl::GetFlag(FLAGS_helpshort)) {
336  flags_internal::FlagsHelpImpl(
337  out, flags_internal::GetUsageConfig().contains_helpshort_flags,
339  return 1;
340  }
341 
342  if (absl::GetFlag(FLAGS_helpfull)) {
343  // show all options
345  return 1;
346  }
347 
348  if (!absl::GetFlag(FLAGS_helpon).empty()) {
350  out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."));
351  return 1;
352  }
353 
354  if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
355  flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch));
356  return 1;
357  }
358 
359  if (absl::GetFlag(FLAGS_help)) {
360  flags_internal::FlagsHelpImpl(
361  out, flags_internal::GetUsageConfig().contains_help_flags);
362 
363  out << "\nTry --helpfull to get a list of all flags.\n";
364 
365  return 1;
366  }
367 
368  if (absl::GetFlag(FLAGS_helppackage)) {
369  flags_internal::FlagsHelpImpl(
370  out, flags_internal::GetUsageConfig().contains_helppackage_flags);
371 
372  out << "\nTry --helpfull to get a list of all flags.\n";
373 
374  return 1;
375  }
376 
377  if (absl::GetFlag(FLAGS_version)) {
378  if (flags_internal::GetUsageConfig().version_string)
380  // Unlike help, we may be asking for version in a script, so return 0
381  return 0;
382  }
383 
384  if (absl::GetFlag(FLAGS_only_check_args)) {
385  return 0;
386  }
387 
388  return -1;
389 }
390 
391 } // namespace flags_internal
392 } // namespace absl
void SetProgramUsageMessage(absl::string_view new_usage_message)
Definition: usage.cc:290
bool first_line_
Definition: usage.cc:159
void FlagHelp(std::ostream &out, const flags_internal::CommandLineFlag &flag, HelpFormat format)
Definition: usage.cc:315
absl::Mutex * InitFlagIfNecessary(CommandLineFlag *flag) LOCK_RETURNED(flag-> locks->primary_mu)
#define ABSL_CONST_INIT
Definition: attributes.h:605
static constexpr size_type npos
Definition: string_view.h:159
absl::string_view Package(absl::string_view filename)
Definition: path_util.h:49
uint32_t version
Definition: graphcycles.cc:278
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: str_cat.cc:98
std::ostream & operator<<(std::ostream &os, absl::LogSeverity s)
Definition: log_severity.cc:21
const int max_line_len_
Definition: usage.cc:157
void FlagsHelp(std::ostream &out, absl::string_view filter, HelpFormat format)
Definition: usage.cc:324
absl::string_view txt_
Definition: usage.cc:91
Definition: algorithm.h:29
std::function< bool(absl::string_view)> FlagKindFilter
Definition: usage_config.h:59
strings_internal::Splitter< typename strings_internal::SelectDelimiter< Delimiter >::type, AllowEmpty > StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d)
Definition: str_split.h:491
#define ABSL_INTERNAL_LOG(severity, message)
Definition: raw_logging.h:73
std::ostream & out_
Definition: usage.cc:156
std::string format(const std::string &, const time_point< seconds > &, const femtoseconds &, const time_zone &)
int HandleUsageFlags(std::ostream &out)
Definition: usage.cc:334
const char kStrippedFlagHelp[]
FlagsUsageConfig GetUsageConfig()
absl::string_view tag_
Definition: usage.cc:90
absl::string_view ProgramUsageMessage()
Definition: usage.cc:305
constexpr bool empty() const noexcept
Definition: string_view.h:277
std::string ShortProgramInvocationName()
Definition: program_name.cc:36
std::function< std::string()> version_string
Definition: usage_config.h:93
static ABSL_CONST_INIT std::string *program_name GUARDED_BY(program_name_guard)
T GetFlag(const absl::Flag< T > &flag)
Definition: flag.h:86
char * out
Definition: mutex.h:1013
ABSL_FLAG(bool, help, false,"show help on important flags for this binary [tip: all flags can ""have two dashes]")
int line_len_
Definition: usage.cc:158
void ForEachFlag(std::function< void(CommandLineFlag *)> visitor)
Definition: registry.cc:459


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:19:58