Commit 81927248 by Gennadiy Rozental Committed by Copybara-Service

Change ParseAbseilFlagsOnly function to include handling of usage flags and exit on syntax errors.

PiperOrigin-RevId: 518198160
Change-Id: Ib4fe53e0b0b371472b6b9473aeb84779953b6a38
parent 396e9764
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/internal/usage.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile); ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
...@@ -40,8 +41,8 @@ enum class OnUndefinedFlag { ...@@ -40,8 +41,8 @@ enum class OnUndefinedFlag {
}; };
std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
UsageFlagsAction usage_flag_act, UsageFlagsAction usage_flag_action,
OnUndefinedFlag on_undef_flag); OnUndefinedFlag undef_flag_action);
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Inspect original command line // Inspect original command line
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <stdint.h> #include <stdint.h>
#include <algorithm> #include <algorithm>
#include <cstdlib>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <map> #include <map>
...@@ -354,8 +355,8 @@ void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format, ...@@ -354,8 +355,8 @@ void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Checks all the 'usage' command line flags to see if any have been set. // Checks all the 'usage' command line flags to see if any have been set.
// If so, handles them appropriately. // If so, handles them appropriately.
int HandleUsageFlags(std::ostream& out, HelpMode HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message) { absl::string_view program_usage_message) {
switch (GetFlagsHelpMode()) { switch (GetFlagsHelpMode()) {
case HelpMode::kNone: case HelpMode::kNone:
break; break;
...@@ -363,25 +364,24 @@ int HandleUsageFlags(std::ostream& out, ...@@ -363,25 +364,24 @@ int HandleUsageFlags(std::ostream& out,
flags_internal::FlagsHelpImpl( flags_internal::FlagsHelpImpl(
out, flags_internal::GetUsageConfig().contains_help_flags, out, flags_internal::GetUsageConfig().contains_help_flags,
GetFlagsHelpFormat(), program_usage_message); GetFlagsHelpFormat(), program_usage_message);
return 1; break;
case HelpMode::kShort: case HelpMode::kShort:
flags_internal::FlagsHelpImpl( flags_internal::FlagsHelpImpl(
out, flags_internal::GetUsageConfig().contains_helpshort_flags, out, flags_internal::GetUsageConfig().contains_helpshort_flags,
GetFlagsHelpFormat(), program_usage_message); GetFlagsHelpFormat(), program_usage_message);
return 1; break;
case HelpMode::kFull: case HelpMode::kFull:
flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(), flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(),
program_usage_message); program_usage_message);
return 1; break;
case HelpMode::kPackage: case HelpMode::kPackage:
flags_internal::FlagsHelpImpl( flags_internal::FlagsHelpImpl(
out, flags_internal::GetUsageConfig().contains_helppackage_flags, out, flags_internal::GetUsageConfig().contains_helppackage_flags,
GetFlagsHelpFormat(), program_usage_message); GetFlagsHelpFormat(), program_usage_message);
break;
return 1;
case HelpMode::kMatch: { case HelpMode::kMatch: {
std::string substr = GetFlagsHelpMatchSubstr(); std::string substr = GetFlagsHelpMatchSubstr();
...@@ -400,20 +400,19 @@ int HandleUsageFlags(std::ostream& out, ...@@ -400,20 +400,19 @@ int HandleUsageFlags(std::ostream& out,
flags_internal::FlagsHelpImpl( flags_internal::FlagsHelpImpl(
out, filter_cb, HelpFormat::kHumanReadable, program_usage_message); out, filter_cb, HelpFormat::kHumanReadable, program_usage_message);
} }
break;
return 1;
} }
case HelpMode::kVersion: case HelpMode::kVersion:
if (flags_internal::GetUsageConfig().version_string) if (flags_internal::GetUsageConfig().version_string)
out << flags_internal::GetUsageConfig().version_string(); out << flags_internal::GetUsageConfig().version_string();
// Unlike help, we may be asking for version in a script, so return 0 // Unlike help, we may be asking for version in a script, so return 0
return 0; break;
case HelpMode::kOnlyCheckArgs: case HelpMode::kOnlyCheckArgs:
return 0; break;
} }
return -1; return GetFlagsHelpMode();
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -521,6 +520,22 @@ bool DeduceUsageFlags(absl::string_view name, absl::string_view value) { ...@@ -521,6 +520,22 @@ bool DeduceUsageFlags(absl::string_view name, absl::string_view value) {
return false; return false;
} }
// --------------------------------------------------------------------
void MaybeExit(HelpMode mode) {
switch (mode) {
case flags_internal::HelpMode::kNone:
return;
case flags_internal::HelpMode::kOnlyCheckArgs:
case flags_internal::HelpMode::kVersion:
std::exit(0);
default: // For all the other modes we exit with 1
std::exit(1);
}
}
// --------------------------------------------------------------------
} // namespace flags_internal } // namespace flags_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
#define ABSL_FLAGS_INTERNAL_USAGE_H_ #define ABSL_FLAGS_INTERNAL_USAGE_H_
#include <iosfwd> #include <iosfwd>
#include <ostream>
#include <string> #include <string>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/commandlineflag.h" #include "absl/flags/commandlineflag.h"
#include "absl/flags/declare.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -36,6 +36,18 @@ enum class HelpFormat { ...@@ -36,6 +36,18 @@ enum class HelpFormat {
kHumanReadable, kHumanReadable,
}; };
// The kind of usage help requested.
enum class HelpMode {
kNone,
kImportant,
kShort,
kFull,
kPackage,
kMatch,
kVersion,
kOnlyCheckArgs
};
// Streams the help message describing `flag` to `out`. // Streams the help message describing `flag` to `out`.
// The default value for `flag` is included in the output. // The default value for `flag` is included in the output.
void FlagHelp(std::ostream& out, const CommandLineFlag& flag, void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
...@@ -57,28 +69,18 @@ void FlagsHelp(std::ostream& out, absl::string_view filter, ...@@ -57,28 +69,18 @@ void FlagsHelp(std::ostream& out, absl::string_view filter,
// If any of the 'usage' related command line flags (listed on the bottom of // If any of the 'usage' related command line flags (listed on the bottom of
// this file) has been set this routine produces corresponding help message in // this file) has been set this routine produces corresponding help message in
// the specified output stream and returns: // the specified output stream and returns HelpMode that was handled. Otherwise
// 0 - if "version" or "only_check_flags" flags were set and handled. // it returns HelpMode::kNone.
// 1 - if some other 'usage' related flag was set and handled. HelpMode HandleUsageFlags(std::ostream& out,
// -1 - if no usage flags were set on a commmand line. absl::string_view program_usage_message);
// Non negative return values are expected to be used as an exit code for a
// binary.
int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message);
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Globals representing usage reporting flags // Encapsulates the logic of exiting the binary depending on handled help mode.
enum class HelpMode { void MaybeExit(HelpMode mode);
kNone,
kImportant, // --------------------------------------------------------------------
kShort, // Globals representing usage reporting flags
kFull,
kPackage,
kMatch,
kVersion,
kOnlyCheckArgs
};
// Returns substring to filter help output (--help=substr argument) // Returns substring to filter help output (--help=substr argument)
std::string GetFlagsHelpMatchSubstr(); std::string GetFlagsHelpMatchSubstr();
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h" #include "absl/flags/internal/program_name.h"
#include "absl/flags/reflection.h" #include "absl/flags/reflection.h"
#include "absl/flags/usage.h" #include "absl/flags/usage.h"
...@@ -256,7 +255,8 @@ path. ...@@ -256,7 +255,8 @@ path.
TEST_F(UsageReportingTest, TestNoUsageFlags) { TEST_F(UsageReportingTest, TestNoUsageFlags) {
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kNone);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -265,7 +265,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { ...@@ -265,7 +265,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
flags::SetFlagsHelpMode(flags::HelpMode::kShort); flags::SetFlagsHelpMode(flags::HelpMode::kShort);
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kShort);
EXPECT_EQ(test_buf.str(), EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
...@@ -298,7 +299,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_simple) { ...@@ -298,7 +299,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_simple) {
flags::SetFlagsHelpMode(flags::HelpMode::kImportant); flags::SetFlagsHelpMode(flags::HelpMode::kImportant);
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kImportant);
EXPECT_EQ(test_buf.str(), EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
...@@ -332,7 +334,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_one_flag) { ...@@ -332,7 +334,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_one_flag) {
flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06"); flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06");
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kMatch);
EXPECT_EQ(test_buf.str(), EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
...@@ -356,7 +359,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) { ...@@ -356,7 +359,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) {
flags::SetFlagsHelpMatchSubstr("test_flag"); flags::SetFlagsHelpMatchSubstr("test_flag");
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kMatch);
EXPECT_EQ(test_buf.str(), EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
...@@ -389,7 +393,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { ...@@ -389,7 +393,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
flags::SetFlagsHelpMode(flags::HelpMode::kPackage); flags::SetFlagsHelpMode(flags::HelpMode::kPackage);
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kPackage);
EXPECT_EQ(test_buf.str(), EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
...@@ -422,7 +427,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) { ...@@ -422,7 +427,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) {
flags::SetFlagsHelpMode(flags::HelpMode::kVersion); flags::SetFlagsHelpMode(flags::HelpMode::kVersion);
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kVersion);
#ifndef NDEBUG #ifndef NDEBUG
EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n"); EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
#else #else
...@@ -436,7 +442,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) { ...@@ -436,7 +442,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs); flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs);
std::stringstream test_buf; std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
flags::HelpMode::kOnlyCheckArgs);
EXPECT_EQ(test_buf.str(), ""); EXPECT_EQ(test_buf.str(), "");
} }
...@@ -447,7 +454,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) { ...@@ -447,7 +454,8 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
flags::SetFlagsHelpMatchSubstr("/bla-bla."); flags::SetFlagsHelpMatchSubstr("/bla-bla.");
std::stringstream test_buf_01; std::stringstream test_buf_01;
EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage),
flags::HelpMode::kMatch);
EXPECT_EQ(test_buf_01.str(), EXPECT_EQ(test_buf_01.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
...@@ -461,7 +469,8 @@ path. ...@@ -461,7 +469,8 @@ path.
flags::SetFlagsHelpMatchSubstr("/usage_test."); flags::SetFlagsHelpMatchSubstr("/usage_test.");
std::stringstream test_buf_02; std::stringstream test_buf_02;
EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1); EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage),
flags::HelpMode::kMatch);
EXPECT_EQ(test_buf_02.str(), EXPECT_EQ(test_buf_02.str(),
R"(usage_test: Custom usage message R"(usage_test: Custom usage message
......
...@@ -666,7 +666,7 @@ std::vector<std::string> GetMisspellingHints(const absl::string_view flag) { ...@@ -666,7 +666,7 @@ std::vector<std::string> GetMisspellingHints(const absl::string_view flag) {
const size_t maxCutoff = std::min(flag.size() / 2 + 1, kMaxDistance); const size_t maxCutoff = std::min(flag.size() / 2 + 1, kMaxDistance);
auto undefok = absl::GetFlag(FLAGS_undefok); auto undefok = absl::GetFlag(FLAGS_undefok);
BestHints best_hints(static_cast<uint8_t>(maxCutoff)); BestHints best_hints(static_cast<uint8_t>(maxCutoff));
absl::flags_internal::ForEachFlag([&](const CommandLineFlag& f) { flags_internal::ForEachFlag([&](const CommandLineFlag& f) {
if (best_hints.hints.size() >= kMaxHints) return; if (best_hints.hints.size() >= kMaxHints) return;
uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance( uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance(
flag, f.Name(), best_hints.best_distance); flag, f.Name(), best_hints.best_distance);
...@@ -697,51 +697,42 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], ...@@ -697,51 +697,42 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
std::vector<char*> positional_args; std::vector<char*> positional_args;
std::vector<UnrecognizedFlag> unrecognized_flags; std::vector<UnrecognizedFlag> unrecognized_flags;
bool parse_successful = absl::ParseAbseilFlagsOnly( auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
argc, argv, positional_args, unrecognized_flags); argc, argv, positional_args, unrecognized_flags, usage_flag_action);
if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) { if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) {
if (parse_successful &&
undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) {
if (!unrecognized_flags.empty()) { parse_successful = false; }
}
flags_internal::ReportUnrecognizedFlags( flags_internal::ReportUnrecognizedFlags(
unrecognized_flags, unrecognized_flags,
!parse_successful && (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined));
(undef_flag_action == OnUndefinedFlag::kAbortIfUndefined));
}
#if ABSL_FLAGS_STRIP_NAMES
if (!parse_successful) {
ReportUsageError("NOTE: command line flags are disabled in this build",
true);
}
#endif
if (!parse_successful) {
HandleUsageFlags(std::cout, ProgramUsageMessage());
std::exit(1);
}
if (usage_flag_action == UsageFlagsAction::kHandleUsage) {
int exit_code = HandleUsageFlags(std::cout, ProgramUsageMessage());
if (exit_code != -1) { if (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) {
std::exit(exit_code); if (!unrecognized_flags.empty()) { std::exit(1); }
} }
} }
flags_internal::MaybeExit(help_mode);
return positional_args; return positional_args;
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
} // namespace flags_internal // This function handles all Abseil Flags and built-in usage flags and, if any
// help mode was handled, it returns that help mode. The caller of this function
bool ParseAbseilFlagsOnly(int argc, char* argv[], // can decide to exit based on the returned help mode.
std::vector<char*>& positional_args, // The caller may decide to handle unrecognized positional arguments and
std::vector<UnrecognizedFlag>& unrecognized_flags) { // unrecognized flags first before exiting.
//
// Returns:
// * HelpMode::kFull if parsing errors were detected in recognized arguments
// * The HelpMode that was handled in case when `usage_flag_action` is
// UsageFlagsAction::kHandleUsage and a usage flag was specified on the
// commandline
// * Otherwise it returns HelpMode::kNone
HelpMode ParseAbseilFlagsOnlyImpl(
int argc, char* argv[], std::vector<char*>& positional_args,
std::vector<UnrecognizedFlag>& unrecognized_flags,
UsageFlagsAction usage_flag_action) {
ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
using flags_internal::ArgsList; using flags_internal::ArgsList;
...@@ -771,9 +762,9 @@ bool ParseAbseilFlagsOnly(int argc, char* argv[], ...@@ -771,9 +762,9 @@ bool ParseAbseilFlagsOnly(int argc, char* argv[],
specified_flags->clear(); specified_flags->clear();
} }
// Iterate through the list of the input arguments. First level are arguments // Iterate through the list of the input arguments. First level are
// originated from argc/argv. Following levels are arguments originated from // arguments originated from argc/argv. Following levels are arguments
// recursive parsing of flagfile(s). // originated from recursive parsing of flagfile(s).
bool success = true; bool success = true;
while (!input_args.empty()) { while (!input_args.empty()) {
// First we process the built-in generator flags. // First we process the built-in generator flags.
...@@ -793,8 +784,8 @@ bool ParseAbseilFlagsOnly(int argc, char* argv[], ...@@ -793,8 +784,8 @@ bool ParseAbseilFlagsOnly(int argc, char* argv[],
} }
// Handle the next argument in the current list. If the stack of argument // Handle the next argument in the current list. If the stack of argument
// lists contains only one element - we are processing an argument from the // lists contains only one element - we are processing an argument from
// original argv. // the original argv.
absl::string_view arg(curr_list.Front()); absl::string_view arg(curr_list.Front());
bool arg_from_argv = input_args.size() == 1; bool arg_from_argv = input_args.size() == 1;
...@@ -895,7 +886,33 @@ bool ParseAbseilFlagsOnly(int argc, char* argv[], ...@@ -895,7 +886,33 @@ bool ParseAbseilFlagsOnly(int argc, char* argv[],
std::swap(unrecognized_flags, filtered); std::swap(unrecognized_flags, filtered);
return success; if (!success) {
#if ABSL_FLAGS_STRIP_NAMES
flags_internal::ReportUsageError(
"NOTE: command line flags are disabled in this build", true);
#else
flags_internal::HandleUsageFlags(std::cout, ProgramUsageMessage());
#endif
return HelpMode::kFull; // We just need to make sure the exit with
// code 1.
}
return usage_flag_action == UsageFlagsAction::kHandleUsage
? flags_internal::HandleUsageFlags(std::cout,
ProgramUsageMessage())
: HelpMode::kNone;
}
} // namespace flags_internal
void ParseAbseilFlagsOnly(int argc, char* argv[],
std::vector<char*>& positional_args,
std::vector<UnrecognizedFlag>& unrecognized_flags) {
auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
argc, argv, positional_args, unrecognized_flags,
flags_internal::UsageFlagsAction::kHandleUsage);
flags_internal::MaybeExit(help_mode);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/strings/string_view.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
...@@ -52,6 +51,15 @@ inline bool operator==(const UnrecognizedFlag& lhs, ...@@ -52,6 +51,15 @@ inline bool operator==(const UnrecognizedFlag& lhs,
return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name; return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name;
} }
namespace flags_internal {
HelpMode ParseAbseilFlagsOnlyImpl(
int argc, char* argv[], std::vector<char*>& positional_args,
std::vector<UnrecognizedFlag>& unrecognized_flags,
UsageFlagsAction usage_flag_action);
} // namespace flags_internal
// ParseAbseilFlagsOnly() // ParseAbseilFlagsOnly()
// //
// Parses a list of command-line arguments, passed in the `argc` and `argv[]` // Parses a list of command-line arguments, passed in the `argc` and `argv[]`
...@@ -69,7 +77,11 @@ inline bool operator==(const UnrecognizedFlag& lhs, ...@@ -69,7 +77,11 @@ inline bool operator==(const UnrecognizedFlag& lhs,
// treated as positional arguments regardless of their syntax. // treated as positional arguments regardless of their syntax.
// //
// All of the deduced Abseil Flag arguments are then parsed into their // All of the deduced Abseil Flag arguments are then parsed into their
// corresponding flag values. // corresponding flag values. If any syntax errors are found in these arguments,
// the binary exits with code 1.
//
// This function also handles Abseil Flags built-in usage flags (e.g. --help)
// if any were present on the command line.
// //
// All the remaining positional arguments including original program name // All the remaining positional arguments including original program name
// (argv[0]) are are returned in the `positional_args` output parameter. // (argv[0]) are are returned in the `positional_args` output parameter.
...@@ -81,10 +93,7 @@ inline bool operator==(const UnrecognizedFlag& lhs, ...@@ -81,10 +93,7 @@ inline bool operator==(const UnrecognizedFlag& lhs,
// that appear within `undefok` will therefore be ignored and not included in // that appear within `undefok` will therefore be ignored and not included in
// the `unrecognized_flag` output parameter. // the `unrecognized_flag` output parameter.
// //
// This function returns true if no syntax errors were found on the command line void ParseAbseilFlagsOnly(int argc, char* argv[],
// or in the referenced flag files. Unrecognized flags do not cause this routine
// to return false.
bool ParseAbseilFlagsOnly(int argc, char* argv[],
std::vector<char*>& positional_args, std::vector<char*>& positional_args,
std::vector<UnrecognizedFlag>& unrecognized_flags); std::vector<UnrecognizedFlag>& unrecognized_flags);
......
...@@ -250,19 +250,31 @@ class ParseTest : public testing::Test { ...@@ -250,19 +250,31 @@ class ParseTest : public testing::Test {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <int N> template <int N>
std::vector<char*> InvokeParse(const char* (&in_argv)[N]) { flags::HelpMode InvokeParseAbslOnlyImpl(const char* (&in_argv)[N]) {
return absl::ParseCommandLine(N, const_cast<char**>(in_argv)); std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags;
return flags::ParseAbseilFlagsOnlyImpl(N, const_cast<char**>(in_argv),
positional_args, unrecognized_flags,
flags::UsageFlagsAction::kHandleUsage);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <int N> template <int N>
bool InvokeParseAbslOnly(const char* (&in_argv)[N]) { void InvokeParseAbslOnly(const char* (&in_argv)[N]) {
std::vector<char*> positional_args; std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags; std::vector<absl::UnrecognizedFlag> unrecognized_flags;
return absl::ParseAbseilFlagsOnly(N, const_cast<char**>(in_argv), absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_argv), positional_args,
positional_args, unrecognized_flags); unrecognized_flags);
}
// --------------------------------------------------------------------
template <int N>
std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
return absl::ParseCommandLine(N, const_cast<char**>(in_argv));
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -871,6 +883,7 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { ...@@ -871,6 +883,7 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
"--help", "--help",
}; };
EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kImportant);
EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), ""); EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), "");
const char* in_args2[] = { const char* in_args2[] = {
...@@ -879,34 +892,55 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { ...@@ -879,34 +892,55 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
"--int_flag=3", "--int_flag=3",
}; };
InvokeParseAbslOnly(in_args2); EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args2), flags::HelpMode::kImportant);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
const char* in_args3[] = {"testbin", "--help", "some_positional_arg"}; const char* in_args3[] = {"testbin", "--help", "some_positional_arg"};
InvokeParseAbslOnly(in_args3); EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args3), flags::HelpMode::kImportant);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) { TEST_F(ParseTest, TestSubstringHelpFlagHandling) {
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "testbin",
"--help=abcd", "--help=abcd",
}; };
InvokeParseAbslOnly(in_args1); EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kMatch);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd"); EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(ParseDeathTest, TestVersionHandling) {
const char* in_args1[] = {
"testbin",
"--version",
};
EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kVersion);
}
// --------------------------------------------------------------------
TEST_F(ParseTest, TestCheckArgsHandling) {
const char* in_args1[] = {"testbin", "--only_check_args", "--int_flag=211"};
EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kOnlyCheckArgs);
EXPECT_EXIT(InvokeParseAbslOnly(in_args1), testing::ExitedWithCode(0), "");
EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(0), "");
const char* in_args2[] = {"testbin", "--only_check_args", "--unknown_flag=a"};
EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args2), flags::HelpMode::kOnlyCheckArgs);
EXPECT_EXIT(InvokeParseAbslOnly(in_args2), testing::ExitedWithCode(0), "");
EXPECT_EXIT(InvokeParse(in_args2), testing::ExitedWithCode(1), "");
}
// --------------------------------------------------------------------
TEST_F(ParseTest, WasPresentOnCommandLine) { TEST_F(ParseTest, WasPresentOnCommandLine) {
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "arg1", "--bool_flag", "testbin", "arg1", "--bool_flag",
...@@ -947,8 +981,8 @@ TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) { ...@@ -947,8 +981,8 @@ TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) {
std::vector<char*> positional_args; std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags; std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_TRUE(absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args), absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args), positional_args,
positional_args, unrecognized_flags)); unrecognized_flags);
EXPECT_THAT(positional_args, EXPECT_THAT(positional_args,
ElementsAreArray( ElementsAreArray(
{absl::string_view("testbin"), absl::string_view("arg1"), {absl::string_view("testbin"), absl::string_view("arg1"),
...@@ -964,17 +998,15 @@ TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) { ...@@ -964,17 +998,15 @@ TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(ParseTest, ParseAbseilFlagsOnlyFailure) { TEST_F(ParseDeathTest, ParseAbseilFlagsOnlyFailure) {
const char* in_args[] = { const char* in_args[] = {
"testbin", "testbin",
"--int_flag=21.1", "--int_flag=21.1",
}; };
std::vector<char*> positional_args; EXPECT_DEATH_IF_SUPPORTED(
std::vector<absl::UnrecognizedFlag> unrecognized_flags; InvokeParseAbslOnly(in_args),
"Illegal value '21.1' specified for flag 'int_flag'");
EXPECT_FALSE(absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_args),
positional_args, unrecognized_flags));
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -989,8 +1021,8 @@ TEST_F(ParseTest, UndefOkFlagsAreIgnored) { ...@@ -989,8 +1021,8 @@ TEST_F(ParseTest, UndefOkFlagsAreIgnored) {
std::vector<char*> positional_args; std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags; std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_TRUE(absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args), absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args), positional_args,
positional_args, unrecognized_flags)); unrecognized_flags);
EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"), EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"),
absl::string_view("value")})); absl::string_view("value")}));
EXPECT_THAT(unrecognized_flags, EXPECT_THAT(unrecognized_flags,
...@@ -1018,8 +1050,8 @@ TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) { ...@@ -1018,8 +1050,8 @@ TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) {
std::vector<char*> positional_args; std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags; std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_TRUE(absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args), absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args), positional_args,
positional_args, unrecognized_flags)); unrecognized_flags);
EXPECT_THAT(positional_args, EXPECT_THAT(positional_args,
ElementsAreArray({absl::string_view("testbin"), ElementsAreArray({absl::string_view("testbin"),
absl::string_view("value"), absl::string_view("value"),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment