blob: 75fa396d298119283812ff3be36732c00c37db01 [file] [log] [blame]
// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/main/cpp/startup_options.h"
#include <assert.h>
#include <errno.h> // errno, ENOENT
#include <string.h> // strerror
#include <cstdio>
#include <cstdlib>
#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/blaze_util.h"
#include "src/main/cpp/util/exit_code.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
#include "src/main/cpp/util/numbers.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/cpp/workspace_layout.h"
namespace blaze {
using std::string;
using std::vector;
StartupOptions::StartupOptions() : StartupOptions("Bazel") {}
StartupOptions::StartupOptions(const string &product_name)
: product_name(product_name),
deep_execroot(true),
block_for_lock(true),
host_jvm_debug(false),
batch(false),
batch_cpu_scheduling(false),
io_nice_level(-1),
oom_more_eagerly(false),
oom_more_eagerly_threshold(100),
write_command_log(true),
watchfs(false),
allow_configurable_attributes(false),
fatal_event_bus_exceptions(false),
command_port(0),
connect_timeout_secs(10),
invocation_policy(NULL),
client_debug(false) {
bool testing = !blaze::GetEnv("TEST_TMPDIR").empty();
if (testing) {
output_root = MakeAbsolute(blaze::GetEnv("TEST_TMPDIR"));
} else {
output_root = WorkspaceLayout::GetOutputRoot();
}
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)
max_idle_secs = testing ? 15 : (3 * 3600);
nullary_options = {"deep_execroot", "block_for_lock",
"host_jvm_debug", "master_blazerc", "master_bazelrc", "batch",
"batch_cpu_scheduling", "allow_configurable_attributes",
"fatal_event_bus_exceptions", "experimental_oom_more_eagerly",
"write_command_log", "watchfs", "client_debug"};
unary_options = {"output_base", "install_base",
"output_user_root", "host_jvm_profile", "host_javabase",
"host_jvm_args", "bazelrc", "blazerc", "io_nice_level",
"max_idle_secs", "experimental_oom_more_eagerly_threshold",
"command_port", "invocation_policy", "connect_timeout_secs"};
}
StartupOptions::~StartupOptions() {}
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())) {
return true;
}
}
return false;
}
bool StartupOptions::IsUnary(const string& arg) const {
for (string option : unary_options) {
// 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;
}
}
return false;
}
void StartupOptions::AddExtraOptions(vector<string> *result) const {}
blaze_exit_code::ExitCode StartupOptions::ProcessArg(
const string &argstr, const string &next_argstr, const string &rcfile,
bool *is_space_separated, string *error) {
// We have to parse a specific option syntax, so GNU getopts won't do. All
// options begin with "--" or "-". Values are given together with the option
// delimited by '=' or in the next option.
const char* arg = argstr.c_str();
const char* next_arg = next_argstr.empty() ? NULL : next_argstr.c_str();
const char* value = NULL;
if ((value = GetUnaryOption(arg, next_arg, "--output_base")) != NULL) {
output_base = MakeAbsolute(value);
option_sources["output_base"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--install_base")) != NULL) {
install_base = MakeAbsolute(value);
option_sources["install_base"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--output_user_root")) != NULL) {
output_user_root = MakeAbsolute(value);
option_sources["output_user_root"] = rcfile;
} else if (GetNullaryOption(arg, "--deep_execroot")) {
deep_execroot = true;
option_sources["deep_execroot"] = rcfile;
} else if (GetNullaryOption(arg, "--nodeep_execroot")) {
deep_execroot = false;
option_sources["deep_execroot"] = rcfile;
} else if (GetNullaryOption(arg, "--noblock_for_lock")) {
block_for_lock = false;
option_sources["block_for_lock"] = rcfile;
} else if (GetNullaryOption(arg, "--host_jvm_debug")) {
host_jvm_debug = true;
option_sources["host_jvm_debug"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--host_jvm_profile")) != NULL) {
host_jvm_profile = value;
option_sources["host_jvm_profile"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--host_javabase")) != NULL) {
// TODO(bazel-team): Consider examining the javabase and re-execing in case
// of architecture mismatch.
host_javabase = MakeAbsolute(value);
option_sources["host_javabase"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg, "--host_jvm_args")) !=
NULL) {
host_jvm_args.push_back(value);
option_sources["host_jvm_args"] = rcfile; // NB: This is incorrect
} else if ((value = GetUnaryOption(arg, next_arg, "--bazelrc")) != NULL) {
if (rcfile != "") {
*error = "Can't specify --bazelrc in the .bazelrc file.";
return blaze_exit_code::BAD_ARGV;
}
} else if ((value = GetUnaryOption(arg, next_arg, "--blazerc")) != NULL) {
if (rcfile != "") {
*error = "Can't specify --blazerc in the .blazerc file.";
return blaze_exit_code::BAD_ARGV;
}
} else if (GetNullaryOption(arg, "--nomaster_blazerc") ||
GetNullaryOption(arg, "--master_blazerc")) {
if (rcfile != "") {
*error = "Can't specify --[no]master_blazerc in .blazerc file.";
return blaze_exit_code::BAD_ARGV;
}
option_sources["blazerc"] = rcfile;
} else if (GetNullaryOption(arg, "--nomaster_bazelrc") ||
GetNullaryOption(arg, "--master_bazelrc")) {
if (rcfile != "") {
*error = "Can't specify --[no]master_bazelrc in .bazelrc file.";
return blaze_exit_code::BAD_ARGV;
}
option_sources["blazerc"] = rcfile;
} else if (GetNullaryOption(arg, "--batch")) {
batch = true;
option_sources["batch"] = rcfile;
} else if (GetNullaryOption(arg, "--nobatch")) {
batch = false;
option_sources["batch"] = rcfile;
} else if (GetNullaryOption(arg, "--batch_cpu_scheduling")) {
batch_cpu_scheduling = true;
option_sources["batch_cpu_scheduling"] = rcfile;
} else if (GetNullaryOption(arg, "--nobatch_cpu_scheduling")) {
batch_cpu_scheduling = false;
option_sources["batch_cpu_scheduling"] = rcfile;
} else if (GetNullaryOption(arg, "--allow_configurable_attributes")) {
allow_configurable_attributes = true;
option_sources["allow_configurable_attributes"] = rcfile;
} else if (GetNullaryOption(arg, "--noallow_configurable_attributes")) {
allow_configurable_attributes = false;
option_sources["allow_configurable_attributes"] = rcfile;
} else if (GetNullaryOption(arg, "--fatal_event_bus_exceptions")) {
fatal_event_bus_exceptions = true;
option_sources["fatal_event_bus_exceptions"] = rcfile;
} else if (GetNullaryOption(arg, "--nofatal_event_bus_exceptions")) {
fatal_event_bus_exceptions = false;
option_sources["fatal_event_bus_exceptions"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--io_nice_level")) != NULL) {
if (!blaze_util::safe_strto32(value, &io_nice_level) ||
io_nice_level > 7) {
blaze_util::StringPrintf(error,
"Invalid argument to --io_nice_level: '%s'. Must not exceed 7.",
value);
return blaze_exit_code::BAD_ARGV;
}
option_sources["io_nice_level"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--max_idle_secs")) != NULL) {
if (!blaze_util::safe_strto32(value, &max_idle_secs) ||
max_idle_secs < 0) {
blaze_util::StringPrintf(error,
"Invalid argument to --max_idle_secs: '%s'.", value);
return blaze_exit_code::BAD_ARGV;
}
option_sources["max_idle_secs"] = rcfile;
} else if (GetNullaryOption(arg, "-x")) {
fprintf(stderr, "WARNING: The -x startup option is now ignored "
"and will be removed in a future release\n");
} else if (GetNullaryOption(arg, "--experimental_oom_more_eagerly")) {
oom_more_eagerly = true;
option_sources["experimental_oom_more_eagerly"] = rcfile;
} else if (GetNullaryOption(arg, "--noexperimental_oom_more_eagerly")) {
oom_more_eagerly = false;
option_sources["experimental_oom_more_eagerly"] = rcfile;
} else if ((value = GetUnaryOption(
arg, next_arg,
"--experimental_oom_more_eagerly_threshold")) != NULL) {
if (!blaze_util::safe_strto32(value, &oom_more_eagerly_threshold) ||
oom_more_eagerly_threshold < 0) {
blaze_util::StringPrintf(error,
"Invalid argument to "
"--experimental_oom_more_eagerly_threshold: "
"'%s'.",
value);
return blaze_exit_code::BAD_ARGV;
}
option_sources["experimental_oom_more_eagerly_threshold"] = rcfile;
} else if (GetNullaryOption(arg, "--write_command_log")) {
write_command_log = true;
option_sources["write_command_log"] = rcfile;
} else if (GetNullaryOption(arg, "--nowrite_command_log")) {
write_command_log = false;
option_sources["write_command_log"] = rcfile;
} else if (GetNullaryOption(arg, "--watchfs")) {
watchfs = true;
option_sources["watchfs"] = rcfile;
} else if (GetNullaryOption(arg, "--nowatchfs")) {
watchfs = false;
option_sources["watchfs"] = rcfile;
} else if (GetNullaryOption(arg, "--client_debug")) {
client_debug = true;
option_sources["client_debug"] = rcfile;
} else if (GetNullaryOption(arg, "--noclient_debug")) {
client_debug = false;
option_sources["client_debug"] = rcfile;
} else if ((value = GetUnaryOption(
arg, next_arg, "--connect_timeout_secs")) != NULL) {
if (!blaze_util::safe_strto32(value, &connect_timeout_secs) ||
connect_timeout_secs < 1 || connect_timeout_secs > 120) {
blaze_util::StringPrintf(error,
"Invalid argument to --connect_timeout_secs: '%s'.\n"
"Must be an integer between 1 and 120.\n",
value);
return blaze_exit_code::BAD_ARGV;
}
option_sources["connect_timeout_secs"] = rcfile;
} else if ((value = GetUnaryOption(
arg, next_arg, "--command_port")) != NULL) {
if (!blaze_util::safe_strto32(value, &command_port) ||
command_port < 0 || command_port > 65535) {
blaze_util::StringPrintf(error,
"Invalid argument to --command_port: '%s'.\n"
"Must be a valid port number or 0.\n",
value);
return blaze_exit_code::BAD_ARGV;
}
option_sources["command_port"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg, "--invocation_policy"))
!= NULL) {
if (invocation_policy == NULL) {
invocation_policy = value;
option_sources["invocation_policy"] = rcfile;
} else {
*error = "The startup flag --invocation_policy cannot be specified "
"multiple times.";
return blaze_exit_code::BAD_ARGV;
}
} else {
bool extra_argument_processed;
blaze_exit_code::ExitCode process_extra_arg_exit_code = ProcessArgExtra(
arg, next_arg, rcfile, &value, &extra_argument_processed, error);
if (process_extra_arg_exit_code != blaze_exit_code::SUCCESS) {
return process_extra_arg_exit_code;
}
if (!extra_argument_processed) {
blaze_util::StringPrintf(
error,
"Unknown %s startup option: '%s'.\n"
" For more info, run '%s help startup_options'.",
product_name.c_str(), arg, product_name.c_str());
return blaze_exit_code::BAD_ARGV;
}
}
*is_space_separated = ((value == next_arg) && (value != NULL));
return blaze_exit_code::SUCCESS;
}
blaze_exit_code::ExitCode StartupOptions::ProcessArgExtra(
const char *arg, const char *next_arg, const string &rcfile,
const char **value, bool *is_processed, string *error) {
*is_processed = false;
return blaze_exit_code::SUCCESS;
}
string StartupOptions::GetDefaultHostJavabase() const {
return blaze::GetDefaultHostJavabase();
}
string StartupOptions::GetHostJavabase() {
if (host_javabase.empty()) {
host_javabase = GetDefaultHostJavabase();
}
return host_javabase;
}
string StartupOptions::GetJvm() {
string java_program = GetHostJavabase() + "/bin/java";
if (!blaze_util::CanAccess(java_program, false, false, true)) {
if (!blaze_util::PathExists(java_program)) {
fprintf(stderr, "Couldn't find java at '%s'.\n", java_program.c_str());
} else {
fprintf(stderr, "Couldn't access %s: %s\n", java_program.c_str(),
strerror(errno));
}
exit(1);
}
// If the full JDK is installed
string jdk_rt_jar = GetHostJavabase() + "/jre/lib/rt.jar";
// If just the JRE is installed
string jre_rt_jar = GetHostJavabase() + "/lib/rt.jar";
if (blaze_util::CanAccess(jdk_rt_jar, true, false, false)
|| blaze_util::CanAccess(jre_rt_jar, true, false, false)) {
return java_program;
}
fprintf(stderr, "Problem with java installation: "
"couldn't find/access rt.jar in %s\n", GetHostJavabase().c_str());
exit(1);
}
string StartupOptions::GetExe(const string &jvm, const string &jar_path) {
return jvm;
}
void StartupOptions::AddJVMArgumentPrefix(const string &javabase,
std::vector<string> *result) const {
}
void StartupOptions::AddJVMArgumentSuffix(const string &real_install_dir,
const string &jar_path,
std::vector<string> *result) const {
result->push_back("-jar");
result->push_back(blaze::ConvertPath(
blaze_util::JoinPath(real_install_dir, jar_path)));
}
blaze_exit_code::ExitCode StartupOptions::AddJVMArguments(
const string &host_javabase, vector<string> *result,
const vector<string> &user_options, string *error) const {
// Configure logging
const string propFile = output_base + "/javalog.properties";
if (!blaze_util::WriteFile("handlers=java.util.logging.FileHandler\n"
".level=INFO\n"
"java.util.logging.FileHandler.level=INFO\n"
"java.util.logging.FileHandler.pattern=" +
output_base +
"/java.log\n"
"java.util.logging.FileHandler.limit=50000\n"
"java.util.logging.FileHandler.count=1\n"
"java.util.logging.FileHandler.formatter="
"java.util.logging.SimpleFormatter\n",
propFile)) {
perror(("Couldn't write logging file " + propFile).c_str());
} else {
result->push_back("-Djava.util.logging.config.file=" + propFile);
}
return blaze_exit_code::SUCCESS;
}
blaze_exit_code::ExitCode StartupOptions::ValidateStartupOptions(
const std::vector<string>& args, string* error) {
return blaze_exit_code::SUCCESS;
}
} // namespace blaze