Commit 297efcc9 by Gennadiy Rozental Committed by Copybara-Service

Introduce two new public APIs for parsing command line flags.

ParseAbseilFlagsOnly
To be used to parse Abseil Flags only and classify the rest of the arguments.

ReportUnrecognizedFlags
To be used to report unrecognized flags.

PiperOrigin-RevId: 515082682
Change-Id: I73271b56cc512a5e78b5fcd035564b3672d62ca8
parent b75a3ff4
...@@ -23,35 +23,103 @@ ...@@ -23,35 +23,103 @@
#ifndef ABSL_FLAGS_PARSE_H_ #ifndef ABSL_FLAGS_PARSE_H_
#define ABSL_FLAGS_PARSE_H_ #define ABSL_FLAGS_PARSE_H_
#include <string>
#include <vector> #include <vector>
#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
// This type represent information about an unrecognized flag in the command
// line.
struct UnrecognizedFlag {
enum Source { kFromArgv, kFromFlagfile };
explicit UnrecognizedFlag(Source s, absl::string_view f)
: source(s), flag_name(f) {}
// This field indicates where we found this flag: on the original command line
// or read in some flag file.
Source source;
// Name of the flag we did not recognize in --flag_name=value or --flag_name.
std::string flag_name;
};
inline bool operator==(const UnrecognizedFlag& lhs,
const UnrecognizedFlag& rhs) {
return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name;
}
// ParseAbseilFlagsOnly()
//
// Parses a list of command-line arguments, passed in the `argc` and `argv[]`
// parameters, into a set of Abseil Flag values, returning any unparsed
// arguments in `positional_args` and `unrecognized_flags` output parameters.
//
// This function classifies all the arguments (including content of the
// flagfiles, if any) into one of the following groups:
//
// * arguments specified as "--flag=value" or "--flag value" that match
// registered or built-in Abseil Flags. These are "Abseil Flag arguments."
// * arguments specified as "--flag" that are unrecognized as Abseil Flags
// * arguments that are not specified as "--flag" are positional arguments
// * arguments that follow the flag-terminating delimiter (`--`) are also
// treated as positional arguments regardless of their syntax.
//
// All of the deduced Abseil Flag arguments are then parsed into their
// corresponding flag values.
//
// All the remaining positional arguments including original program name
// (argv[0]) are are returned in the `positional_args` output parameter.
//
// All unrecognized flags that are not otherwise ignored are returned in the
// `unrecognized_flags` output parameter. Note that the special `undefok`
// flag allows you to specify flags which can be safely ignored; `undefok`
// specifies these flags as a comma-separated list. Any unrecognized flags
// that appear within `undefok` will therefore be ignored and not included in
// the `unrecognized_flag` output parameter.
//
// This function returns true if no syntax errors were found on the command line
// 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<UnrecognizedFlag>& unrecognized_flags);
// ReportUnrecognizedFlags()
//
// Reports an error for all non-ignored unrecognized flags in the provided
// `unrecognized_flags` list.
//
// If `report_fatal_error` is true, the fatal error is reported and program is
// aborted. Otherwise non-fatal error is reported for all flags.
//
// This function returns true if any non-ignored unrecognized flags were
// located in the list and false otherwise.
void ReportUnrecognizedFlags(
const std::vector<UnrecognizedFlag>& unrecognized_flags,
bool report_fatal_error);
// ParseCommandLine() // ParseCommandLine()
// //
// Parses the set of command-line arguments passed in the `argc` (argument // First parses Abseil Flags only from the command line according to the
// count) and `argv[]` (argument vector) parameters from `main()`, assigning // description in `ParseAbseilFlagsOnly`. In addition this function handles
// values to any defined Abseil flags. (Any arguments passed after the // unrecognized and usage flags.
// flag-terminating delimiter (`--`) are treated as positional arguments and //
// ignored.) // If any unrecognized flags are located they are reported using
// // `ReportUnrecognizedFlags`.
// Any command-line flags (and arguments to those flags) are parsed into Abseil //
// Flag values, if those flags are defined. Any undefined flags will either // If any errors detected during command line parsing, this routine reports a
// return an error, or be ignored if that flag is designated using `undefok` to // usage message and aborts the program.
// indicate "undefined is OK." //
// // If any built-in usage flags were specified on the command line (e.g.
// Any command-line positional arguments not part of any command-line flag (or // `--help`), this function reports help messages and then gracefully exits the
// arguments to a flag) are returned in a vector, with the program invocation // program.
// name at position 0 of that vector. (Note that this includes positional //
// arguments after the flag-terminating delimiter `--`.) // This function returns all the remaining positional arguments collected by
// // `ParseAbseilFlagsOnly`.
// After all flags and flag arguments are parsed, this function looks for any
// built-in usage flags (e.g. `--help`), and if any were specified, it reports
// help messages and then exits the program.
std::vector<char*> ParseCommandLine(int argc, char* argv[]); std::vector<char*> ParseCommandLine(int argc, char* argv[]);
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
......
...@@ -20,13 +20,13 @@ ...@@ -20,13 +20,13 @@
#include <cstddef> #include <cstddef>
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scoped_set_env.h" #include "absl/base/internal/scoped_set_env.h"
#include "absl/flags/declare.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/usage.h" #include "absl/flags/internal/usage.h"
...@@ -199,7 +199,7 @@ constexpr const char* const ff2_data[] = { ...@@ -199,7 +199,7 @@ constexpr const char* const ff2_data[] = {
// Builds flagfile flag in the flagfile_flag buffer and returns it. This // Builds flagfile flag in the flagfile_flag buffer and returns it. This
// function also creates a temporary flagfile based on FlagfileData input. // function also creates a temporary flagfile based on FlagfileData input.
// We create a flagfile in a temporary directory with the name specified in // We create a flagfile in a temporary directory with the name specified in
// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is // FlagfileData and populate it with lines specified in FlagfileData. If $0 is
// referenced in any of the lines in FlagfileData they are replaced with // referenced in any of the lines in FlagfileData they are replaced with
// temporary directory location. This way we can test inclusion of one flagfile // temporary directory location. This way we can test inclusion of one flagfile
// from another flagfile. // from another flagfile.
...@@ -257,6 +257,17 @@ std::vector<char*> InvokeParse(const char* (&in_argv)[N]) { ...@@ -257,6 +257,17 @@ std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <int N> template <int N>
bool InvokeParseAbslOnly(const char* (&in_argv)[N]) {
std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags;
return absl::ParseAbseilFlagsOnly(N, const_cast<char**>(in_argv),
positional_args, unrecognized_flags);
}
// --------------------------------------------------------------------
template <int N>
void TestParse(const char* (&in_argv)[N], int int_flag_value, void TestParse(const char* (&in_argv)[N], int int_flag_value,
double double_flag_val, absl::string_view string_flag_val, double double_flag_val, absl::string_view string_flag_val,
bool bool_flag_val, int exp_position_args = 0) { bool bool_flag_val, int exp_position_args = 0) {
...@@ -854,26 +865,6 @@ TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) { ...@@ -854,26 +865,6 @@ TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
const char* in_args1[] = {
"testbin",
"arg1",
"--undef_flag=aa",
"--int_flag=21",
};
auto out_args1 = flags::ParseCommandLineImpl(
4, const_cast<char**>(in_args1), flags::UsageFlagsAction::kHandleUsage,
flags::OnUndefinedFlag::kIgnoreUndefined);
EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
absl::string_view("arg1")}));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
}
// --------------------------------------------------------------------
TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "testbin",
...@@ -888,12 +879,16 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { ...@@ -888,12 +879,16 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
"--int_flag=3", "--int_flag=3",
}; };
auto out_args2 = flags::ParseCommandLineImpl( InvokeParseAbslOnly(in_args2);
3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
flags::OnUndefinedFlag::kAbortIfUndefined);
EXPECT_EQ(flags::GetFlagsHelpMode(), 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"};
InvokeParseAbslOnly(in_args3);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -904,20 +899,10 @@ TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) { ...@@ -904,20 +899,10 @@ TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
"--help=abcd", "--help=abcd",
}; };
auto out_args1 = flags::ParseCommandLineImpl( InvokeParseAbslOnly(in_args1);
2, const_cast<char**>(in_args1), flags::UsageFlagsAction::kIgnoreUsage,
flags::OnUndefinedFlag::kAbortIfUndefined);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch); EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd"); EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
auto out_args2 = flags::ParseCommandLineImpl(
3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
flags::OnUndefinedFlag::kAbortIfUndefined);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -942,4 +927,106 @@ TEST_F(ParseTest, WasPresentOnCommandLine) { ...@@ -942,4 +927,106 @@ TEST_F(ParseTest, WasPresentOnCommandLine) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) {
const char* in_args[] = {
"testbin",
"arg1",
"--bool_flag",
"--int_flag=211",
"arg2",
"--double_flag=1.1",
"--undef_flag1",
"--undef_flag2=123",
"--string_flag",
"asd",
"--",
"--some_flag",
"arg4",
};
std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_TRUE(absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args),
positional_args, unrecognized_flags));
EXPECT_THAT(positional_args,
ElementsAreArray(
{absl::string_view("testbin"), absl::string_view("arg1"),
absl::string_view("arg2"), absl::string_view("--some_flag"),
absl::string_view("arg4")}));
EXPECT_THAT(unrecognized_flags,
ElementsAreArray(
{absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
"undef_flag1"),
absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
"undef_flag2")}));
}
// --------------------------------------------------------------------
TEST_F(ParseTest, ParseAbseilFlagsOnlyFailure) {
const char* in_args[] = {
"testbin",
"--int_flag=21.1",
};
std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_FALSE(absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_args),
positional_args, unrecognized_flags));
}
// --------------------------------------------------------------------
TEST_F(ParseTest, UndefOkFlagsAreIgnored) {
const char* in_args[] = {
"testbin", "--undef_flag1",
"--undef_flag2=123", "--undefok=undef_flag2",
"--undef_flag3", "value",
};
std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_TRUE(absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args),
positional_args, unrecognized_flags));
EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"),
absl::string_view("value")}));
EXPECT_THAT(unrecognized_flags,
ElementsAreArray(
{absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
"undef_flag1"),
absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
"undef_flag3")}));
}
// --------------------------------------------------------------------
TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) {
const char* in_args[] = {
"testbin",
"--undef_flag1",
"--undef_flag2=123",
"--undefok=undef_flag2,undef_flag1,undef_flag3",
"--undef_flag3",
"value",
"--",
"--undef_flag4",
};
std::vector<char*> positional_args;
std::vector<absl::UnrecognizedFlag> unrecognized_flags;
EXPECT_TRUE(absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args),
positional_args, unrecognized_flags));
EXPECT_THAT(positional_args,
ElementsAreArray({absl::string_view("testbin"),
absl::string_view("value"),
absl::string_view("--undef_flag4")}));
EXPECT_THAT(unrecognized_flags, testing::IsEmpty());
}
// --------------------------------------------------------------------
} // namespace } // namespace
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