Bazel client: Refactor startup options parsing to gracefully handle user errors, rather than crash.
blaze::OptionProcessor::SplitCommandLine is a nice standalone helper function, and so it's undesirable for blaze::StartupOptions::IsNullary to intentionally crash the host program via BAZEL_DIE.
Even ignoring that, option_processor.h's comment for SplitCommandLine says "... If the method fails then error will contain the cause..." and so transitive BAZEL_DIE usage makes this comment technically incorrect. Therefore, this change here can be motivated by a desire to make that comment correct.
RELNOTES: None
PiperOrigin-RevId: 300896347
diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
index 78df306..e44af24 100644
--- a/src/main/cpp/option_processor.cc
+++ b/src/main/cpp/option_processor.cc
@@ -88,10 +88,17 @@
vector<string>::size_type i = 1;
while (i < args.size() && IsArg(args[i])) {
string& current_arg = args[i];
+
+ bool is_nullary;
+ if (!startup_options_->MaybeCheckValidNullary(current_arg, &is_nullary,
+ error)) {
+ return nullptr;
+ }
+
// If the current argument is a valid nullary startup option such as
// --master_bazelrc or --nomaster_bazelrc proceed to examine the next
// argument.
- if (startup_options_->IsNullary(current_arg)) {
+ if (is_nullary) {
startup_args.push_back(current_arg);
i++;
} else if (startup_options_->IsUnary(current_arg)) {
diff --git a/src/main/cpp/startup_options.cc b/src/main/cpp/startup_options.cc
index bf93cdd..6bd300d 100644
--- a/src/main/cpp/startup_options.cc
+++ b/src/main/cpp/startup_options.cc
@@ -182,21 +182,24 @@
}
}
-bool StartupOptions::IsNullary(const string &arg) const {
+bool StartupOptions::MaybeCheckValidNullary(const string &arg, bool *result,
+ std::string *error) const {
std::string::size_type i = arg.find_first_of('=');
if (i == std::string::npos) {
- return all_nullary_startup_flags_.find(arg) !=
- all_nullary_startup_flags_.end();
- } else {
- std::string f = arg.substr(0, i);
- if (all_nullary_startup_flags_.find(f) !=
- all_nullary_startup_flags_.end()) {
- BAZEL_DIE(blaze_exit_code::BAD_ARGV)
- << "In argument '" << arg << "': option '" << f
- << "' does not take a value.";
- }
- return false;
+ *result = all_nullary_startup_flags_.find(arg) !=
+ all_nullary_startup_flags_.end();
+ return true;
}
+ std::string f = arg.substr(0, i);
+ if (all_nullary_startup_flags_.find(f) == all_nullary_startup_flags_.end()) {
+ *result = false;
+ return true;
+ }
+
+ blaze_util::StringPrintf(
+ error, "In argument '%s': option '%s' does not take a value.",
+ arg.c_str(), f.c_str());
+ return false;
}
void StartupOptions::AddExtraOptions(vector<string> *result) const {
@@ -217,7 +220,13 @@
const char* next_arg = next_argstr.empty() ? NULL : next_argstr.c_str();
const char* value = NULL;
- if (IsNullary(argstr)) {
+ bool is_nullary;
+ if (!MaybeCheckValidNullary(argstr, &is_nullary, error)) {
+ *is_space_separated = false;
+ return blaze_exit_code::BAD_ARGV;
+ }
+
+ if (is_nullary) {
// 'enabled' is true if 'argstr' is "--foo", and false if it's "--nofoo".
bool enabled = (argstr.compare(0, 4, "--no") != 0);
if (no_rc_nullary_startup_flags_.find(argstr) !=
diff --git a/src/main/cpp/startup_options.h b/src/main/cpp/startup_options.h
index adabe74..efe99e8 100644
--- a/src/main/cpp/startup_options.h
+++ b/src/main/cpp/startup_options.h
@@ -106,9 +106,21 @@
const blaze_util::Path &server_javabase, std::vector<std::string> *result,
const std::vector<std::string> &user_options, std::string *error) const;
- // Checks whether the argument is a valid nullary option.
- // E.g. --master_bazelrc, --nomaster_bazelrc.
- bool IsNullary(const std::string& arg) const;
+ // Checks whether "arg" is a valid nullary option (e.g. "--master_bazelrc" or
+ // "--nomaster_bazelrc").
+ //
+ // Returns true, if "arg" looks like either a valid nullary option or a
+ // potentially valid unary option. In this case, "result" will be populated
+ // with true iff "arg" is definitely a valid nullary option.
+ //
+ // Returns false, if "arg" looks like an attempt to pass a value to nullary
+ // option (e.g. "--nullary_option=idontknowwhatimdoing"). In this case,
+ // "error" will be populated with a user-friendly error message.
+ //
+ // Therefore, callers of this function should look at the return value and
+ // then either look at "result" (on true) or "error" (on false).
+ bool MaybeCheckValidNullary(const std::string &arg, bool *result,
+ std::string *error) const;
// Checks whether the argument is a valid unary option.
// E.g. --blazerc=foo, --blazerc foo.
diff --git a/src/test/cpp/bazel_startup_options_test.cc b/src/test/cpp/bazel_startup_options_test.cc
index 2a07b80..5fc432a 100644
--- a/src/test/cpp/bazel_startup_options_test.cc
+++ b/src/test/cpp/bazel_startup_options_test.cc
@@ -66,8 +66,21 @@
}
TEST_F(BazelStartupOptionsTest, EmptyFlagsAreInvalid) {
- EXPECT_FALSE(startup_options_->IsNullary(""));
- EXPECT_FALSE(startup_options_->IsNullary("--"));
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(startup_options_->MaybeCheckValidNullary("", &result, &error));
+ EXPECT_FALSE(result);
+ }
+
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ startup_options_->MaybeCheckValidNullary("--", &result, &error));
+ EXPECT_FALSE(result);
+ }
+
EXPECT_FALSE(startup_options_->IsUnary(""));
EXPECT_FALSE(startup_options_->IsUnary("--"));
}
@@ -80,23 +93,23 @@
// member that knows the Google-internal procedure for adding/deprecating
// startup flags.
const StartupOptions* options = startup_options_.get();
- ExpectIsNullaryOption(options, "batch");
- ExpectIsNullaryOption(options, "batch_cpu_scheduling");
- ExpectIsNullaryOption(options, "block_for_lock");
- ExpectIsNullaryOption(options, "client_debug");
- ExpectIsNullaryOption(options, "deep_execroot");
- ExpectIsNullaryOption(options, "experimental_oom_more_eagerly");
- ExpectIsNullaryOption(options, "fatal_event_bus_exceptions");
- ExpectIsNullaryOption(options, "home_rc");
- ExpectIsNullaryOption(options, "host_jvm_debug");
- ExpectIsNullaryOption(options, "ignore_all_rc_files");
- ExpectIsNullaryOption(options, "incompatible_enable_execution_transition");
- ExpectIsNullaryOption(options, "master_bazelrc");
- ExpectIsNullaryOption(options, "shutdown_on_low_sys_mem");
- ExpectIsNullaryOption(options, "system_rc");
- ExpectIsNullaryOption(options, "watchfs");
- ExpectIsNullaryOption(options, "workspace_rc");
- ExpectIsNullaryOption(options, "write_command_log");
+ ExpectValidNullaryOption(options, "batch");
+ ExpectValidNullaryOption(options, "batch_cpu_scheduling");
+ ExpectValidNullaryOption(options, "block_for_lock");
+ ExpectValidNullaryOption(options, "client_debug");
+ ExpectValidNullaryOption(options, "deep_execroot");
+ ExpectValidNullaryOption(options, "experimental_oom_more_eagerly");
+ ExpectValidNullaryOption(options, "fatal_event_bus_exceptions");
+ ExpectValidNullaryOption(options, "home_rc");
+ ExpectValidNullaryOption(options, "host_jvm_debug");
+ ExpectValidNullaryOption(options, "ignore_all_rc_files");
+ ExpectValidNullaryOption(options, "incompatible_enable_execution_transition");
+ ExpectValidNullaryOption(options, "master_bazelrc");
+ ExpectValidNullaryOption(options, "shutdown_on_low_sys_mem");
+ ExpectValidNullaryOption(options, "system_rc");
+ ExpectValidNullaryOption(options, "watchfs");
+ ExpectValidNullaryOption(options, "workspace_rc");
+ ExpectValidNullaryOption(options, "write_command_log");
ExpectIsUnaryOption(options, "bazelrc");
ExpectIsUnaryOption(options, "command_port");
ExpectIsUnaryOption(options, "connect_timeout_secs");
@@ -115,9 +128,24 @@
}
TEST_F(BazelStartupOptionsTest, BlazercFlagsAreNotAccepted) {
- EXPECT_FALSE(startup_options_->IsNullary("--master_blazerc"));
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(startup_options_->MaybeCheckValidNullary("--master_blazerc",
+ &result, &error));
+ EXPECT_FALSE(result);
+ }
+
EXPECT_FALSE(startup_options_->IsUnary("--master_blazerc"));
- EXPECT_FALSE(startup_options_->IsNullary("--blazerc"));
+
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ startup_options_->MaybeCheckValidNullary("--blazerc", &result, &error));
+ EXPECT_FALSE(result);
+ }
+
EXPECT_FALSE(startup_options_->IsUnary("--blazerc"));
}
diff --git a/src/test/cpp/startup_options_test.cc b/src/test/cpp/startup_options_test.cc
index 1968fda..a5bec67 100644
--- a/src/test/cpp/startup_options_test.cc
+++ b/src/test/cpp/startup_options_test.cc
@@ -84,7 +84,7 @@
TEST_F(StartupOptionsTest, JavaLoggingOptions) {
ASSERT_EQ("com.google.devtools.build.lib.util.SingleLineFormatter",
- startup_options_->java_logging_formatter);
+ startup_options_->java_logging_formatter);
}
// TODO(bazel-team): remove the ifdef guard once the implementation of
@@ -108,8 +108,21 @@
#endif // __linux
TEST_F(StartupOptionsTest, EmptyFlagsAreInvalidTest) {
- EXPECT_FALSE(startup_options_->IsNullary(""));
- EXPECT_FALSE(startup_options_->IsNullary("--"));
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(startup_options_->MaybeCheckValidNullary("", &result, &error));
+ EXPECT_FALSE(result);
+ }
+
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ startup_options_->MaybeCheckValidNullary("--", &result, &error));
+ EXPECT_FALSE(result);
+ }
+
EXPECT_FALSE(startup_options_->IsUnary(""));
EXPECT_FALSE(startup_options_->IsUnary("--"));
}
diff --git a/src/test/cpp/test_util.cc b/src/test/cpp/test_util.cc
index e39c96c..6015d88 100644
--- a/src/test/cpp/test_util.cc
+++ b/src/test/cpp/test_util.cc
@@ -22,22 +22,51 @@
// but also that they are parsed. StartupOptions* options would need to be
// non-const to call ProcessArgs and test that the value is recognized by the
// command line.
-void ExpectIsNullaryOption(const StartupOptions* options,
- const std::string& flag_name) {
- EXPECT_TRUE(options->IsNullary("--" + flag_name));
- EXPECT_TRUE(options->IsNullary("--no" + flag_name));
+void ExpectValidNullaryOption(const StartupOptions* options,
+ const std::string& flag_name) {
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ options->MaybeCheckValidNullary("--" + flag_name, &result, &error));
+ EXPECT_TRUE(result);
+ }
- EXPECT_FALSE(options->IsNullary("--" + flag_name + "__invalid"));
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ options->MaybeCheckValidNullary("--no" + flag_name, &result, &error));
+ EXPECT_TRUE(result);
+ }
- EXPECT_DEATH(options->IsNullary("--" + flag_name + "=foo"),
- ("In argument '--" + flag_name + "=foo': option '--" +
- flag_name + "' does not take a value")
- .c_str());
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(options->MaybeCheckValidNullary("--" + flag_name + "__invalid",
+ &result, &error));
+ EXPECT_FALSE(result);
+ }
- EXPECT_DEATH(options->IsNullary("--no" + flag_name + "=foo"),
- ("In argument '--no" + flag_name + "=foo': option '--no" +
- flag_name + "' does not take a value")
- .c_str());
+ {
+ bool result;
+ std::string error;
+ EXPECT_FALSE(options->MaybeCheckValidNullary("--" + flag_name + "=foo",
+ &result, &error));
+ EXPECT_EQ("In argument '--" + flag_name + "=foo': option '--" + flag_name +
+ "' does not take a value.",
+ error);
+ }
+
+ {
+ bool result;
+ std::string error;
+ EXPECT_FALSE(options->MaybeCheckValidNullary("--no" + flag_name + "=foo",
+ &result, &error));
+ EXPECT_EQ("In argument '--no" + flag_name + "=foo': option '--no" +
+ flag_name + "' does not take a value.",
+ error);
+ }
EXPECT_FALSE(options->IsUnary("--" + flag_name));
EXPECT_FALSE(options->IsUnary("--no" + flag_name));
@@ -50,8 +79,22 @@
EXPECT_TRUE(options->IsUnary("--" + flag_name + "=foo"));
EXPECT_FALSE(options->IsUnary("--" + flag_name + "__invalid"));
- EXPECT_FALSE(options->IsNullary("--" + flag_name));
- EXPECT_FALSE(options->IsNullary("--no" + flag_name));
+
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ options->MaybeCheckValidNullary("--" + flag_name, &result, &error));
+ EXPECT_FALSE(result);
+ }
+
+ {
+ bool result;
+ std::string error;
+ EXPECT_TRUE(
+ options->MaybeCheckValidNullary("--no" + flag_name, &result, &error));
+ EXPECT_FALSE(result);
+ }
}
void ParseStartupOptionsAndExpectWarning(
diff --git a/src/test/cpp/test_util.h b/src/test/cpp/test_util.h
index 6c23c2e..cdbfcf2 100644
--- a/src/test/cpp/test_util.h
+++ b/src/test/cpp/test_util.h
@@ -19,8 +19,8 @@
namespace blaze {
-void ExpectIsNullaryOption(const StartupOptions* options,
- const std::string& flag_name);
+void ExpectValidNullaryOption(const StartupOptions* options,
+ const std::string& flag_name);
void ExpectIsUnaryOption(const StartupOptions* options,
const std::string& flag_name);
void ParseStartupOptionsAndExpectWarning(