|  | // 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 <cstdio> | 
|  | #include <cstdlib> | 
|  | #include <cstring> | 
|  |  | 
|  | #include "src/main/cpp/blaze_util.h" | 
|  | #include "src/main/cpp/blaze_util_platform.h" | 
|  | #include "src/main/cpp/util/errors.h" | 
|  | #include "src/main/cpp/util/exit_code.h" | 
|  | #include "src/main/cpp/util/file.h" | 
|  | #include "src/main/cpp/util/logging.h" | 
|  | #include "src/main/cpp/util/numbers.h" | 
|  | #include "src/main/cpp/util/path.h" | 
|  | #include "src/main/cpp/util/path_platform.h" | 
|  | #include "src/main/cpp/util/strings.h" | 
|  | #include "src/main/cpp/workspace_layout.h" | 
|  |  | 
|  | namespace blaze { | 
|  |  | 
|  | using std::string; | 
|  | using std::vector; | 
|  |  | 
|  | void StartupOptions::RegisterNullaryStartupFlag(const std::string &flag_name, | 
|  | bool *flag_value) { | 
|  | all_nullary_startup_flags_[std::string("--") + flag_name] = flag_value; | 
|  | all_nullary_startup_flags_[std::string("--no") + flag_name] = flag_value; | 
|  | } | 
|  |  | 
|  | void StartupOptions::RegisterNullaryStartupFlagNoRc( | 
|  | const std::string &flag_name, bool *flag_value) { | 
|  | RegisterNullaryStartupFlag(flag_name, flag_value); | 
|  | no_rc_nullary_startup_flags_.insert(std::string("--") + flag_name); | 
|  | no_rc_nullary_startup_flags_.insert(std::string("--no") + flag_name); | 
|  | } | 
|  |  | 
|  | void StartupOptions::RegisterSpecialNullaryStartupFlag( | 
|  | const std::string &flag_name, SpecialNullaryFlagHandler handler) { | 
|  | RegisterNullaryStartupFlag(flag_name, nullptr); | 
|  | special_nullary_startup_flags_[std::string("--") + flag_name] = handler; | 
|  | special_nullary_startup_flags_[std::string("--no") + flag_name] = handler; | 
|  | } | 
|  |  | 
|  | void StartupOptions::RegisterUnaryStartupFlag(const std::string &flag_name) { | 
|  | valid_unary_startup_flags_.insert(std::string("--") + flag_name); | 
|  | } | 
|  |  | 
|  | void StartupOptions::OverrideOptionSourcesKey(const std::string &flag_name, | 
|  | const std::string &new_name) { | 
|  | option_sources_key_override_[flag_name] = new_name; | 
|  | } | 
|  |  | 
|  | StartupOptions::StartupOptions(const string &product_name, | 
|  | const WorkspaceLayout *workspace_layout) | 
|  | : product_name(product_name), | 
|  | ignore_all_rc_files(false), | 
|  | block_for_lock(true), | 
|  | host_jvm_debug(false), | 
|  | autodetect_server_javabase(true), | 
|  | batch(false), | 
|  | batch_cpu_scheduling(false), | 
|  | io_nice_level(-1), | 
|  | shutdown_on_low_sys_mem(false), | 
|  | oom_more_eagerly(false), | 
|  | oom_more_eagerly_threshold(100), | 
|  | write_command_log(true), | 
|  | watchfs(false), | 
|  | fatal_event_bus_exceptions(false), | 
|  | command_port(0), | 
|  | connect_timeout_secs(30), | 
|  | local_startup_timeout_secs(120), | 
|  | have_invocation_policy_(false), | 
|  | client_debug(false), | 
|  | preemptible(false), | 
|  | java_logging_formatter( | 
|  | "com.google.devtools.build.lib.util.SingleLineFormatter"), | 
|  | expand_configs_in_place(true), | 
|  | digest_function(), | 
|  | idle_server_tasks(true), | 
|  | original_startup_options_(std::vector<RcStartupFlag>()), | 
|  | #if defined(__APPLE__) | 
|  | macos_qos_class(QOS_CLASS_UNSPECIFIED), | 
|  | #endif | 
|  | unlimit_coredumps(false), | 
|  | windows_enable_symlinks(false) { | 
|  | if (blaze::IsRunningWithinTest()) { | 
|  | output_root = blaze_util::MakeAbsolute(blaze::GetPathEnv("TEST_TMPDIR")); | 
|  | max_idle_secs = 15; | 
|  | BAZEL_LOG(USER) << "$TEST_TMPDIR defined: output root default is '" | 
|  | << output_root << "' and max_idle_secs default is '" | 
|  | << max_idle_secs << "'."; | 
|  | } else { | 
|  | output_root = workspace_layout->GetOutputRoot(); | 
|  | max_idle_secs = 3 * 3600; | 
|  | BAZEL_LOG(INFO) << "output root is '" << output_root | 
|  | << "' and max_idle_secs default is '" << max_idle_secs | 
|  | << "'."; | 
|  | } | 
|  |  | 
|  | #if defined(_WIN32) || defined(__CYGWIN__) | 
|  | string windows_unix_root = DetectBashAndExportBazelSh(); | 
|  | if (!windows_unix_root.empty()) { | 
|  | host_jvm_args.push_back(string("-Dbazel.windows_unix_root=") + | 
|  | windows_unix_root); | 
|  | } | 
|  | #endif  // defined(_WIN32) || defined(__CYGWIN__) | 
|  |  | 
|  | const string product_name_lower = GetLowercaseProductName(); | 
|  | output_user_root = blaze_util::JoinPath( | 
|  | output_root, "_" + product_name_lower + "_" + GetUserName()); | 
|  |  | 
|  | // IMPORTANT: Before modifying the statements below please contact a Bazel | 
|  | // core team member that knows the internal procedure for adding/deprecating | 
|  | // startup flags. | 
|  | RegisterNullaryStartupFlag("batch", &batch); | 
|  | RegisterNullaryStartupFlag("batch_cpu_scheduling", &batch_cpu_scheduling); | 
|  | RegisterNullaryStartupFlag("block_for_lock", &block_for_lock); | 
|  | RegisterNullaryStartupFlag("client_debug", &client_debug); | 
|  | RegisterNullaryStartupFlag("preemptible", &preemptible); | 
|  | RegisterNullaryStartupFlag("expand_configs_in_place", | 
|  | &expand_configs_in_place); | 
|  | RegisterNullaryStartupFlag("fatal_event_bus_exceptions", | 
|  | &fatal_event_bus_exceptions); | 
|  | RegisterNullaryStartupFlag("host_jvm_debug", &host_jvm_debug); | 
|  | RegisterNullaryStartupFlag("autodetect_server_javabase", | 
|  | &autodetect_server_javabase); | 
|  | RegisterNullaryStartupFlag("idle_server_tasks", &idle_server_tasks); | 
|  | RegisterNullaryStartupFlag("shutdown_on_low_sys_mem", | 
|  | &shutdown_on_low_sys_mem); | 
|  | RegisterNullaryStartupFlagNoRc("ignore_all_rc_files", &ignore_all_rc_files); | 
|  | RegisterNullaryStartupFlag("unlimit_coredumps", &unlimit_coredumps); | 
|  | RegisterNullaryStartupFlag("watchfs", &watchfs); | 
|  | RegisterNullaryStartupFlag("write_command_log", &write_command_log); | 
|  | RegisterNullaryStartupFlag("windows_enable_symlinks", | 
|  | &windows_enable_symlinks); | 
|  | RegisterUnaryStartupFlag("command_port"); | 
|  | RegisterUnaryStartupFlag("connect_timeout_secs"); | 
|  | RegisterUnaryStartupFlag("local_startup_timeout_secs"); | 
|  | RegisterUnaryStartupFlag("digest_function"); | 
|  | RegisterUnaryStartupFlag("unix_digest_hash_attribute_name"); | 
|  | RegisterUnaryStartupFlag("server_javabase"); | 
|  | RegisterUnaryStartupFlag("host_jvm_args"); | 
|  | RegisterUnaryStartupFlag("host_jvm_profile"); | 
|  | RegisterUnaryStartupFlag("invocation_policy"); | 
|  | RegisterUnaryStartupFlag("io_nice_level"); | 
|  | RegisterUnaryStartupFlag("install_base"); | 
|  | RegisterUnaryStartupFlag("macos_qos_class"); | 
|  | RegisterUnaryStartupFlag("max_idle_secs"); | 
|  | RegisterUnaryStartupFlag("output_base"); | 
|  | RegisterUnaryStartupFlag("output_user_root"); | 
|  | RegisterUnaryStartupFlag("server_jvm_out"); | 
|  | RegisterUnaryStartupFlag("failure_detail_out"); | 
|  | } | 
|  |  | 
|  | StartupOptions::~StartupOptions() {} | 
|  |  | 
|  | string StartupOptions::GetLowercaseProductName() const { | 
|  | string lowercase_product_name = product_name; | 
|  | blaze_util::ToLower(&lowercase_product_name); | 
|  | return lowercase_product_name; | 
|  | } | 
|  |  | 
|  | bool StartupOptions::IsUnary(const string &arg) const { | 
|  | std::string::size_type i = arg.find_first_of('='); | 
|  | if (i == std::string::npos) { | 
|  | return valid_unary_startup_flags_.find(arg) != | 
|  | valid_unary_startup_flags_.end(); | 
|  | } else { | 
|  | return valid_unary_startup_flags_.find(arg.substr(0, i)) != | 
|  | valid_unary_startup_flags_.end(); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | *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 {} | 
|  |  | 
|  | 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() ? nullptr : next_argstr.c_str(); | 
|  | const char *value = nullptr; | 
|  |  | 
|  | 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) != | 
|  | no_rc_nullary_startup_flags_.end()) { | 
|  | // no_rc_nullary_startup_flags_ are forbidden in .bazelrc files. | 
|  | if (!rcfile.empty()) { | 
|  | *error = std::string("Can't specify ") + argstr + " in the " + | 
|  | GetRcFileBaseName() + " file."; | 
|  | return blaze_exit_code::BAD_ARGV; | 
|  | } | 
|  | } | 
|  | if (special_nullary_startup_flags_.find(argstr) != | 
|  | special_nullary_startup_flags_.end()) { | 
|  | // 'argstr' is either "--foo" or "--nofoo", and the map entry is the | 
|  | // lambda that handles setting the flag's value. | 
|  | special_nullary_startup_flags_[argstr](enabled); | 
|  | } else { | 
|  | // 'argstr' is either "--foo" or "--nofoo", and the map entry is the | 
|  | // pointer to the bool storing the flag's value. | 
|  | *all_nullary_startup_flags_[argstr] = enabled; | 
|  | } | 
|  | // Use the key "foo" for 'argstr' of "--foo" / "--nofoo", unless there's an | 
|  | // overridden name we must use. | 
|  | std::string key = argstr.substr(enabled ? 2 : 4); | 
|  | if (option_sources_key_override_.find(key) != | 
|  | option_sources_key_override_.end()) { | 
|  | key = option_sources_key_override_[key]; | 
|  | } | 
|  | option_sources[key] = rcfile; | 
|  | *is_space_separated = false; | 
|  | return blaze_exit_code::SUCCESS; | 
|  | } | 
|  |  | 
|  | if ((value = GetUnaryOption(arg, next_arg, "--output_base")) != nullptr) { | 
|  | output_base = blaze_util::Path(blaze::AbsolutePathFromFlag(value)); | 
|  | option_sources["output_base"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--install_base")) != | 
|  | nullptr) { | 
|  | install_base = blaze::AbsolutePathFromFlag(value); | 
|  | option_sources["install_base"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--output_user_root")) != | 
|  | nullptr) { | 
|  | output_user_root = blaze::AbsolutePathFromFlag(value); | 
|  | option_sources["output_user_root"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--server_jvm_out")) != | 
|  | nullptr) { | 
|  | server_jvm_out = blaze_util::Path(blaze::AbsolutePathFromFlag(value)); | 
|  | option_sources["server_jvm_out"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--failure_detail_out")) != | 
|  | nullptr) { | 
|  | failure_detail_out = blaze_util::Path(blaze::AbsolutePathFromFlag(value)); | 
|  | option_sources["failure_detail_out"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--host_jvm_profile")) != | 
|  | nullptr) { | 
|  | host_jvm_profile = value; | 
|  | option_sources["host_jvm_profile"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--server_javabase")) != | 
|  | nullptr) { | 
|  | // TODO(bazel-team): Consider examining the javabase and re-execing in case | 
|  | // of architecture mismatch. | 
|  | explicit_server_javabase_ = | 
|  | blaze_util::Path(blaze::AbsolutePathFromFlag(value)); | 
|  | option_sources["server_javabase"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--host_jvm_args")) != | 
|  | nullptr) { | 
|  | host_jvm_args.push_back(value); | 
|  | option_sources["host_jvm_args"] = rcfile;  // NB: This is incorrect | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--io_nice_level")) != | 
|  | nullptr) { | 
|  | 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")) != | 
|  | nullptr) { | 
|  | 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 ((value = GetUnaryOption(arg, next_arg, "--macos_qos_class")) != | 
|  | nullptr) { | 
|  | // We parse the value of this flag on all platforms even if it is | 
|  | // macOS-specific to ensure that rc files mentioning it are valid. | 
|  | // There is also apparently "QOS_CLASS_MAINTENANCE", but this doesn't | 
|  | // appear to have been exposed in the public headers as of macOS 11.1. | 
|  | if (strcmp(value, "utility") == 0) { | 
|  | #if defined(__APPLE__) | 
|  | macos_qos_class = QOS_CLASS_UTILITY; | 
|  | #endif | 
|  | } else if (strcmp(value, "background") == 0) { | 
|  | #if defined(__APPLE__) | 
|  | macos_qos_class = QOS_CLASS_BACKGROUND; | 
|  | #endif | 
|  | } else { | 
|  | blaze_util::StringPrintf( | 
|  | error, "Invalid argument to --macos_qos_class: '%s'.", value); | 
|  | return blaze_exit_code::BAD_ARGV; | 
|  | } | 
|  | option_sources["macos_qos_class"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, | 
|  | "--connect_timeout_secs")) != nullptr) { | 
|  | if (!blaze_util::safe_strto32(value, &connect_timeout_secs) || | 
|  | connect_timeout_secs < 1 || connect_timeout_secs > 3600) { | 
|  | blaze_util::StringPrintf( | 
|  | error, | 
|  | "Invalid argument to --connect_timeout_secs: '%s'.\n" | 
|  | "Must be an integer between 1 and 3600.\n", | 
|  | value); | 
|  | return blaze_exit_code::BAD_ARGV; | 
|  | } | 
|  | option_sources["connect_timeout_secs"] = rcfile; | 
|  | } else if ((value = GetUnaryOption( | 
|  | arg, next_arg, "--local_startup_timeout_secs")) != nullptr) { | 
|  | if (!blaze_util::safe_strto32(value, &local_startup_timeout_secs) || | 
|  | local_startup_timeout_secs < 1) { | 
|  | blaze_util::StringPrintf( | 
|  | error, | 
|  | "Invalid argument to --local_startup_timeout_secs: '%s'.\n" | 
|  | "Must be a positive integer.\n", | 
|  | value); | 
|  | return blaze_exit_code::BAD_ARGV; | 
|  | } | 
|  | option_sources["local_startup_timeout_secs"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--digest_function")) != | 
|  | nullptr) { | 
|  | digest_function = value; | 
|  | option_sources["digest_function"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, | 
|  | "--unix_digest_hash_attribute_name")) != | 
|  | nullptr) { | 
|  | unix_digest_hash_attribute_name = value; | 
|  | option_sources["unix_digest_hash_attribute_name"] = rcfile; | 
|  | } else if ((value = GetUnaryOption(arg, next_arg, "--command_port")) != | 
|  | nullptr) { | 
|  | 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")) != | 
|  | nullptr) { | 
|  | if (!have_invocation_policy_) { | 
|  | have_invocation_policy_ = true; | 
|  | 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 startup option: '%s'.\n" | 
|  | "  For more info, run '%s help startup_options'.", | 
|  | arg, GetLowercaseProductName().c_str()); | 
|  | return blaze_exit_code::BAD_ARGV; | 
|  | } | 
|  | } | 
|  |  | 
|  | *is_space_separated = ((value == next_arg) && (value != nullptr)); | 
|  | return blaze_exit_code::SUCCESS; | 
|  | } | 
|  |  | 
|  | blaze_exit_code::ExitCode StartupOptions::ProcessArgs( | 
|  | const std::vector<RcStartupFlag>& rcstartup_flags, | 
|  | std::string *error) { | 
|  | std::vector<RcStartupFlag>::size_type i = 0; | 
|  | while (i < rcstartup_flags.size()) { | 
|  | bool is_space_separated = false; | 
|  | const std::string next_value = | 
|  | (i == rcstartup_flags.size() - 1) ? "" : rcstartup_flags[i + 1].value; | 
|  | const blaze_exit_code::ExitCode process_arg_exit_code = | 
|  | ProcessArg(rcstartup_flags[i].value, next_value, | 
|  | rcstartup_flags[i].source, &is_space_separated, error); | 
|  | // Store the provided option in --flag(=value)? form. Store these before | 
|  | // propagating any error code, since we want to have the correct | 
|  | // information for the output. The fact that the options aren't parseable | 
|  | // doesn't matter for this step. | 
|  | if (is_space_separated) { | 
|  | const std::string combined_value = | 
|  | rcstartup_flags[i].value + "=" + next_value; | 
|  | original_startup_options_.push_back( | 
|  | RcStartupFlag(rcstartup_flags[i].source, combined_value)); | 
|  | i += 2; | 
|  | } else { | 
|  | original_startup_options_.push_back( | 
|  | RcStartupFlag(rcstartup_flags[i].source, rcstartup_flags[i].value)); | 
|  | i++; | 
|  | } | 
|  |  | 
|  | if (process_arg_exit_code != blaze_exit_code::SUCCESS) { | 
|  | return process_arg_exit_code; | 
|  | } | 
|  | } | 
|  | return blaze_exit_code::SUCCESS; | 
|  | } | 
|  |  | 
|  | blaze_util::Path StartupOptions::GetSystemJavabase() const { | 
|  | return blaze_util::Path(blaze::GetSystemJavabase()); | 
|  | } | 
|  |  | 
|  | blaze_util::Path StartupOptions::GetEmbeddedJavabase() const { | 
|  | blaze_util::Path bundled_jre_path = blaze_util::Path( | 
|  | blaze_util::JoinPath(install_base, "embedded_tools/jdk")); | 
|  | if (blaze_util::CanExecuteFile( | 
|  | bundled_jre_path.GetRelative(GetJavaBinaryUnderJavabase()))) { | 
|  | return bundled_jre_path; | 
|  | } | 
|  | return blaze_util::Path(); | 
|  | } | 
|  |  | 
|  | std::pair<blaze_util::Path, StartupOptions::JavabaseType> | 
|  | StartupOptions::GetServerJavabaseAndType() const { | 
|  | // 1) Allow overriding the server_javabase via --server_javabase. | 
|  | if (!explicit_server_javabase_.IsEmpty()) { | 
|  | return std::pair<blaze_util::Path, JavabaseType>(explicit_server_javabase_, | 
|  | JavabaseType::EXPLICIT); | 
|  | } | 
|  | if (default_server_javabase_.first.IsEmpty()) { | 
|  | blaze_util::Path bundled_jre_path = GetEmbeddedJavabase(); | 
|  | if (!bundled_jre_path.IsEmpty()) { | 
|  | // 2) Use a bundled JVM if we have one. | 
|  | default_server_javabase_ = std::pair<blaze_util::Path, JavabaseType>( | 
|  | bundled_jre_path, JavabaseType::EMBEDDED); | 
|  | } else if (!autodetect_server_javabase) { | 
|  | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
|  | << "Could not find embedded or explicit server javabase, and " | 
|  | "--noautodetect_server_javabase is set."; | 
|  | } else { | 
|  | // 3) Otherwise fall back to using the default system JVM. | 
|  | blaze_util::Path system_javabase = GetSystemJavabase(); | 
|  | if (system_javabase.IsEmpty()) { | 
|  | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
|  | << "Could not find system javabase. Ensure JAVA_HOME is set, or " | 
|  | "javac is on your PATH."; | 
|  | } | 
|  | default_server_javabase_ = std::pair<blaze_util::Path, JavabaseType>( | 
|  | system_javabase, JavabaseType::SYSTEM); | 
|  | } | 
|  | } | 
|  | return default_server_javabase_; | 
|  | } | 
|  |  | 
|  | blaze_util::Path StartupOptions::GetServerJavabase() const { | 
|  | return GetServerJavabaseAndType().first; | 
|  | } | 
|  |  | 
|  | blaze_util::Path StartupOptions::GetExplicitServerJavabase() const { | 
|  | return explicit_server_javabase_; | 
|  | } | 
|  |  | 
|  | blaze_util::Path StartupOptions::GetJvm() const { | 
|  | auto javabase_and_type = GetServerJavabaseAndType(); | 
|  | blaze_exit_code::ExitCode sanity_check_code = | 
|  | SanityCheckJavabase(javabase_and_type.first, javabase_and_type.second); | 
|  | if (sanity_check_code != blaze_exit_code::SUCCESS) { | 
|  | exit(sanity_check_code); | 
|  | } | 
|  | return javabase_and_type.first.GetRelative(GetJavaBinaryUnderJavabase()); | 
|  | } | 
|  |  | 
|  | // Prints an appropriate error message and returns an appropriate error exit | 
|  | // code for a server javabase which failed sanity checks. | 
|  | static blaze_exit_code::ExitCode BadServerJavabaseError( | 
|  | StartupOptions::JavabaseType javabase_type, | 
|  | const std::map<string, string> &option_sources) { | 
|  | switch (javabase_type) { | 
|  | case StartupOptions::JavabaseType::EXPLICIT: { | 
|  | auto source = option_sources.find("server_javabase"); | 
|  | string rc_file; | 
|  | if (source != option_sources.end() && !source->second.empty()) { | 
|  | rc_file = source->second; | 
|  | } | 
|  | BAZEL_LOG(ERROR) | 
|  | << "  The java path was specified by a '--server_javabase' option " + | 
|  | (rc_file.empty() ? "on the command line" : "in " + rc_file); | 
|  | return blaze_exit_code::BAD_ARGV; | 
|  | } | 
|  | case StartupOptions::JavabaseType::EMBEDDED: | 
|  | BAZEL_LOG(ERROR) << "  Internal error: embedded JDK fails sanity check."; | 
|  | return blaze_exit_code::INTERNAL_ERROR; | 
|  | case StartupOptions::JavabaseType::SYSTEM: | 
|  | return blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR; | 
|  | default: | 
|  | BAZEL_LOG(ERROR) | 
|  | << "  Internal error: server javabase type was not initialized."; | 
|  | // Fall through. | 
|  | } | 
|  | return blaze_exit_code::INTERNAL_ERROR; | 
|  | } | 
|  |  | 
|  | blaze_exit_code::ExitCode StartupOptions::SanityCheckJavabase( | 
|  | const blaze_util::Path &javabase, | 
|  | StartupOptions::JavabaseType javabase_type) const { | 
|  | blaze_util::Path java_program = | 
|  | javabase.GetRelative(GetJavaBinaryUnderJavabase()); | 
|  | if (!blaze_util::CanExecuteFile(java_program)) { | 
|  | if (!blaze_util::PathExists(java_program)) { | 
|  | BAZEL_LOG(ERROR) << "Couldn't find java at '" | 
|  | << java_program.AsPrintablePath() << "'."; | 
|  | } else { | 
|  | string err = blaze_util::GetLastErrorString(); | 
|  | BAZEL_LOG(ERROR) << "Java at '" << java_program.AsPrintablePath() | 
|  | << "' exists but is not executable: " << err; | 
|  | } | 
|  | return BadServerJavabaseError(javabase_type, option_sources); | 
|  | } | 
|  | if (  // If the full JDK is installed | 
|  | blaze_util::CanReadFile(javabase.GetRelative("jre/lib/rt.jar")) || | 
|  | // If just the JRE is installed | 
|  | blaze_util::CanReadFile(javabase.GetRelative("lib/rt.jar")) || | 
|  | // rt.jar does not exist in java 9+ so check for java instead | 
|  | blaze_util::CanReadFile(javabase.GetRelative("bin/java")) || | 
|  | blaze_util::CanReadFile(javabase.GetRelative("bin/java.exe"))) { | 
|  | return blaze_exit_code::SUCCESS; | 
|  | } | 
|  | BAZEL_LOG(ERROR) << "Problem with java installation: couldn't find/access " | 
|  | "rt.jar or java in " | 
|  | << javabase.AsPrintablePath(); | 
|  | return BadServerJavabaseError(javabase_type, option_sources); | 
|  | } | 
|  |  | 
|  | blaze_util::Path StartupOptions::GetExe(const blaze_util::Path &jvm, | 
|  | const string &jar_path) const { | 
|  | return jvm; | 
|  | } | 
|  |  | 
|  | void StartupOptions::AddJVMArgumentPrefix(const blaze_util::Path &javabase, | 
|  | std::vector<string> *result) const {} | 
|  |  | 
|  | void StartupOptions::AddJVMArgumentSuffix( | 
|  | const blaze_util::Path &real_install_dir, const string &jar_path, | 
|  | std::vector<string> *result) const { | 
|  | result->push_back("-jar"); | 
|  | result->push_back(real_install_dir.GetRelative(jar_path).AsJvmArgument()); | 
|  | } | 
|  |  | 
|  | blaze_exit_code::ExitCode StartupOptions::AddJVMArguments( | 
|  | const blaze_util::Path &server_javabase, std::vector<string> *result, | 
|  | const vector<string> &user_options, string *error) const { | 
|  | AddJVMLoggingArguments(result); | 
|  |  | 
|  | // Disable the JVM's own unlimiting of file descriptors.  We do this | 
|  | // ourselves in blaze.cc so we want our setting to propagate to the JVM. | 
|  | // | 
|  | // The reason to do this is that the JVM's unlimiting is suboptimal on | 
|  | // macOS.  Under that platform, the JVM limits the open file descriptors | 
|  | // to the OPEN_MAX constant... which is much lower than the per-process | 
|  | // kernel allowed limit of kern.maxfilesperproc (which is what we set | 
|  | // ourselves to). | 
|  | result->push_back("-XX:-MaxFDLimit"); | 
|  |  | 
|  | return AddJVMMemoryArguments(server_javabase, result, user_options, error); | 
|  | } | 
|  |  | 
|  | static std::string GetSimpleLogHandlerProps( | 
|  | const blaze_util::Path &java_log, | 
|  | const std::string &java_logging_formatter) { | 
|  | return "handlers=com.google.devtools.build.lib.util.SimpleLogHandler\n" | 
|  | ".level=INFO\n" | 
|  | "com.google.devtools.build.lib.util.SimpleLogHandler.level=INFO\n" | 
|  | "com.google.devtools.build.lib.util.SimpleLogHandler.prefix=" + | 
|  | java_log.AsJvmArgument() + | 
|  | "\n" | 
|  | "com.google.devtools.build.lib.util.SimpleLogHandler.limit=1024000\n" | 
|  | "com.google.devtools.build.lib.util.SimpleLogHandler.total_limit=" | 
|  | "20971520\n"  // 20 MB. | 
|  | "com.google.devtools.build.lib.util.SimpleLogHandler.formatter=" + | 
|  | java_logging_formatter + "\n"; | 
|  | } | 
|  |  | 
|  | void StartupOptions::AddJVMLoggingArguments(std::vector<string> *result) const { | 
|  | // Configure logging | 
|  | const blaze_util::Path propFile = | 
|  | output_base.GetRelative("javalog.properties"); | 
|  | const blaze_util::Path java_log = output_base.GetRelative("java.log"); | 
|  | const std::string loggingProps = | 
|  | GetSimpleLogHandlerProps(java_log, java_logging_formatter); | 
|  |  | 
|  | if (!blaze_util::WriteFile(loggingProps, propFile)) { | 
|  | perror( | 
|  | ("Couldn't write logging file " + propFile.AsPrintablePath()).c_str()); | 
|  | } else { | 
|  | result->push_back("-Djava.util.logging.config.file=" + | 
|  | propFile.AsJvmArgument()); | 
|  | result->push_back( | 
|  | "-Dcom.google.devtools.build.lib.util.LogHandlerQuerier.class=" | 
|  | "com.google.devtools.build.lib.util.SimpleLogHandler$HandlerQuerier"); | 
|  | } | 
|  | } | 
|  |  | 
|  | blaze_exit_code::ExitCode StartupOptions::AddJVMMemoryArguments( | 
|  | const blaze_util::Path &, std::vector<string> *, const vector<string> &, | 
|  | string *) const { | 
|  | return blaze_exit_code::SUCCESS; | 
|  | } | 
|  |  | 
|  | }  // namespace blaze |