aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools
diff options
context:
space:
mode:
authorGravatar Yun Peng <pcloudy@google.com>2017-08-24 17:40:22 +0200
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2017-08-25 12:53:08 +0200
commit54c5c5cf41bd47d11ea309f37697d83ae65fe9e7 (patch)
tree181e37a31a457de9989f79199bd98d046e85830c /src/tools
parent5371d134cfaf3c258f9ebb606a5cb061f419613e (diff)
Windows: Implement Java native launcher
Now Bazel build a Windows exe binary to launch JVM for java_binary and java_test. The Java native launcher is implemented with the same logic and functionalities as the original java shell stub script. Change-Id: Ida40579bce82425f3506f9376b7256aa3edc265e PiperOrigin-RevId: 166346445
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/launcher/bash_launcher.cc2
-rw-r--r--src/tools/launcher/bash_launcher.h2
-rw-r--r--src/tools/launcher/java_launcher.cc276
-rw-r--r--src/tools/launcher/java_launcher.h66
-rw-r--r--src/tools/launcher/launcher.cc40
-rw-r--r--src/tools/launcher/launcher.h24
-rw-r--r--src/tools/launcher/python_launcher.cc2
-rw-r--r--src/tools/launcher/python_launcher.h2
-rw-r--r--src/tools/launcher/util/launcher_util.cc26
-rw-r--r--src/tools/launcher/util/launcher_util.h14
-rw-r--r--src/tools/launcher/util/launcher_util_test.cc18
11 files changed, 459 insertions, 13 deletions
diff --git a/src/tools/launcher/bash_launcher.cc b/src/tools/launcher/bash_launcher.cc
index 3e41ac152a..01d9511c74 100644
--- a/src/tools/launcher/bash_launcher.cc
+++ b/src/tools/launcher/bash_launcher.cc
@@ -26,6 +26,8 @@ using std::ostringstream;
using std::string;
using std::vector;
+static constexpr const char* BASH_BIN_PATH = "bash_bin_path";
+
ExitCode BashBinaryLauncher::Launch() {
string bash_binary = this->GetLaunchInfoByKey(BASH_BIN_PATH);
// If specified bash binary path doesn't exist, then fall back to
diff --git a/src/tools/launcher/bash_launcher.h b/src/tools/launcher/bash_launcher.h
index 659c144c39..758735561e 100644
--- a/src/tools/launcher/bash_launcher.h
+++ b/src/tools/launcher/bash_launcher.h
@@ -20,8 +20,6 @@
namespace bazel {
namespace launcher {
-static constexpr const char* BASH_BIN_PATH = "bash_bin_path";
-
class BashBinaryLauncher : public BinaryLauncherBase {
public:
BashBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc,
diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc
index ce626d347e..b8c0bdbd43 100644
--- a/src/tools/launcher/java_launcher.cc
+++ b/src/tools/launcher/java_launcher.cc
@@ -12,14 +12,286 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <sstream>
+#include <string>
+#include <vector>
+
#include "src/tools/launcher/java_launcher.h"
+#include "src/tools/launcher/util/launcher_util.h"
namespace bazel {
namespace launcher {
+using std::getline;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+// The runfile path of java binary, eg. local_jdk/bin/java.exe
+static constexpr const char* JAVA_BIN_PATH = "java_bin_path";
+static constexpr const char* JAR_BIN_PATH = "jar_bin_path";
+static constexpr const char* CLASSPATH = "classpath";
+static constexpr const char* JAVA_START_CLASS = "java_start_class";
+static constexpr const char* JVM_FLAGS = "jvm_flags";
+
+// Check if a string start with a certain prefix.
+// If it's true, store the substring without the prefix in value.
+// If value is quoted, then remove the quotes.
+static bool GetFlagValue(const string& str, const string& prefix,
+ string* value_ptr) {
+ if (str.compare(0, prefix.length(), prefix)) {
+ return false;
+ }
+ string& value = *value_ptr;
+ value = str.substr(prefix.length());
+ int len = value.length();
+ if (len >= 2 && value[0] == '"' && value[len - 1] == '"') {
+ value = value.substr(1, len - 2);
+ }
+ return true;
+}
+
+// Parses one launcher flag and updates this object's state accordingly.
+//
+// Returns true if the flag is a valid launcher flag; false otherwise.
+bool JavaBinaryLauncher::ProcessWrapperArgument(const string& argument) {
+ string flag_value;
+ if (argument.compare("--debug") == 0) {
+ string default_jvm_debug_port;
+ if (GetEnv("DEFAULT_JVM_DEBUG_PORT", &default_jvm_debug_port)) {
+ this->jvm_debug_port = default_jvm_debug_port;
+ } else {
+ this->jvm_debug_port = "5005";
+ }
+ } else if (GetFlagValue(argument, "--debug=", &flag_value)) {
+ this->jvm_debug_port = flag_value;
+ } else if (GetFlagValue(argument, "--main_advice=", &flag_value)) {
+ this->main_advice = flag_value;
+ } else if (GetFlagValue(argument, "--main_advice_classpath=", &flag_value)) {
+ this->main_advice_classpath = flag_value;
+ } else if (GetFlagValue(argument, "--jvm_flag=", &flag_value)) {
+ this->jvm_flags_cmdline.push_back(flag_value);
+ } else if (GetFlagValue(argument, "--jvm_flags=", &flag_value)) {
+ stringstream flag_value_ss(flag_value);
+ string item;
+ while (getline(flag_value_ss, item, ' ')) {
+ this->jvm_flags_cmdline.push_back(item);
+ }
+ } else if (argument.compare("--singlejar") == 0) {
+ this->singlejar = true;
+ } else if (argument.compare("--print_javabin") == 0) {
+ this->print_javabin = true;
+ } else if (GetFlagValue(argument, "--classpath_limit=", &flag_value)) {
+ this->classpath_limit = std::stoi(flag_value);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+vector<string> JavaBinaryLauncher::ProcessesCommandLine() {
+ vector<string> args;
+ bool first = 1;
+ for (const auto& arg : this->GetCommandlineArguments()) {
+ // Skip the first arugment.
+ if (first) {
+ first = 0;
+ continue;
+ }
+ string flag_value;
+ // TODO(pcloudy): Should rename this flag to --native_launcher_flag.
+ // But keep it as it is for now to be consistent with the shell script
+ // launcher.
+ if (GetFlagValue(arg, "--wrapper_script_flag=", &flag_value)) {
+ if (!ProcessWrapperArgument(flag_value)) {
+ die("invalid wrapper argument '%s'", arg);
+ }
+ } else if (!args.empty() || !ProcessWrapperArgument(arg)) {
+ args.push_back(arg);
+ }
+ }
+ return args;
+}
+
+string JavaBinaryLauncher::CreateClasspathJar(const string& classpath) {
+ static constexpr const char* URI_PREFIX = "file:/";
+ ostringstream manifest_classpath;
+ manifest_classpath << "Class-Path:";
+ stringstream classpath_ss(classpath);
+ string path;
+ while (getline(classpath_ss, path, ';')) {
+ manifest_classpath << ' ';
+ manifest_classpath << URI_PREFIX;
+ for (const auto& x : path) {
+ if (x == ' ') {
+ manifest_classpath << "%20";
+ } else {
+ manifest_classpath << x;
+ }
+ }
+ }
+
+ string binary_base_path =
+ GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]);
+ string jar_manifest_file_path = binary_base_path + ".jar_manifest";
+ ofstream jar_manifest_file(jar_manifest_file_path);
+ jar_manifest_file << "Manifest-Version: 1.0\n";
+ // No line in the MANIFEST.MF file may be longer than 72 bytes.
+ // A space prefix indicates the line is still the content of the last
+ // attribute.
+ string manifest_classpath_str = manifest_classpath.str();
+ for (size_t i = 0; i < manifest_classpath_str.length(); i += 71) {
+ if (i > 0) {
+ jar_manifest_file << " ";
+ }
+ jar_manifest_file << manifest_classpath_str.substr(i, 71) << "\n";
+ }
+ jar_manifest_file.close();
+
+ // Create the command for generating classpath jar.
+ // We pass the command to cmd.exe to use redirection for suppressing output.
+ string manifest_jar_path = binary_base_path + "-classpath.jar";
+ string jar_bin = this->Rlocation(this->GetLaunchInfoByKey(JAR_BIN_PATH),
+ /*need_workspace_name =*/false);
+ vector<string> arguments;
+ arguments.push_back("/c");
+
+ ostringstream jar_command;
+ jar_command << jar_bin << " cvfm ";
+ jar_command << manifest_jar_path << " ";
+ jar_command << jar_manifest_file_path << " ";
+ jar_command << "> nul";
+ arguments.push_back(jar_command.str());
+
+ if (this->LaunchProcess("cmd.exe", arguments) != 0) {
+ die("Couldn't create classpath jar: %s", manifest_jar_path.c_str());
+ }
+
+ return manifest_jar_path;
+}
+
ExitCode JavaBinaryLauncher::Launch() {
- // TODO(pcloudy): Implement Java launcher
- return 0;
+ // Parse the original command line.
+ vector<string> remaining_args = this->ProcessesCommandLine();
+
+ // Set JAVA_RUNFILES
+ string java_runfiles;
+ if (!GetEnv("JAVA_RUNFILES", &java_runfiles)) {
+ java_runfiles = this->GetRunfilesPath();
+ }
+ SetEnv("JAVA_RUNFILES", java_runfiles);
+
+ // Print Java binary path if needed
+ string java_bin = this->Rlocation(this->GetLaunchInfoByKey(JAVA_BIN_PATH),
+ /*need_workspace_name =*/false);
+ if (this->print_javabin ||
+ this->GetLaunchInfoByKey(JAVA_START_CLASS) == "--print_javabin") {
+ printf("%s\n", java_bin.c_str());
+ return 0;
+ }
+
+ ostringstream classpath;
+
+ // Run deploy jar if needed, otherwise generate the CLASSPATH by rlocation.
+ if (this->singlejar) {
+ string deploy_jar =
+ GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]) +
+ "_deploy.jar";
+ if (!DoesFilePathExist(deploy_jar.c_str())) {
+ die("Option --singlejar was passed, but %s does not exist.\n (You may "
+ "need to build it explicitly.)",
+ deploy_jar.c_str());
+ }
+ classpath << deploy_jar << ';';
+ } else {
+ // Add main advice classpath if exists
+ if (!this->main_advice_classpath.empty()) {
+ classpath << this->main_advice_classpath << ';';
+ }
+ string path;
+ stringstream classpath_ss(this->GetLaunchInfoByKey(CLASSPATH));
+ while (getline(classpath_ss, path, ';')) {
+ classpath << this->Rlocation(path) << ';';
+ }
+ }
+
+ // Set jvm debug options
+ ostringstream jvm_debug_flags;
+ if (!this->jvm_debug_port.empty()) {
+ string jvm_debug_suspend;
+ if (!GetEnv("DEFAULT_JVM_DEBUG_SUSPEND", &jvm_debug_suspend)) {
+ jvm_debug_suspend = "y";
+ }
+ jvm_debug_flags << "-agentlib:jdwp=transport=dt_socket,server=y";
+ jvm_debug_flags << ",suspend=" << jvm_debug_suspend;
+ jvm_debug_flags << ",address=" << jvm_debug_port;
+
+ string value;
+ if (GetEnv("PERSISTENT_TEST_RUNNER", &value) && value == "true") {
+ jvm_debug_flags << ",quiet=y";
+ }
+ }
+
+ // Get jvm flags from JVM_FLAGS environment variable and JVM_FLAGS launch info
+ vector<string> jvm_flags;
+ string jvm_flags_env;
+ GetEnv("JVM_FLAGS", &jvm_flags_env);
+ string flag;
+ stringstream jvm_flags_env_ss(jvm_flags_env);
+ while (getline(jvm_flags_env_ss, flag, ' ')) {
+ jvm_flags.push_back(flag);
+ }
+ stringstream jvm_flags_launch_info_ss(this->GetLaunchInfoByKey(JVM_FLAGS));
+ while (getline(jvm_flags_launch_info_ss, flag, ' ')) {
+ jvm_flags.push_back(flag);
+ }
+
+ // Check if TEST_TMPDIR is available to use for scratch.
+ string test_tmpdir;
+ if (GetEnv("TEST_TMPDIR", &test_tmpdir) &&
+ DoesDirectoryPathExist(test_tmpdir.c_str())) {
+ jvm_flags.push_back("-Djava.io.tmpdir=" + test_tmpdir);
+ }
+
+ // Construct the final command line arguments
+ vector<string> arguments;
+ // Add classpath flags
+ arguments.push_back("-classpath");
+ // Check if CLASSPATH is over classpath length limit.
+ // If it does, then we create a classpath jar to pass CLASSPATH value.
+ string classpath_str = classpath.str();
+ if (classpath_str.length() > this->classpath_limit) {
+ arguments.push_back(CreateClasspathJar(classpath_str));
+ } else {
+ arguments.push_back(classpath_str);
+ }
+ // Add JVM debug flags
+ string jvm_debug_flags_str = jvm_debug_flags.str();
+ if (!jvm_debug_flags_str.empty()) {
+ arguments.push_back(jvm_debug_flags_str);
+ }
+ // Add JVM flags parsed from env and launch info.
+ for (const auto& arg : jvm_flags) {
+ arguments.push_back(arg);
+ }
+ // Add JVM flags parsed from command line.
+ for (const auto& arg : this->jvm_flags_cmdline) {
+ arguments.push_back(arg);
+ }
+ // Add main advice class
+ if (!this->main_advice.empty()) {
+ arguments.push_back(this->main_advice);
+ }
+ // Add java start class
+ arguments.push_back(this->GetLaunchInfoByKey(JAVA_START_CLASS));
+ // Add the remaininng arguements, they will be passed to the program.
+ for (const auto& arg : remaining_args) {
+ arguments.push_back(arg);
+ }
+
+ return this->LaunchProcess(java_bin, arguments);
}
} // namespace launcher
diff --git a/src/tools/launcher/java_launcher.h b/src/tools/launcher/java_launcher.h
index 5ffe5881ab..896aacd74a 100644
--- a/src/tools/launcher/java_launcher.h
+++ b/src/tools/launcher/java_launcher.h
@@ -15,17 +15,81 @@
#ifndef BAZEL_SRC_TOOLS_LAUNCHER_JAVA_LAUNCHER_H_
#define BAZEL_SRC_TOOLS_LAUNCHER_JAVA_LAUNCHER_H_
+#include <string>
+#include <vector>
+
#include "src/tools/launcher/launcher.h"
namespace bazel {
namespace launcher {
+// Windows per-arg limit is MAX_ARG_STRLEN == 8k,
+// here we use a slightly smaller value.
+static const int MAX_ARG_STRLEN = 7000;
+
class JavaBinaryLauncher : public BinaryLauncherBase {
public:
JavaBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc,
char* argv[])
- : BinaryLauncherBase(launch_info, argc, argv){}
+ : BinaryLauncherBase(launch_info, argc, argv),
+ singlejar(false),
+ print_javabin(false),
+ classpath_limit(MAX_ARG_STRLEN) {}
ExitCode Launch();
+
+ private:
+ // If present, these flags should either be at the beginning of the command
+ // line, or they should be wrapped in a --wrapper_script_flag=FLAG argument.
+ //
+ // --debug Launch the JVM in remote debugging mode listening
+ // --debug=<port> to the specified port or the port set in the
+ // DEFAULT_JVM_DEBUG_PORT environment variable (e.g.
+ // 'export DEFAULT_JVM_DEBUG_PORT=8000') or else the
+ // default port of 5005. The JVM starts suspended
+ // unless the DEFAULT_JVM_DEBUG_SUSPEND environment
+ // variable is set to 'n'.
+ // --main_advice=<class> Run an alternate main class with the usual main
+ // program and arguments appended as arguments.
+ // --main_advice_classpath=<classpath>
+ // Prepend additional class path entries.
+ // --jvm_flag=<flag> Pass <flag> to the "java" command itself.
+ // <flag> may contain spaces. Can be used multiple
+ // times.
+ // --jvm_flags=<flags> Pass space-separated flags to the "java" command
+ // itself. Can be used multiple times.
+ // --singlejar Start the program from the packed-up deployment
+ // jar rather than from the classpath.
+ // --print_javabin Print the location of java executable binary and
+ // exit.
+ // --classpath_limit=<length>
+ // Specify the maximum classpath length. If the
+ // classpath is shorter, this script passes it to Java
+ // as a command line flag, otherwise it creates a
+ // classpath jar.
+ //
+ // The remainder of the command line is passed to the program.
+ bool ProcessWrapperArgument(const std::string& argument);
+
+ // Parse arguments sequentially until the first unrecognized arg is
+ // encountered. Scan the remaining args for --wrapper_script_flag=X options
+ // and process them.
+ //
+ // Return the remaining arguments that should be passed to the program.
+ std::vector<std::string> ProcessesCommandLine();
+
+ std::string jvm_debug_port;
+ std::string main_advice;
+ std::string main_advice_classpath;
+ std::vector<std::string> jvm_flags_cmdline;
+ bool singlejar;
+ bool print_javabin;
+ int classpath_limit;
+
+ // Create a classpath jar to pass CLASSPATH value when its length is over
+ // limit.
+ //
+ // Return the path of the classpath jar created.
+ std::string CreateClasspathJar(const std::string& classpath);
};
} // namespace launcher
diff --git a/src/tools/launcher/launcher.cc b/src/tools/launcher/launcher.cc
index 8e82542fb4..b981761da6 100644
--- a/src/tools/launcher/launcher.cc
+++ b/src/tools/launcher/launcher.cc
@@ -67,6 +67,13 @@ string BinaryLauncherBase::FindManifestFile(const char* argv0) {
die("Couldn't find MANIFEST file under %s.runfiles\\", binary.c_str());
}
+string BinaryLauncherBase::GetRunfilesPath() const {
+ string runfiles_path =
+ GetBinaryPathWithExtension(this->commandline_arguments[0]) + ".runfiles";
+ std::replace(runfiles_path.begin(), runfiles_path.end(), '/', '\\');
+ return runfiles_path;
+}
+
void BinaryLauncherBase::ParseManifestFile(ManifestFileMap* manifest_file_map,
const string& manifest_path) {
// TODO(laszlocsomor): prefix manifest_path with the longpath prefix.
@@ -90,11 +97,16 @@ void BinaryLauncherBase::ParseManifestFile(ManifestFileMap* manifest_file_map,
}
}
-string BinaryLauncherBase::Rlocation(const string& path) const {
- auto entry = manifest_file_map.find(this->workspace_name + "/" + path);
+string BinaryLauncherBase::Rlocation(const string& path,
+ bool need_workspace_name) const {
+ string query_path = path;
+ if (need_workspace_name) {
+ query_path = this->workspace_name + "/" + path;
+ }
+ auto entry = manifest_file_map.find(query_path);
if (entry == manifest_file_map.end()) {
die("Rlocation failed on %s, path doesn't exist in MANIFEST file",
- path.c_str());
+ query_path.c_str());
}
return entry->second;
}
@@ -132,10 +144,28 @@ void BinaryLauncherBase::CreateCommandLine(
result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0;
}
+bool BinaryLauncherBase::PrintLauncherCommandLine(
+ const string& executable, const vector<string>& arguments) const {
+ bool has_print_cmd_flag = false;
+ for (const auto& arg : arguments) {
+ has_print_cmd_flag |= (arg == "--print_launcher_command");
+ }
+ if (has_print_cmd_flag) {
+ printf("%s\n", executable.c_str());
+ for (const auto& arg : arguments) {
+ printf("%s\n", arg.c_str());
+ }
+ }
+ return has_print_cmd_flag;
+}
+
ExitCode BinaryLauncherBase::LaunchProcess(
const string& executable, const vector<string>& arguments) const {
- SetEnvironmentVariableA("RUNFILES_MANIFEST_ONLY", "1");
- SetEnvironmentVariableA("RUNFILES_MANIFEST_FILE", manifest_file.c_str());
+ if (PrintLauncherCommandLine(executable, arguments)) {
+ return 0;
+ }
+ SetEnv("RUNFILES_MANIFEST_ONLY", "1");
+ SetEnv("RUNFILES_MANIFEST_FILE", manifest_file);
CmdLine cmdline;
CreateCommandLine(&cmdline, executable, arguments);
PROCESS_INFORMATION processInfo = {0};
diff --git a/src/tools/launcher/launcher.h b/src/tools/launcher/launcher.h
index 3ba8ca4723..1915b36455 100644
--- a/src/tools/launcher/launcher.h
+++ b/src/tools/launcher/launcher.h
@@ -49,9 +49,17 @@ class BinaryLauncherBase {
const std::vector<std::string>& GetCommandlineArguments() const;
// Map a runfile path to its absolute path.
- std::string Rlocation(const std::string& path) const;
+ //
+ // If need_workspace_name is true, then this method prepend workspace name to
+ // path before doing rlocation.
+ // If need_workspace_name is false, then this method uses path directly.
+ // The default value of need_workspace_name is true.
+ std::string Rlocation(const std::string& path,
+ bool need_workspace_name = true) const;
// Lauch a process with given executable and command line arguments.
+ // If --print_launcher_command exists in arguments, then we print the full
+ // command line instead of launching the real process.
//
// exectuable: the binary to be executed.
// arguments: the command line arguments to be passed to the exectuable,
@@ -62,6 +70,12 @@ class BinaryLauncherBase {
// A launch function to be implemented for a specific language.
virtual ExitCode Launch() = 0;
+ // Return the runfiles directory of this binary.
+ //
+ // The method appends ".exe.runfiles" to the first command line argument,
+ // converts forward slashes to back slashes, then returns that.
+ std::string GetRunfilesPath() const;
+
private:
// A map to store all the launch information.
const LaunchDataParser::LaunchInfo& launch_info;
@@ -79,6 +93,14 @@ class BinaryLauncherBase {
// A map to store all entries of the manifest file.
std::unordered_map<std::string, std::string> manifest_file_map;
+ // If --print_launcher_command is presented in arguments,
+ // then print the command line.
+ //
+ // Return true if command line is printed.
+ bool PrintLauncherCommandLine(
+ const std::string& executable,
+ const std::vector<std::string>& arguments) const;
+
// Create a command line to be passed to Windows CreateProcessA API.
//
// exectuable: the binary to be executed.
diff --git a/src/tools/launcher/python_launcher.cc b/src/tools/launcher/python_launcher.cc
index 5131243a69..b5e65f9187 100644
--- a/src/tools/launcher/python_launcher.cc
+++ b/src/tools/launcher/python_launcher.cc
@@ -24,6 +24,8 @@ namespace launcher {
using std::string;
using std::vector;
+static constexpr const char* PYTHON_BIN_PATH = "python_bin_path";
+
ExitCode PythonBinaryLauncher::Launch() {
string python_binary = this->GetLaunchInfoByKey(PYTHON_BIN_PATH);
// If specified python binary path doesn't exist, then fall back to
diff --git a/src/tools/launcher/python_launcher.h b/src/tools/launcher/python_launcher.h
index d909ea6bcd..1a21ca308e 100644
--- a/src/tools/launcher/python_launcher.h
+++ b/src/tools/launcher/python_launcher.h
@@ -20,8 +20,6 @@
namespace bazel {
namespace launcher {
-static constexpr const char* PYTHON_BIN_PATH = "python_bin_path";
-
class PythonBinaryLauncher : public BinaryLauncherBase {
public:
PythonBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info,
diff --git a/src/tools/launcher/util/launcher_util.cc b/src/tools/launcher/util/launcher_util.cc
index 247c7050c5..bc3eddb284 100644
--- a/src/tools/launcher/util/launcher_util.cc
+++ b/src/tools/launcher/util/launcher_util.cc
@@ -76,6 +76,15 @@ bool DoesFilePathExist(const char* path) {
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
+bool DoesDirectoryPathExist(const char* path) {
+ // TODO(laszlocsomor): convert `path` to (const wchar_t*), add longpath-prefix
+ // and use GetFileAttributesW.
+ DWORD dwAttrib = GetFileAttributesA(path);
+
+ return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
+ (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+}
+
string GetBinaryPathWithoutExtension(const string& binary) {
if (binary.find(".exe", binary.size() - 4) != string::npos) {
return binary.substr(0, binary.length() - 4);
@@ -120,5 +129,22 @@ string GetEscapedArgument(const string& argument) {
return escaped_arg.str();
}
+// An environment variable has a maximum size limit of 32,767 characters
+// https://msdn.microsoft.com/en-us/library/ms683188.aspx
+static const int BUFFER_SIZE = 32767;
+
+bool GetEnv(const string& env_name, string* value) {
+ char buffer[BUFFER_SIZE];
+ if (!GetEnvironmentVariableA(env_name.c_str(), buffer, BUFFER_SIZE)) {
+ return false;
+ }
+ *value = buffer;
+ return true;
+}
+
+bool SetEnv(const string& env_name, const string& value) {
+ return SetEnvironmentVariableA(env_name.c_str(), value.c_str());
+}
+
} // namespace launcher
} // namespace bazel
diff --git a/src/tools/launcher/util/launcher_util.h b/src/tools/launcher/util/launcher_util.h
index 669ea1dbfc..e5583006c0 100644
--- a/src/tools/launcher/util/launcher_util.h
+++ b/src/tools/launcher/util/launcher_util.h
@@ -50,6 +50,20 @@ std::string GetEscapedArgument(const std::string& argument);
// Check if a file exists at a given path.
bool DoesFilePathExist(const char* path);
+// Check if a directory exists at a given path.
+bool DoesDirectoryPathExist(const char* path);
+
+// Get the value of a specific environment variable
+//
+// Return true if succeeded and the result is stored in buffer.
+// Return false if the environment variable doesn't exist or the value is empty.
+bool GetEnv(const std::string& env_name, std::string* buffer);
+
+// Set the value of a specific environment variable
+//
+// Return true if succeeded, otherwise false.
+bool SetEnv(const std::string& env_name, const std::string& value);
+
} // namespace launcher
} // namespace bazel
diff --git a/src/tools/launcher/util/launcher_util_test.cc b/src/tools/launcher/util/launcher_util_test.cc
index b895b65c9c..9d03849a83 100644
--- a/src/tools/launcher/util/launcher_util_test.cc
+++ b/src/tools/launcher/util/launcher_util_test.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <windows.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
@@ -90,5 +91,22 @@ TEST_F(LaunchUtilTest, DoesFilePathExistTest) {
ASSERT_FALSE(DoesFilePathExist(file2.c_str()));
}
+TEST_F(LaunchUtilTest, DoesDirectoryPathExistTest) {
+ string dir1 = GetTmpDir() + "/dir1";
+ string dir2 = GetTmpDir() + "/dir2";
+ CreateDirectory(dir1.c_str(), NULL);
+ ASSERT_TRUE(DoesDirectoryPathExist(dir1.c_str()));
+ ASSERT_FALSE(DoesDirectoryPathExist(dir2.c_str()));
+}
+
+TEST_F(LaunchUtilTest, SetAndGetEnvTest) {
+ ASSERT_TRUE(SetEnv("foo", "bar"));
+ string value;
+ ASSERT_TRUE(GetEnv("foo", &value));
+ ASSERT_EQ(value, "bar");
+ SetEnv("FOO", "");
+ ASSERT_FALSE(GetEnv("FOO", &value));
+}
+
} // namespace launcher
} // namespace bazel