Provide a SplitCommandLine method in the option processor
that takes a given command line args and splits it into the corresponding
{binary, startup_args, command, command_args}.
The purpose of this function is to help split the responsibilities
of the ParseOptions function by processing the startup options independently
(i.e. rc files detection and processing) from the command options.
This will be combined with ParseOptions in a subsequent CL.
--
MOS_MIGRATED_REVID=139773786
diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc
index 6eebf72..b7aa84a 100644
--- a/src/main/cpp/blaze_util.cc
+++ b/src/main/cpp/blaze_util.cc
@@ -445,4 +445,9 @@
close(blaze_lock->lockfd);
}
+bool IsArg(const string& arg) {
+ return blaze_util::starts_with(arg, "-") && (arg != "--help")
+ && (arg != "-help") && (arg != "-h");
+}
+
} // namespace blaze
diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h
index eee40f9..666b06e 100644
--- a/src/main/cpp/blaze_util.h
+++ b/src/main/cpp/blaze_util.h
@@ -115,6 +115,9 @@
bool CheckJavaVersionIsAtLeast(const std::string &jvm_version,
const std::string &version_spec);
+// Returns true iff arg is a valid command line argument for bazel.
+bool IsArg(const std::string& arg);
+
// Converts a project identifier to string.
// Workaround for mingw where std::to_string is not implemented.
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52015.
diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
index 29f59cf..6be8823 100644
--- a/src/main/cpp/option_processor.cc
+++ b/src/main/cpp/option_processor.cc
@@ -167,6 +167,85 @@
parsed_startup_options_(std::move(default_startup_options)) {
}
+std::unique_ptr<CommandLine> OptionProcessor::SplitCommandLine(
+ const vector<string>& args,
+ string* error) {
+ const string lowercase_product_name =
+ parsed_startup_options_->GetLowercaseProductName();
+
+ if (args.empty()) {
+ blaze_util::StringPrintf(error,
+ "Unable to split command line, args is empty");
+ return nullptr;
+ }
+
+ const string path_to_binary(args[0]);
+
+ // Process the startup options.
+ vector<string> startup_args;
+ vector<string>::size_type i = 1;
+ while (i < args.size() && IsArg(args[i])) {
+ const string current_arg(args[i]);
+ // If the current argument is a valid nullary startup option such as
+ // --master_bazelrc or --nomaster_bazelrc proceed to examine the next
+ // argument.
+ if (parsed_startup_options_->IsNullary(current_arg)) {
+ startup_args.push_back(current_arg);
+ i++;
+ } else if (parsed_startup_options_->IsUnary(current_arg)) {
+ // If the current argument is a valid unary startup option such as
+ // --bazelrc there are two cases to consider.
+
+ // The option is of the form '--bazelrc=value', hence proceed to
+ // examine the next argument.
+ if (current_arg.find("=") != string::npos) {
+ startup_args.push_back(current_arg);
+ i++;
+ } else {
+ // Otherwise, the option is of the form '--bazelrc value', hence
+ // skip the next argument and proceed to examine the argument located
+ // two places after.
+ if (i + 1 >= args.size()) {
+ blaze_util::StringPrintf(
+ error,
+ "Startup option '%s' expects a value.\n"
+ "Usage: '%s=somevalue' or '%s somevalue'.\n"
+ " For more info, run '%s help startup_options'.",
+ current_arg.c_str(), current_arg.c_str(), current_arg.c_str(),
+ lowercase_product_name.c_str());
+ return nullptr;
+ }
+ // In this case we transform it to the form '--bazelrc=value'.
+ startup_args.push_back(current_arg + string("=") + args[i + 1]);
+ i += 2;
+ }
+ } else {
+ // If the current argument is not a valid unary or nullary startup option
+ // then fail.
+ blaze_util::StringPrintf(
+ error,
+ "Unknown %s startup option: '%s'.\n"
+ " For more info, run '%s help startup_options'.",
+ lowercase_product_name.c_str(), current_arg.c_str(),
+ lowercase_product_name.c_str());
+ return nullptr;
+ }
+ }
+
+ // The command is the arg right after the startup options.
+ if (i == args.size()) {
+ return std::unique_ptr<CommandLine>(
+ new CommandLine(path_to_binary, startup_args, "", {}));
+ }
+ const string command(args[i]);
+
+ // The rest are the command arguments.
+ const vector<string> command_args(args.begin() + i + 1, args.end());
+
+ return std::unique_ptr<CommandLine>(
+ new CommandLine(path_to_binary, startup_args, command, command_args));
+}
+
// Return the path to the user's rc file. If cmdLineRcFile != NULL,
// use it, dying if it is not readable. Otherwise, return the first
// readable file called rc_basename from [workspace, $HOME]
@@ -318,11 +397,6 @@
return ParseOptions(args, workspace, cwd, error);
}
-static bool IsArg(const string& arg) {
- return blaze_util::starts_with(arg, "-") && (arg != "--help")
- && (arg != "-help") && (arg != "-h");
-}
-
blaze_exit_code::ExitCode OptionProcessor::ParseStartupOptions(string *error) {
// Process rcfile startup options
map< string, vector<RcOption> >::const_iterator it =
diff --git a/src/main/cpp/option_processor.h b/src/main/cpp/option_processor.h
index 472414d..1281e77 100644
--- a/src/main/cpp/option_processor.h
+++ b/src/main/cpp/option_processor.h
@@ -26,6 +26,22 @@
namespace blaze {
+struct CommandLine {
+ const std::string path_to_binary;
+ const std::vector<std::string> startup_args;
+ const std::string command;
+ const std::vector<std::string> command_args;
+
+ CommandLine(const std::string& path_to_binary_arg,
+ const std::vector<std::string>& startup_args_arg,
+ const std::string& command_arg,
+ const std::vector<std::string>& command_args_arg)
+ : path_to_binary(path_to_binary_arg),
+ startup_args(startup_args_arg),
+ command(command_arg),
+ command_args(command_args_arg) {}
+};
+
// This class is responsible for parsing the command line of the Blaze binary,
// parsing blazerc files, and putting together the command that should be sent
// to the server.
@@ -35,6 +51,29 @@
virtual ~OptionProcessor();
+ // Splits the arguments of a command line invocation.
+ //
+ // For instance:
+ // output/bazel --foo --bar=42 --bar blah build --myflag value :mytarget
+ //
+ // returns a CommandLine structure with the following values:
+ // result.path_to_binary = "output/bazel"
+ // result.startup_args = {"--foo", "--bar=42", "--bar=blah"}
+ // result.command = "build"
+ // result.command_args = {"--some_flag", "value", ":mytarget"}
+ //
+ // Note that result.startup_args is guaranteed to contain only valid
+ // startup options (w.r.t. StartupOptions::IsUnary and
+ // StartupOptions::IsNullary) and unary startup args of the form '--bar blah'
+ // are rewritten as '--bar=blah' for uniformity.
+ // In turn, the command and command args are not rewritten nor validated.
+ //
+ // If the method fails then error will contain the cause, otherwise error
+ // remains untouched.
+ std::unique_ptr<CommandLine> SplitCommandLine(
+ const std::vector<std::string>& args,
+ std::string* error);
+
// Parse a command line and the appropriate blazerc files. This should be
// invoked only once per OptionProcessor object.
blaze_exit_code::ExitCode ParseOptions(const std::vector<std::string>& args,
diff --git a/src/main/cpp/startup_options.cc b/src/main/cpp/startup_options.cc
index c4590b9..fd02628 100644
--- a/src/main/cpp/startup_options.cc
+++ b/src/main/cpp/startup_options.cc
@@ -61,8 +61,7 @@
output_root = WorkspaceLayout::GetOutputRoot();
}
- string product_name_lower = product_name;
- blaze_util::ToLower(&product_name_lower);
+ const string product_name_lower = GetLowercaseProductName();
output_user_root = blaze_util::JoinPath(
output_root, "_" + product_name_lower + "_" + GetUserName());
// 3 hours (but only 15 seconds if used within a test)
@@ -81,7 +80,13 @@
StartupOptions::~StartupOptions() {}
-bool StartupOptions::IsNullary(const string &arg) const {
+string StartupOptions::GetLowercaseProductName() const {
+ string lowercase_product_name = product_name;
+ blaze_util::ToLower(&lowercase_product_name);
+ return lowercase_product_name;
+}
+
+bool StartupOptions::IsNullary(const string& arg) const {
for (string option : nullary_options) {
if (GetNullaryOption(arg.c_str(), ("--" + option).c_str()) ||
GetNullaryOption(arg.c_str(), ("--no" + option).c_str())) {
@@ -91,11 +96,15 @@
return false;
}
-bool StartupOptions::IsUnary(const string &arg,
- const string &next_arg) const {
+bool StartupOptions::IsUnary(const string& arg) const {
for (string option : unary_options) {
- if (GetUnaryOption(arg.c_str(), next_arg.c_str(),
- ("--" + option).c_str()) != NULL) {
+ // The second argument of GetUnaryOption is not relevant to determine
+ // whether the option is unary or not, hence we set it to the empty string
+ // by default.
+ //
+ // TODO(lpino): Improve GetUnaryOption to only require the arg and the
+ // option we are looking for.
+ if (GetUnaryOption(arg.c_str(), "", ("--" + option).c_str()) != NULL) {
return true;
}
}
diff --git a/src/main/cpp/startup_options.h b/src/main/cpp/startup_options.h
index d0cd804..833af1b 100644
--- a/src/main/cpp/startup_options.h
+++ b/src/main/cpp/startup_options.h
@@ -111,12 +111,13 @@
// Checks whether the argument is a valid nullary option.
// E.g. --master_bazelrc, --nomaster_bazelrc.
- bool IsNullary(const std::string &arg) const;
+ bool IsNullary(const std::string& arg) const;
// Checks whether the argument is a valid unary option.
// E.g. --blazerc=foo, --blazerc foo.
- bool IsUnary(const std::string &arg,
- const std::string &next_arg) const;
+ bool IsUnary(const std::string& arg) const;
+
+ std::string GetLowercaseProductName() const;
// The capitalized name of this binary.
const std::string product_name;
diff --git a/src/test/cpp/startup_options_test.cc b/src/test/cpp/startup_options_test.cc
index 0291dfc..304282a 100644
--- a/src/test/cpp/startup_options_test.cc
+++ b/src/test/cpp/startup_options_test.cc
@@ -76,27 +76,27 @@
TEST_F(StartupOptionsTest, IsNullaryTest) {
blaze::StartupOptions startup_options;
- ASSERT_TRUE(startup_options.IsNullary("--master_bazelrc"));
- ASSERT_TRUE(startup_options.IsNullary("--nomaster_bazelrc"));
- ASSERT_FALSE(startup_options.IsNullary(""));
- ASSERT_FALSE(startup_options.IsNullary("--"));
- ASSERT_FALSE(startup_options.IsNullary("--master_bazelrcascasc"));
+ EXPECT_TRUE(startup_options.IsNullary("--master_bazelrc"));
+ EXPECT_TRUE(startup_options.IsNullary("--nomaster_bazelrc"));
+ EXPECT_FALSE(startup_options.IsNullary(""));
+ EXPECT_FALSE(startup_options.IsNullary("--"));
+ EXPECT_FALSE(startup_options.IsNullary("--master_bazelrcascasc"));
string error_msg = std::string("In argument '--master_bazelrc=foo': option ")
+ std::string("'--master_bazelrc' does not take a value");
- ASSERT_DEATH(startup_options.IsNullary("--master_bazelrc=foo"),
+ EXPECT_DEATH(startup_options.IsNullary("--master_bazelrc=foo"),
error_msg.c_str());
}
TEST_F(StartupOptionsTest, IsUnaryTest) {
blaze::StartupOptions startup_options;
- ASSERT_FALSE(startup_options.IsUnary("", ""));
- ASSERT_FALSE(startup_options.IsUnary("--", ""));
+ EXPECT_FALSE(startup_options.IsUnary(""));
+ EXPECT_FALSE(startup_options.IsUnary("--"));
- ASSERT_TRUE(startup_options.IsUnary("--blazerc=foo", "--blah"));
- ASSERT_TRUE(startup_options.IsUnary("--blazerc", "foo"));
- ASSERT_TRUE(startup_options.IsUnary("--blazerc=", "--foo"));
- ASSERT_TRUE(startup_options.IsUnary("--blazerc", ""));
- ASSERT_FALSE(startup_options.IsUnary("--blazercfooblah", "foo"));
+ EXPECT_TRUE(startup_options.IsUnary("--blazerc=foo"));
+ EXPECT_TRUE(startup_options.IsUnary("--blazerc"));
+ EXPECT_TRUE(startup_options.IsUnary("--blazerc="));
+ EXPECT_TRUE(startup_options.IsUnary("--blazerc"));
+ EXPECT_FALSE(startup_options.IsUnary("--blazercfooblah"));
}
} // namespace blaze