usage.cc
Go to the documentation of this file.
00001 //
00002 // Copyright 2019 The Abseil Authors.
00003 //
00004 // Licensed under the Apache License, Version 2.0 (the "License");
00005 // you may not use this file except in compliance with the License.
00006 // You may obtain a copy of the License at
00007 //
00008 //      https://www.apache.org/licenses/LICENSE-2.0
00009 //
00010 // Unless required by applicable law or agreed to in writing, software
00011 // distributed under the License is distributed on an "AS IS" BASIS,
00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 // See the License for the specific language governing permissions and
00014 // limitations under the License.
00015 
00016 #include "absl/flags/internal/usage.h"
00017 
00018 #include <map>
00019 #include <string>
00020 
00021 #include "absl/flags/flag.h"
00022 #include "absl/flags/internal/path_util.h"
00023 #include "absl/flags/internal/program_name.h"
00024 #include "absl/flags/usage_config.h"
00025 #include "absl/strings/ascii.h"
00026 #include "absl/strings/str_cat.h"
00027 #include "absl/strings/str_split.h"
00028 #include "absl/synchronization/mutex.h"
00029 
00030 ABSL_FLAG(bool, help, false,
00031           "show help on important flags for this binary [tip: all flags can "
00032           "have two dashes]");
00033 ABSL_FLAG(bool, helpfull, false, "show help on all flags");
00034 ABSL_FLAG(bool, helpshort, false,
00035           "show help on only the main module for this program");
00036 ABSL_FLAG(bool, helppackage, false,
00037           "show help on all modules in the main package");
00038 ABSL_FLAG(bool, version, false, "show version and build info and exit");
00039 ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags");
00040 ABSL_FLAG(std::string, helpon, "",
00041           "show help on the modules named by this flag value");
00042 ABSL_FLAG(std::string, helpmatch, "",
00043           "show help on modules whose name contains the specified substr");
00044 
00045 namespace absl {
00046 namespace flags_internal {
00047 namespace {
00048 
00049 // This class is used to emit an XML element with `tag` and `text`.
00050 // It adds opening and closing tags and escapes special characters in the text.
00051 // For example:
00052 // std::cout << XMLElement("title", "Milk & Cookies");
00053 // prints "<title>Milk &amp; Cookies</title>"
00054 class XMLElement {
00055  public:
00056   XMLElement(absl::string_view tag, absl::string_view txt)
00057       : tag_(tag), txt_(txt) {}
00058 
00059   friend std::ostream& operator<<(std::ostream& out,
00060                                   const XMLElement& xml_elem) {
00061     out << "<" << xml_elem.tag_ << ">";
00062 
00063     for (auto c : xml_elem.txt_) {
00064       switch (c) {
00065         case '"':
00066           out << "&quot;";
00067           break;
00068         case '\'':
00069           out << "&apos;";
00070           break;
00071         case '&':
00072           out << "&amp;";
00073           break;
00074         case '<':
00075           out << "&lt;";
00076           break;
00077         case '>':
00078           out << "&gt;";
00079           break;
00080         default:
00081           out << c;
00082           break;
00083       }
00084     }
00085 
00086     return out << "</" << xml_elem.tag_ << ">";
00087   }
00088 
00089  private:
00090   absl::string_view tag_;
00091   absl::string_view txt_;
00092 };
00093 
00094 // --------------------------------------------------------------------
00095 // Helper class to pretty-print info about a flag.
00096 
00097 class FlagHelpPrettyPrinter {
00098  public:
00099   // Pretty printer holds on to the std::ostream& reference to direct an output
00100   // to that stream.
00101   FlagHelpPrettyPrinter(int max_line_len, std::ostream* out)
00102       : out_(*out),
00103         max_line_len_(max_line_len),
00104         line_len_(0),
00105         first_line_(true) {}
00106 
00107   void Write(absl::string_view str, bool wrap_line = false) {
00108     // Empty std::string - do nothing.
00109     if (str.empty()) return;
00110 
00111     std::vector<absl::string_view> tokens;
00112     if (wrap_line) {
00113       tokens = absl::StrSplit(str, absl::ByAnyChar(" \f\n\r\t\v"),
00114                               absl::SkipEmpty());
00115     } else {
00116       tokens.push_back(str);
00117     }
00118 
00119     for (auto token : tokens) {
00120       bool new_line = (line_len_ == 0);
00121 
00122       // Write the token, ending the std::string first if necessary/possible.
00123       if (!new_line && (line_len_ + token.size() >= max_line_len_)) {
00124         EndLine();
00125         new_line = true;
00126       }
00127 
00128       if (new_line) {
00129         StartLine();
00130       } else {
00131         out_ << ' ';
00132         ++line_len_;
00133       }
00134 
00135       out_ << token;
00136       line_len_ += token.size();
00137     }
00138   }
00139 
00140   void StartLine() {
00141     if (first_line_) {
00142       out_ << "    ";
00143       line_len_ = 4;
00144       first_line_ = false;
00145     } else {
00146       out_ << "      ";
00147       line_len_ = 6;
00148     }
00149   }
00150   void EndLine() {
00151     out_ << '\n';
00152     line_len_ = 0;
00153   }
00154 
00155  private:
00156   std::ostream& out_;
00157   const int max_line_len_;
00158   int line_len_;
00159   bool first_line_;
00160 };
00161 
00162 void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
00163                            std::ostream* out) {
00164   FlagHelpPrettyPrinter printer(80, out);  // Max line length is 80.
00165 
00166   // Flag name.
00167   printer.Write(absl::StrCat("-", flag.Name()));
00168 
00169   // Flag help.
00170   printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
00171 
00172   // Flag data type (for V1 flags only).
00173   if (!flag.IsAbseilFlag() && !flag.IsRetired()) {
00174     printer.Write(absl::StrCat("type: ", flag.Typename(), ";"));
00175   }
00176 
00177   // The listed default value will be the actual default from the flag
00178   // definition in the originating source file, unless the value has
00179   // subsequently been modified using SetCommandLineOption() with mode
00180   // SET_FLAGS_DEFAULT.
00181   std::string dflt_val = flag.DefaultValue();
00182   if (flag.IsOfType<std::string>()) {
00183     dflt_val = absl::StrCat("\"", dflt_val, "\"");
00184   }
00185   printer.Write(absl::StrCat("default: ", dflt_val, ";"));
00186 
00187   if (flag.modified) {
00188     std::string curr_val = flag.CurrentValue();
00189     if (flag.IsOfType<std::string>()) {
00190       curr_val = absl::StrCat("\"", curr_val, "\"");
00191     }
00192     printer.Write(absl::StrCat("currently: ", curr_val, ";"));
00193   }
00194 
00195   printer.EndLine();
00196 }
00197 
00198 // Shows help for every filename which matches any of the filters
00199 // If filters are empty, shows help for every file.
00200 // If a flag's help message has been stripped (e.g. by adding '#define
00201 // STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
00202 // and its variants.
00203 void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
00204                    HelpFormat format = HelpFormat::kHumanReadable) {
00205   if (format == HelpFormat::kHumanReadable) {
00206     out << flags_internal::ShortProgramInvocationName() << ": "
00207         << flags_internal::ProgramUsageMessage() << "\n\n";
00208   } else {
00209     // XML schema is not a part of our public API for now.
00210     out << "<?xml version=\"1.0\"?>\n"
00211         // The document.
00212         << "<AllFlags>\n"
00213         // The program name and usage.
00214         << XMLElement("program", flags_internal::ShortProgramInvocationName())
00215         << '\n'
00216         << XMLElement("usage", flags_internal::ProgramUsageMessage()) << '\n';
00217   }
00218 
00219   // Map of package name to
00220   //   map of file name to
00221   //     vector of flags in the file.
00222   // This map is used to output matching flags grouped by package and file
00223   // name.
00224   std::map<std::string,
00225            std::map<std::string,
00226                     std::vector<const flags_internal::CommandLineFlag*>>>
00227       matching_flags;
00228 
00229   flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
00230     absl::MutexLock l(InitFlagIfNecessary(flag));
00231 
00232     std::string flag_filename = flag->Filename();
00233 
00234     // Ignore retired flags.
00235     if (flag->IsRetired()) return;
00236 
00237     // If the flag has been stripped, pretend that it doesn't exist.
00238     if (flag->Help() == flags_internal::kStrippedFlagHelp) return;
00239 
00240     // Make sure flag satisfies the filter
00241     if (!filter_cb || !filter_cb(flag_filename)) return;
00242 
00243     matching_flags[std::string(flags_internal::Package(flag_filename))]
00244                   [flag_filename]
00245                       .push_back(flag);
00246   });
00247 
00248   absl::string_view
00249       package_separator;             // controls blank lines between packages.
00250   absl::string_view file_separator;  // controls blank lines between files.
00251   for (const auto& package : matching_flags) {
00252     if (format == HelpFormat::kHumanReadable) {
00253       out << package_separator;
00254       package_separator = "\n\n";
00255     }
00256 
00257     file_separator = "";
00258     for (const auto& flags_in_file : package.second) {
00259       if (format == HelpFormat::kHumanReadable) {
00260         out << file_separator << "  Flags from " << flags_in_file.first
00261             << ":\n";
00262         file_separator = "\n";
00263       }
00264 
00265       for (const auto* flag : flags_in_file.second) {
00266         flags_internal::FlagHelp(out, *flag, format);
00267       }
00268     }
00269   }
00270 
00271   if (format == HelpFormat::kHumanReadable) {
00272     if (filter_cb && matching_flags.empty()) {
00273       out << "  No modules matched: use -helpfull\n";
00274     }
00275   } else {
00276     // The end of the document.
00277     out << "</AllFlags>\n";
00278   }
00279 }
00280 
00281 ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit);
00282 ABSL_CONST_INIT std::string* program_usage_message
00283     GUARDED_BY(usage_message_guard) = nullptr;
00284 
00285 }  // namespace
00286 
00287 // --------------------------------------------------------------------
00288 // Sets the "usage" message to be used by help reporting routines.
00289 
00290 void SetProgramUsageMessage(absl::string_view new_usage_message) {
00291   absl::MutexLock l(&usage_message_guard);
00292 
00293   if (flags_internal::program_usage_message != nullptr) {
00294     ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice.");
00295     std::exit(1);
00296   }
00297 
00298   program_usage_message = new std::string(new_usage_message);
00299 }
00300 
00301 // --------------------------------------------------------------------
00302 // Returns the usage message set by SetProgramUsageMessage().
00303 // Note: We able to return string_view here only because calling
00304 // SetProgramUsageMessage twice is prohibited.
00305 absl::string_view ProgramUsageMessage() {
00306   absl::MutexLock l(&usage_message_guard);
00307 
00308   return program_usage_message != nullptr
00309              ? absl::string_view(*program_usage_message)
00310              : "Warning: SetProgramUsageMessage() never called";
00311 }
00312 
00313 // --------------------------------------------------------------------
00314 // Produces the help message describing specific flag.
00315 void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
00316               HelpFormat format) {
00317   if (format == HelpFormat::kHumanReadable)
00318     flags_internal::FlagHelpHumanReadable(flag, &out);
00319 }
00320 
00321 // --------------------------------------------------------------------
00322 // Produces the help messages for all flags matching the filter.
00323 // If filter is empty produces help messages for all flags.
00324 void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format) {
00325   flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
00326     return filter.empty() || filename.find(filter) != absl::string_view::npos;
00327   };
00328   flags_internal::FlagsHelpImpl(out, filter_cb, format);
00329 }
00330 
00331 // --------------------------------------------------------------------
00332 // Checks all the 'usage' command line flags to see if any have been set.
00333 // If so, handles them appropriately.
00334 int HandleUsageFlags(std::ostream& out) {
00335   if (absl::GetFlag(FLAGS_helpshort)) {
00336     flags_internal::FlagsHelpImpl(
00337         out, flags_internal::GetUsageConfig().contains_helpshort_flags,
00338         HelpFormat::kHumanReadable);
00339     return 1;
00340   }
00341 
00342   if (absl::GetFlag(FLAGS_helpfull)) {
00343     // show all options
00344     flags_internal::FlagsHelp(out);
00345     return 1;
00346   }
00347 
00348   if (!absl::GetFlag(FLAGS_helpon).empty()) {
00349     flags_internal::FlagsHelp(
00350         out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."));
00351     return 1;
00352   }
00353 
00354   if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
00355     flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch));
00356     return 1;
00357   }
00358 
00359   if (absl::GetFlag(FLAGS_help)) {
00360     flags_internal::FlagsHelpImpl(
00361         out, flags_internal::GetUsageConfig().contains_help_flags);
00362 
00363     out << "\nTry --helpfull to get a list of all flags.\n";
00364 
00365     return 1;
00366   }
00367 
00368   if (absl::GetFlag(FLAGS_helppackage)) {
00369     flags_internal::FlagsHelpImpl(
00370         out, flags_internal::GetUsageConfig().contains_helppackage_flags);
00371 
00372     out << "\nTry --helpfull to get a list of all flags.\n";
00373 
00374     return 1;
00375   }
00376 
00377   if (absl::GetFlag(FLAGS_version)) {
00378     if (flags_internal::GetUsageConfig().version_string)
00379       out << flags_internal::GetUsageConfig().version_string();
00380     // Unlike help, we may be asking for version in a script, so return 0
00381     return 0;
00382   }
00383 
00384   if (absl::GetFlag(FLAGS_only_check_args)) {
00385     return 0;
00386   }
00387 
00388   return -1;
00389 }
00390 
00391 }  // namespace flags_internal
00392 }  // namespace absl


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