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 @@
#ifndef ABSL_FLAGS_PARSE_H_
#define ABSL_FLAGS_PARSE_H_
#include <string>
#include <vector>
#include "absl/base/config.h"
#include "absl/flags/internal/parse.h"
#include "absl/strings/string_view.h"
namespace absl {
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()
//
// Parses the set of command-line arguments passed in the `argc` (argument
// count) and `argv[]` (argument vector) parameters from `main()`, assigning
// values to any defined Abseil flags. (Any arguments passed after the
// flag-terminating delimiter (`--`) are treated as positional arguments and
// ignored.)
//
// 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
// return an error, or be ignored if that flag is designated using `undefok` to
// indicate "undefined is OK."
//
// Any command-line positional arguments not part of any command-line flag (or
// arguments to a flag) are returned in a vector, with the program invocation
// name at position 0 of that vector. (Note that this includes positional
// arguments after the flag-terminating delimiter `--`.)
//
// 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.
// First parses Abseil Flags only from the command line according to the
// description in `ParseAbseilFlagsOnly`. In addition this function handles
// unrecognized and usage flags.
//
// If any unrecognized flags are located they are reported using
// `ReportUnrecognizedFlags`.
//
// If any errors detected during command line parsing, this routine reports a
// usage message and aborts the program.
//
// If any built-in usage flags were specified on the command line (e.g.
// `--help`), this function reports help messages and then gracefully exits the
// program.
//
// This function returns all the remaining positional arguments collected by
// `ParseAbseilFlagsOnly`.
std::vector<char*> ParseCommandLine(int argc, char* argv[]);
ABSL_NAMESPACE_END
......
......@@ -20,13 +20,13 @@
#include <cstddef>
#include <fstream>
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scoped_set_env.h"
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/usage.h"
......@@ -199,7 +199,7 @@ constexpr const char* const ff2_data[] = {
// Builds flagfile flag in the flagfile_flag buffer and returns it. This
// function also creates a temporary flagfile based on FlagfileData input.
// 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
// temporary directory location. This way we can test inclusion of one flagfile
// from another flagfile.
......@@ -257,6 +257,17 @@ std::vector<char*> InvokeParse(const char* (&in_argv)[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,
double double_flag_val, absl::string_view string_flag_val,
bool bool_flag_val, int exp_position_args = 0) {
......@@ -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) {
const char* in_args1[] = {
"testbin",
......@@ -888,12 +879,16 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
"--int_flag=3",
};
auto out_args2 = flags::ParseCommandLineImpl(
3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
flags::OnUndefinedFlag::kAbortIfUndefined);
InvokeParseAbslOnly(in_args2);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
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) {
"--help=abcd",
};
auto out_args1 = flags::ParseCommandLineImpl(
2, const_cast<char**>(in_args1), flags::UsageFlagsAction::kIgnoreUsage,
flags::OnUndefinedFlag::kAbortIfUndefined);
InvokeParseAbslOnly(in_args1);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
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) {
// --------------------------------------------------------------------
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
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