Windows: Implement native binary launcher

The native launcher is implemented based on this design doc:
https://docs.google.com/document/d/1z6Xv95CJYNYNYylcRklA6xBeesNLc54dqXfri0z0e14/edit?usp=sharing

Change-Id: I83bae844f792a587ce0e342a3c0f238b760afeaa
PiperOrigin-RevId: 162736269
diff --git a/src/BUILD b/src/BUILD
index d521294..3209cf0 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -377,6 +377,7 @@
         "//src/tools/android/java/com/google/devtools/build/android:srcs",
         "//src/tools/benchmark:srcs",
         "//src/tools/generate_workspace:srcs",
+        "//src/tools/launcher:srcs",
         "//src/tools/xcode/actoolwrapper:srcs",
         "//src/tools/xcode/environment:srcs",
         "//src/tools/xcode/ibtoolwrapper:srcs",
diff --git a/src/tools/launcher/BUILD b/src/tools/launcher/BUILD
new file mode 100644
index 0000000..d6617ce
--- /dev/null
+++ b/src/tools/launcher/BUILD
@@ -0,0 +1,53 @@
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]) + ["//src/tools/launcher/util:srcs"],
+    visibility = ["//src:__pkg__"],
+)
+
+cc_binary(
+    name = "launcher",
+    srcs = ["launcher_main.cc"],
+    visibility = [
+        "//src:__pkg__",
+        "//tools/launcher:__pkg__",
+    ],
+    deps = [
+        ":bash_launcher",
+        ":java_launcher",
+        ":launcher_base",
+        ":python_launcher",
+        "//src/tools/launcher/util",
+        "//src/tools/launcher/util:data_parser",
+    ],
+)
+
+cc_library(
+    name = "launcher_base",
+    srcs = ["launcher.cc"],
+    hdrs = ["launcher.h"],
+    deps = [
+        "//src/tools/launcher/util",
+        "//src/tools/launcher/util:data_parser",
+    ],
+)
+
+cc_library(
+    name = "java_launcher",
+    srcs = ["java_launcher.cc"],
+    hdrs = ["java_launcher.h"],
+    deps = [":launcher_base"],
+)
+
+cc_library(
+    name = "python_launcher",
+    srcs = ["python_launcher.cc"],
+    hdrs = ["python_launcher.h"],
+    deps = [":launcher_base"],
+)
+
+cc_library(
+    name = "bash_launcher",
+    srcs = ["bash_launcher.cc"],
+    hdrs = ["bash_launcher.h"],
+    deps = [":launcher_base"],
+)
diff --git a/src/tools/launcher/bash_launcher.cc b/src/tools/launcher/bash_launcher.cc
new file mode 100644
index 0000000..e1203a3
--- /dev/null
+++ b/src/tools/launcher/bash_launcher.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 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 <sstream>
+#include <string>
+#include <vector>
+
+#include "src/tools/launcher/bash_launcher.h"
+#include "src/tools/launcher/util/launcher_util.h"
+
+namespace bazel {
+namespace launcher {
+
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+ExitCode BashBinaryLauncher::Launch() {
+  string bash_binary = this->GetLaunchInfoByKey(BASH_BIN_PATH);
+  // If specified bash binary path doesn't exist, then fall back to
+  // bash.exe and hope it's in PATH.
+  if (!DoesFilePathExist(bash_binary)) {
+    bash_binary = "bash.exe";
+  }
+
+  vector<string> origin_args = this->GetCommandlineArguments();
+  ostringstream bash_command;
+  string bash_main_file =
+      this->Rlocation(this->GetLaunchInfoByKey(BASH_MAIN_FILE));
+  bash_command << GetEscapedArgument(bash_main_file);
+  for (int i = 1; i < origin_args.size(); i++) {
+    bash_command << ' ';
+    bash_command << GetEscapedArgument(origin_args[i]);
+  }
+
+  vector<string> args;
+  args.push_back("-c");
+  args.push_back(bash_command.str());
+  return this->LaunchProcess(bash_binary, args);
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/bash_launcher.h b/src/tools/launcher/bash_launcher.h
new file mode 100644
index 0000000..8070016
--- /dev/null
+++ b/src/tools/launcher/bash_launcher.h
@@ -0,0 +1,37 @@
+// Copyright 2017 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.
+
+#ifndef BAZEL_SRC_TOOLS_LAUNCHER_BASH_LAUNCHER_H_
+#define BAZEL_SRC_TOOLS_LAUNCHER_BASH_LAUNCHER_H_
+
+#include "src/tools/launcher/launcher.h"
+
+namespace bazel {
+namespace launcher {
+
+static constexpr const char* BASH_BIN_PATH = "bash_bin_path";
+static constexpr const char* BASH_MAIN_FILE = "bash_main_file";
+
+class BashBinaryLauncher : public BinaryLauncherBase {
+ public:
+  BashBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc,
+                     char* argv[])
+      : BinaryLauncherBase(launch_info, argc, argv){}
+  ExitCode Launch();
+};
+
+}  // namespace launcher
+}  // namespace bazel
+
+#endif  // BAZEL_SRC_TOOLS_LAUNCHER_BASH_LAUNCHER_H_
diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc
new file mode 100644
index 0000000..ce626d3
--- /dev/null
+++ b/src/tools/launcher/java_launcher.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 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/tools/launcher/java_launcher.h"
+
+namespace bazel {
+namespace launcher {
+
+ExitCode JavaBinaryLauncher::Launch() {
+  // TODO(pcloudy): Implement Java launcher
+  return 0;
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/java_launcher.h b/src/tools/launcher/java_launcher.h
new file mode 100644
index 0000000..5ffe588
--- /dev/null
+++ b/src/tools/launcher/java_launcher.h
@@ -0,0 +1,34 @@
+// Copyright 2017 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.
+
+#ifndef BAZEL_SRC_TOOLS_LAUNCHER_JAVA_LAUNCHER_H_
+#define BAZEL_SRC_TOOLS_LAUNCHER_JAVA_LAUNCHER_H_
+
+#include "src/tools/launcher/launcher.h"
+
+namespace bazel {
+namespace launcher {
+
+class JavaBinaryLauncher : public BinaryLauncherBase {
+ public:
+  JavaBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc,
+                     char* argv[])
+      : BinaryLauncherBase(launch_info, argc, argv){}
+  ExitCode Launch();
+};
+
+}  // namespace launcher
+}  // namespace bazel
+
+#endif  // BAZEL_SRC_TOOLS_LAUNCHER_JAVA_LAUNCHER_H_
diff --git a/src/tools/launcher/launcher.cc b/src/tools/launcher/launcher.cc
new file mode 100644
index 0000000..86e9d8d
--- /dev/null
+++ b/src/tools/launcher/launcher.cc
@@ -0,0 +1,158 @@
+// Copyright 2017 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 <windows.h>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "src/tools/launcher/launcher.h"
+#include "src/tools/launcher/util/data_parser.h"
+#include "src/tools/launcher/util/launcher_util.h"
+
+namespace bazel {
+namespace launcher {
+
+using std::ifstream;
+using std::ostringstream;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+BinaryLauncherBase::BinaryLauncherBase(
+    const LaunchDataParser::LaunchInfo& _launch_info, int argc, char* argv[])
+    : launch_info(_launch_info) {
+  this->workspace_name = GetLaunchInfoByKey(WORKSPACE_NAME);
+  for (int i = 0; i < argc; i++) {
+    this->commandline_arguments.push_back(argv[i]);
+  }
+  ParseManifestFile(&this->manifest_file_map, FindManifestFile());
+}
+
+string BinaryLauncherBase::FindManifestFile() const {
+  // Get the name of the binary
+  string binary = GetBinaryPathWithoutExtension(this->commandline_arguments[0]);
+
+  // Try to find <path to binary>.runfiles/MANIFEST
+  string manifest_file = binary + ".runfiles\\MANIFEST";
+  if (DoesFilePathExist(manifest_file)) {
+    return manifest_file;
+  }
+
+  // Also try to check if <path to binary>.runfiles_manifest exists
+  manifest_file = binary + ".runfiles_manifest";
+  if (DoesFilePathExist(manifest_file)) {
+    return manifest_file;
+  }
+
+  die("Couldn't find MANIFEST file %s.runfiles\\", binary.c_str());
+}
+
+void BinaryLauncherBase::ParseManifestFile(ManifestFileMap* manifest_file_map,
+                                           const string& manifest_path) {
+  ifstream manifest_file(manifest_path.c_str());
+
+  if (!manifest_file) {
+    die("Couldn't open MANIFEST file: %s", manifest_path.c_str());
+  }
+
+  string line;
+  while (getline(manifest_file, line)) {
+    size_t space_pos = line.find_first_of(' ');
+    if (space_pos == string::npos) {
+      die("Wrong MANIFEST format at line: %s", line.c_str());
+    }
+    string key = line.substr(0, space_pos);
+    string value = line.substr(space_pos + 1);
+    manifest_file_map->insert(make_pair(key, value));
+  }
+}
+
+string BinaryLauncherBase::Rlocation(const string& path) const {
+  auto entry = manifest_file_map.find(this->workspace_name + "/" + path);
+  if (entry == manifest_file_map.end()) {
+    die("Rlocation failed on %s, path doesn't exist in MANIFEST file",
+        path.c_str());
+  }
+  return entry->second;
+}
+
+string BinaryLauncherBase::GetLaunchInfoByKey(const string& key) {
+  auto item = launch_info.find(key);
+  if (item == launch_info.end()) {
+    die("Cannot find key \"%s\" from launch data.\n", key.c_str());
+  }
+  return item->second;
+}
+
+const vector<string>& BinaryLauncherBase::GetCommandlineArguments() const {
+  return this->commandline_arguments;
+}
+
+void BinaryLauncherBase::CreateCommandLine(
+    CmdLine* result, const string& executable,
+    const vector<string>& arguments) const {
+  ostringstream cmdline;
+  cmdline << '\"' << executable << '\"';
+  bool first = true;
+  for (const auto& s : arguments) {
+    cmdline << ' ' << GetEscapedArgument(s);
+  }
+
+  string cmdline_str = cmdline.str();
+  if (cmdline_str.size() >= MAX_CMDLINE_LENGTH) {
+    die("Command line too long: %s", cmdline_str.c_str());
+  }
+
+  // Copy command line into a mutable buffer.
+  // CreateProcess is allowed to mutate its command line argument.
+  strncpy(result->cmdline, cmdline_str.c_str(), MAX_CMDLINE_LENGTH - 1);
+  result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0;
+}
+
+ExitCode BinaryLauncherBase::LaunchProcess(
+    const string& executable, const vector<string>& arguments) const {
+  CmdLine cmdline;
+  CreateCommandLine(&cmdline, executable, arguments);
+  PROCESS_INFORMATION processInfo = {0};
+  STARTUPINFOA startupInfo = {0};
+  startupInfo.cb = sizeof(startupInfo);
+  BOOL ok = CreateProcessA(
+      /* lpApplicationName */ NULL,
+      /* lpCommandLine */ cmdline.cmdline,
+      /* lpProcessAttributes */ NULL,
+      /* lpThreadAttributes */ NULL,
+      /* bInheritHandles */ FALSE,
+      /* dwCreationFlags */ 0,
+      /* lpEnvironment */ NULL,
+      /* lpCurrentDirectory */ NULL,
+      /* lpStartupInfo */ &startupInfo,
+      /* lpProcessInformation */ &processInfo);
+  if (!ok) {
+    PrintError("Cannot launch process:\n%s", GetLastErrorString().c_str());
+    return GetLastError();
+  }
+  WaitForSingleObject(processInfo.hProcess, INFINITE);
+  ExitCode exit_code;
+  GetExitCodeProcess(processInfo.hProcess,
+                     reinterpret_cast<LPDWORD>(&exit_code));
+  CloseHandle(processInfo.hProcess);
+  CloseHandle(processInfo.hThread);
+  return exit_code;
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/launcher.h b/src/tools/launcher/launcher.h
new file mode 100644
index 0000000..e9f461b
--- /dev/null
+++ b/src/tools/launcher/launcher.h
@@ -0,0 +1,102 @@
+// Copyright 2017 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.
+
+#ifndef BAZEL_SRC_TOOLS_LAUNCHER_LAUNCHER_H_
+#define BAZEL_SRC_TOOLS_LAUNCHER_LAUNCHER_H_
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "src/tools/launcher/util/data_parser.h"
+
+namespace bazel {
+namespace launcher {
+
+typedef int32_t ExitCode;
+static constexpr const char* WORKSPACE_NAME = "workspace_name";
+
+// The maximum length of lpCommandLine is 32768 characters.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
+static const int MAX_CMDLINE_LENGTH = 32768;
+
+struct CmdLine {
+  char cmdline[MAX_CMDLINE_LENGTH];
+};
+
+class BinaryLauncherBase {
+  typedef std::unordered_map<std::string, std::string> ManifestFileMap;
+
+ public:
+  BinaryLauncherBase(const LaunchDataParser::LaunchInfo& launch_info, int argc,
+                     char* argv[]);
+
+  // Get launch information based on a launch info key.
+  std::string GetLaunchInfoByKey(const std::string& key);
+
+  // Get the original command line arguments passed to this binary.
+  const std::vector<std::string>& GetCommandlineArguments() const;
+
+  // Map a runfile path to its absolute path.
+  std::string Rlocation(const std::string& path) const;
+
+  // Lauch a process with given executable and command line arguments.
+  //
+  // exectuable: the binary to be executed.
+  // arguments:  the command line arguments to be passed to the exectuable,
+  //             it doesn't include the exectuable itself.
+  ExitCode LaunchProcess(const std::string& executable,
+                         const std::vector<std::string>& arguments) const;
+
+  // A launch function to be implemented for a specific language.
+  virtual ExitCode Launch() = 0;
+
+ private:
+  // A map to store all the launch information.
+  const LaunchDataParser::LaunchInfo& launch_info;
+
+  // The commandline arguments recieved.
+  // The first argument is the path of this launcher itself.
+  std::vector<std::string> commandline_arguments;
+
+  // The workspace name of the repository this target belongs to.
+  std::string workspace_name;
+
+  // A map to store all entries of the manifest file.
+  std::unordered_map<std::string, std::string> manifest_file_map;
+
+  // Create a command line to be passed to Windows CreateProcessA API.
+  //
+  // exectuable: the binary to be executed.
+  // arguments:  the command line arguments to be passed to the exectuable,
+  //             it doesn't include the exectuable itself.
+  void CreateCommandLine(CmdLine* result, const std::string& executable,
+                         const std::vector<std::string>& arguments) const;
+
+  // Find manifest file of the binary
+  //
+  // Expect the manifest file to be at
+  //    1. <path>/<to>/<binary>/<target_name>.runfiles/MANIFEST
+  // or 2. <path>/<to>/<binary>/<target_name>.runfiles_manifest
+  std::string FindManifestFile() const;
+
+  // Parse manifest file into a map
+  static void ParseManifestFile(ManifestFileMap* manifest_file_map,
+                                const std::string& manifest_path);
+};
+
+}  // namespace launcher
+}  // namespace bazel
+
+#endif  // BAZEL_SRC_TOOLS_LAUNCHER_LAUNCHER_H_
diff --git a/src/tools/launcher/launcher_main.cc b/src/tools/launcher/launcher_main.cc
new file mode 100644
index 0000000..6f9133b
--- /dev/null
+++ b/src/tools/launcher/launcher_main.cc
@@ -0,0 +1,63 @@
+// Copyright 2017 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 <memory>
+
+#include "src/tools/launcher/bash_launcher.h"
+#include "src/tools/launcher/java_launcher.h"
+#include "src/tools/launcher/launcher.h"
+#include "src/tools/launcher/python_launcher.h"
+#include "src/tools/launcher/util/data_parser.h"
+#include "src/tools/launcher/util/launcher_util.h"
+
+static constexpr const char* BINARY_TYPE = "binary_type";
+
+using bazel::launcher::BashBinaryLauncher;
+using bazel::launcher::BinaryLauncherBase;
+using bazel::launcher::GetBinaryPathWithExtension;
+using bazel::launcher::JavaBinaryLauncher;
+using bazel::launcher::LaunchDataParser;
+using bazel::launcher::PythonBinaryLauncher;
+using bazel::launcher::die;
+using std::make_unique;
+using std::unique_ptr;
+
+int main(int argc, char* argv[]) {
+  LaunchDataParser::LaunchInfo launch_info;
+
+  if (!LaunchDataParser::GetLaunchInfo(GetBinaryPathWithExtension(argv[0]),
+                                       &launch_info)) {
+    die("Failed to parse launch info.");
+  }
+
+  auto result = launch_info.find(BINARY_TYPE);
+  if (result == launch_info.end()) {
+    die("Cannot find key \"%s\" from launch data.", BINARY_TYPE);
+  }
+
+  unique_ptr<BinaryLauncherBase> binary_launcher;
+
+  if (result->second == "Python") {
+    binary_launcher =
+        make_unique<PythonBinaryLauncher>(launch_info, argc, argv);
+  } else if (result->second == "Bash") {
+    binary_launcher = make_unique<BashBinaryLauncher>(launch_info, argc, argv);
+  } else if (result->second == "Java") {
+    binary_launcher = make_unique<JavaBinaryLauncher>(launch_info, argc, argv);
+  } else {
+    die("Unknown binary type, cannot launch anything.");
+  }
+
+  return binary_launcher->Launch();
+}
diff --git a/src/tools/launcher/python_launcher.cc b/src/tools/launcher/python_launcher.cc
new file mode 100644
index 0000000..b5dded9
--- /dev/null
+++ b/src/tools/launcher/python_launcher.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 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/tools/launcher/python_launcher.h"
+
+namespace bazel {
+namespace launcher {
+
+ExitCode PythonBinaryLauncher::Launch() {
+  // TODO(pcloudy): Implement Python launcher
+  return 0;
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/python_launcher.h b/src/tools/launcher/python_launcher.h
new file mode 100644
index 0000000..1a21ca3
--- /dev/null
+++ b/src/tools/launcher/python_launcher.h
@@ -0,0 +1,34 @@
+// Copyright 2017 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.
+
+#ifndef BAZEL_SRC_TOOLS_LAUNCHER_PYTHON_LAUNCHER_H_
+#define BAZEL_SRC_TOOLS_LAUNCHER_PYTHON_LAUNCHER_H_
+
+#include "src/tools/launcher/launcher.h"
+
+namespace bazel {
+namespace launcher {
+
+class PythonBinaryLauncher : public BinaryLauncherBase {
+ public:
+  PythonBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info,
+                       int argc, char* argv[])
+      : BinaryLauncherBase(launch_info, argc, argv){}
+  ExitCode Launch();
+};
+
+}  // namespace launcher
+}  // namespace bazel
+
+#endif  // BAZEL_SRC_TOOLS_LAUNCHER_PYTHON_LAUNCHER_H_
diff --git a/src/tools/launcher/util/BUILD b/src/tools/launcher/util/BUILD
new file mode 100644
index 0000000..7ecee60
--- /dev/null
+++ b/src/tools/launcher/util/BUILD
@@ -0,0 +1,38 @@
+package(default_visibility = ["//src/tools/launcher:__subpackages__"])
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "data_parser",
+    srcs = ["data_parser.cc"],
+    hdrs = ["data_parser.h"],
+    deps = [":util"],
+)
+
+cc_library(
+    name = "util",
+    srcs = ["launcher_util.cc"],
+    hdrs = ["launcher_util.h"],
+)
+
+cc_test(
+    name = "util_test",
+    srcs = ["launcher_util_test.cc"],
+    deps = [
+        ":util",
+        "//third_party:gtest",
+    ],
+)
+
+cc_test(
+    name = "data_parser_test",
+    srcs = ["data_parser_test.cc"],
+    deps = [
+        ":data_parser",
+        "//third_party:gtest",
+    ],
+)
diff --git a/src/tools/launcher/util/data_parser.cc b/src/tools/launcher/util/data_parser.cc
new file mode 100644
index 0000000..98c8b1d
--- /dev/null
+++ b/src/tools/launcher/util/data_parser.cc
@@ -0,0 +1,105 @@
+// Copyright 2017 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 <fstream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "src/tools/launcher/util/data_parser.h"
+#include "src/tools/launcher/util/launcher_util.h"
+
+namespace bazel {
+namespace launcher {
+
+using std::ifstream;
+using std::ios;
+using std::make_unique;
+using std::string;
+using std::unique_ptr;
+
+int64_t LaunchDataParser::ReadDataSize(ifstream* binary) {
+  int64_t data_size;
+  binary->seekg(0 - sizeof(data_size), ios::end);
+  binary->read(reinterpret_cast<char*>(&data_size), sizeof(data_size));
+  return data_size;
+}
+
+void LaunchDataParser::ReadLaunchData(ifstream* binary, char* launch_data,
+                                      int64_t data_size) {
+  binary->seekg(0 - data_size - sizeof(data_size), ios::end);
+  binary->read(launch_data, data_size);
+}
+
+bool LaunchDataParser::ParseLaunchData(LaunchInfo* launch_info,
+                                       const char* launch_data,
+                                       int64_t data_size) {
+  int64_t start, end, equal;
+  start = 0;
+  while (start < data_size) {
+    // Move start to point to the next non-null character.
+    while (launch_data[start] == '\0' && start < data_size) {
+      start++;
+    }
+    // Move end to the next null character or end of the string,
+    // also find the first equal symbol appears.
+    end = start;
+    equal = -1;
+    while (launch_data[end] != '\0' && end < data_size) {
+      if (equal == -1 && launch_data[end] == '=') {
+        equal = end;
+      }
+      end++;
+    }
+    if (equal == -1) {
+      PrintError("Cannot find equal symbol in line: %s",
+                 string(launch_data + start, end - start).c_str());
+      return false;
+    } else if (start == equal) {
+      PrintError("Key is empty string in line: %s",
+                 string(launch_data + start, end - start).c_str());
+      return false;
+    } else {
+      string key(launch_data + start, equal - start);
+      string value(launch_data + equal + 1, end - equal - 1);
+      if (launch_info->find(key) != launch_info->end()) {
+        PrintError("Duplicated launch info key: %s", key.c_str());
+        return false;
+      }
+      launch_info->insert(make_pair(key, value));
+    }
+    start = end + 1;
+  }
+  return true;
+}
+
+bool LaunchDataParser::GetLaunchInfo(const string& binary_path,
+                                     LaunchInfo* launch_info) {
+  unique_ptr<ifstream> binary =
+      make_unique<ifstream>(binary_path, ios::binary | ios::in);
+  int64_t data_size = ReadDataSize(binary.get());
+  if (data_size == 0) {
+    PrintError("No data appended, cannot launch anything!");
+    return false;
+  }
+  unique_ptr<char[]> launch_data(new char[data_size]);
+  ReadLaunchData(binary.get(), launch_data.get(), data_size);
+  if (!ParseLaunchData(launch_info, launch_data.get(), data_size)) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/util/data_parser.h b/src/tools/launcher/util/data_parser.h
new file mode 100644
index 0000000..ecaf12a
--- /dev/null
+++ b/src/tools/launcher/util/data_parser.h
@@ -0,0 +1,50 @@
+// Copyright 2017 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.
+
+#ifndef BAZEL_SRC_TOOLS_LAUNCHER_UTIL_DATA_PARSER_H_
+#define BAZEL_SRC_TOOLS_LAUNCHER_UTIL_DATA_PARSER_H_
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace bazel {
+namespace launcher {
+
+class LaunchDataParser {
+ public:
+  typedef std::unordered_map<std::string, std::string> LaunchInfo;
+  LaunchDataParser() = delete;
+  ~LaunchDataParser() = delete;
+  static bool GetLaunchInfo(const std::string& binary_path,
+                            LaunchInfo* launch_info);
+
+ private:
+  // Read the last 64 bit from the given binary to get the data size
+  static int64_t ReadDataSize(std::ifstream* binary);
+
+  // Read launch data at the end of the given binary into a buffer
+  static void ReadLaunchData(std::ifstream* binary, char* launch_data,
+                             int64_t data_size);
+
+  // Parse the launch data into a map
+  static bool ParseLaunchData(LaunchInfo* launch_info, const char* launch_data,
+                              int64_t data_size);
+};
+
+}  // namespace launcher
+}  // namespace bazel
+
+#endif  // BAZEL_SRC_TOOLS_LAUNCHER_UTIL_DATA_PARSER_H_
diff --git a/src/tools/launcher/util/data_parser_test.cc b/src/tools/launcher/util/data_parser_test.cc
new file mode 100644
index 0000000..1adaf3a
--- /dev/null
+++ b/src/tools/launcher/util/data_parser_test.cc
@@ -0,0 +1,189 @@
+// Copyright 2017 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 <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include "src/tools/launcher/util/data_parser.h"
+#include "gtest/gtest.h"
+#include "src/tools/launcher/util/launcher_util.h"
+
+namespace bazel {
+namespace launcher {
+
+using std::getenv;
+using std::ios;
+using std::make_unique;
+using std::ofstream;
+using std::pair;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+class LaunchDataParserTest : public ::testing::Test {
+ protected:
+  LaunchDataParserTest() {}
+
+  virtual ~LaunchDataParserTest() {}
+
+  void SetUp() override {
+    char* tmpdir = getenv("TEST_TMPDIR");
+    if (tmpdir != NULL) {
+      test_tmpdir = string(tmpdir);
+    } else {
+      tmpdir = getenv("TEMP");
+      ASSERT_FALSE(tmpdir == NULL);
+      test_tmpdir = string(tmpdir);
+    }
+  }
+
+  void TearDown() override {}
+
+  static void WriteBinaryFileWithList(const string& binary_file,
+                                      const vector<string>& launch_info) {
+    ofstream binary_file_stream(binary_file, ios::out | ios::binary);
+
+    int64_t data_size = 0;
+    for (auto const& entry : launch_info) {
+      binary_file_stream << entry;
+      binary_file_stream << '\0';
+      data_size += entry.length() + 1;
+    }
+
+    binary_file_stream.write(reinterpret_cast<char*>(&data_size),
+                             sizeof(data_size));
+  }
+
+  static void WriteBinaryFileWithMap(
+      const string& binary_file,
+      const vector<pair<string, string>>& launch_info) {
+    ofstream binary_file_stream(binary_file, ios::out | ios::binary);
+
+    int64_t data_size = 0;
+    for (auto const& entry : launch_info) {
+      binary_file_stream << entry.first;
+      binary_file_stream.put('=');
+      binary_file_stream << entry.second;
+      binary_file_stream.put('\0');
+      data_size += entry.first.length() + entry.second.length() + 2;
+    }
+
+    binary_file_stream.write(reinterpret_cast<char*>(&data_size),
+                             sizeof(data_size));
+  }
+
+  static bool ParseBinaryFile(
+      const string& binary_file,
+      LaunchDataParser::LaunchInfo* parsed_launch_info) {
+    if (LaunchDataParser::GetLaunchInfo(binary_file, parsed_launch_info)) {
+      return true;
+    }
+    exit(-1);
+  }
+
+  string GetLaunchInfo(const string& key) const {
+    auto item = parsed_launch_info->find(key);
+    if (item == parsed_launch_info->end()) {
+      return "Cannot find key: " + key;
+    }
+    return item->second;
+  }
+
+  string test_tmpdir;
+  unique_ptr<LaunchDataParser::LaunchInfo> parsed_launch_info;
+};
+
+TEST_F(LaunchDataParserTest, GetLaunchInfoTest) {
+  vector<pair<string, string>> launch_info = {
+      {"binary_type", "Bash"},
+      {"workspace_name", "__main__"},
+      {"bash_bin_path", "C:\\foo\\bar\\bash.exe"},
+      {"bash_main_file", "./bazel-bin/foo/bar/bin.sh"},
+      {"empty_value_key", ""},
+  };
+
+  string binary_file = test_tmpdir + "/binary_file";
+  WriteBinaryFileWithMap(binary_file, launch_info);
+
+  parsed_launch_info = make_unique<LaunchDataParser::LaunchInfo>();
+  ASSERT_TRUE(ParseBinaryFile(binary_file, parsed_launch_info.get()));
+
+  for (auto const& entry : launch_info) {
+    ASSERT_EQ(entry.second, GetLaunchInfo(entry.first));
+  }
+  ASSERT_EQ(GetLaunchInfo("no_such_key"), "Cannot find key: no_such_key");
+}
+
+TEST_F(LaunchDataParserTest, EmptyLaunchInfoTest) {
+  string binary_file = test_tmpdir + "/empty_binary_file";
+  WriteBinaryFileWithMap(binary_file, {});
+
+  parsed_launch_info = make_unique<LaunchDataParser::LaunchInfo>();
+  // ASSERT_DEATH requires TEMP environment variable to be set.
+  // Otherwise, it will try to write to C:/Windows, then fails.
+  // A workaround in Bazel is to use --action_env to set TEMP.
+  ASSERT_DEATH(ParseBinaryFile(binary_file, parsed_launch_info.get()),
+               "LAUNCHER ERROR: No data appended, cannot launch anything!");
+}
+
+TEST_F(LaunchDataParserTest, DuplicatedLaunchInfoTest) {
+  string binary_file = test_tmpdir + "/duplicated_binary_file";
+  WriteBinaryFileWithMap(binary_file, {
+                                          {"foo", "bar1"},
+                                          {"foo", "bar2"},
+                                      });
+
+  parsed_launch_info = make_unique<LaunchDataParser::LaunchInfo>();
+  // ASSERT_DEATH requires TEMP environment variable to be set.
+  // Otherwise, it will try to write to C:/Windows, then fails.
+  // A workaround in Bazel is to use --action_env to set TEMP.
+  ASSERT_DEATH(ParseBinaryFile(binary_file, parsed_launch_info.get()),
+               "LAUNCHER ERROR: Duplicated launch info key: foo");
+}
+
+TEST_F(LaunchDataParserTest, EmptyKeyLaunchInfoTest) {
+  string binary_file = test_tmpdir + "/empty_key_binary_file";
+  WriteBinaryFileWithMap(binary_file, {
+                                          {"foo", "bar"},
+                                          {"", "bar2"},
+                                      });
+
+  parsed_launch_info = make_unique<LaunchDataParser::LaunchInfo>();
+  // ASSERT_DEATH requires TEMP environment variable to be set.
+  // Otherwise, it will try to write to C:/Windows, then fails.
+  // A workaround in Bazel is to use --action_env to set TEMP.
+  ASSERT_DEATH(ParseBinaryFile(binary_file, parsed_launch_info.get()),
+               "LAUNCHER ERROR: Key is empty string in line: =bar2");
+}
+
+TEST_F(LaunchDataParserTest, NoEqualSignLaunchInfoTest) {
+  string binary_file = test_tmpdir + "/no_equal_binary_file";
+  WriteBinaryFileWithList(binary_file, {
+                                           "foo1=bar1",
+                                           "foo2bar2",
+                                       });
+
+  parsed_launch_info = make_unique<LaunchDataParser::LaunchInfo>();
+  // ASSERT_DEATH requires TEMP environment variable to be set.
+  // Otherwise, it will try to write to C:/Windows, then fails.
+  // A workaround in Bazel is to use --action_env to set TEMP.
+  ASSERT_DEATH(ParseBinaryFile(binary_file, parsed_launch_info.get()),
+               "LAUNCHER ERROR: Cannot find equal symbol in line: foo2bar2");
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/util/launcher_util.cc b/src/tools/launcher/util/launcher_util.cc
new file mode 100644
index 0000000..4ef64ea
--- /dev/null
+++ b/src/tools/launcher/util/launcher_util.cc
@@ -0,0 +1,122 @@
+// Copyright 2017 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <sstream>
+#include <string>
+
+#include "src/tools/launcher/util/launcher_util.h"
+
+namespace bazel {
+namespace launcher {
+
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+
+string GetLastErrorString() {
+  DWORD last_error = GetLastError();
+  if (last_error == 0) {
+    return string();
+  }
+
+  char* message_buffer;
+  size_t size = FormatMessageA(
+      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+          FORMAT_MESSAGE_IGNORE_INSERTS,
+      NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+      (LPSTR)&message_buffer, 0, NULL);
+
+  stringstream result;
+  result << "(error: " << last_error << "): " << message_buffer;
+  LocalFree(message_buffer);
+  return result.str();
+}
+
+void die(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  fputs("LAUNCHER ERROR: ", stderr);
+  vfprintf(stderr, format, ap);
+  va_end(ap);
+  fputc('\n', stderr);
+  exit(1);
+}
+
+void PrintError(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  fputs("LAUNCHER ERROR: ", stderr);
+  vfprintf(stderr, format, ap);
+  va_end(ap);
+  fputc('\n', stderr);
+}
+
+bool DoesFilePathExist(const string& path) {
+  DWORD dwAttrib = GetFileAttributes(path.c_str());
+
+  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);
+  }
+  return binary;
+}
+
+string GetBinaryPathWithExtension(const string& binary) {
+  return GetBinaryPathWithoutExtension(binary) + ".exe";
+}
+
+string GetEscapedArgument(const string& argument) {
+  ostringstream escaped_arg;
+  bool has_space = argument.find_first_of(' ') != string::npos;
+
+  if (has_space) {
+    escaped_arg << '\"';
+  }
+
+  string::const_iterator it = argument.begin();
+  while (it != argument.end()) {
+    char ch = *it++;
+    switch (ch) {
+      case '"':
+        // Escape double quotes
+        escaped_arg << "\\\"";
+        break;
+
+      case '\\':
+        // Escape back slashes
+        escaped_arg << "\\\\";
+        break;
+
+      default:
+        escaped_arg << ch;
+    }
+  }
+
+  if (has_space) {
+    escaped_arg << '\"';
+  }
+  return escaped_arg.str();
+}
+
+}  // namespace launcher
+}  // namespace bazel
diff --git a/src/tools/launcher/util/launcher_util.h b/src/tools/launcher/util/launcher_util.h
new file mode 100644
index 0000000..10f5f53
--- /dev/null
+++ b/src/tools/launcher/util/launcher_util.h
@@ -0,0 +1,56 @@
+// Copyright 2017 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.
+
+#ifndef BAZEL_SRC_TOOLS_LAUNCHER_UTIL_LAUNCHER_UTIL_H_
+#define BAZEL_SRC_TOOLS_LAUNCHER_UTIL_LAUNCHER_UTIL_H_
+
+#define PRINTF_ATTRIBUTE(string_index, first_to_check)
+
+#include <string>
+
+namespace bazel {
+namespace launcher {
+
+std::string GetLastErrorString();
+
+// Prints the specified error message and exits nonzero.
+__declspec(noreturn) void die(const char* format, ...) PRINTF_ATTRIBUTE(1, 2);
+
+// Prints the specified error message.
+void PrintError(const char* format, ...) PRINTF_ATTRIBUTE(1, 2);
+
+// Strip the .exe extension from binary path.
+//
+// On Windows, if the binary path is foo/bar/bin.exe then return foo/bar/bin
+std::string GetBinaryPathWithoutExtension(const std::string& binary);
+
+// Add exectuable extension to binary path
+//
+// On Windows, if the binary path is foo/bar/bin then return foo/bar/bin.exe
+std::string GetBinaryPathWithExtension(const std::string& binary);
+
+// Escape a command line argument.
+//
+// If the argument has space, then we quote it.
+// Escape \ to \\
+// Escape " to \"
+std::string GetEscapedArgument(const std::string& argument);
+
+// Check if a file exists at a given path.
+bool DoesFilePathExist(const std::string& path);
+
+}  // namespace launcher
+}  // namespace bazel
+
+#endif  // BAZEL_SRC_TOOLS_LAUNCHER_UTIL_LAUNCHER_UTIL_H_
diff --git a/src/tools/launcher/util/launcher_util_test.cc b/src/tools/launcher/util/launcher_util_test.cc
new file mode 100644
index 0000000..2e528a8
--- /dev/null
+++ b/src/tools/launcher/util/launcher_util_test.cc
@@ -0,0 +1,94 @@
+// Copyright 2017 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 <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "src/tools/launcher/util/launcher_util.h"
+#include "gtest/gtest.h"
+
+namespace bazel {
+namespace launcher {
+
+using std::getenv;
+using std::ios;
+using std::ofstream;
+using std::string;
+
+class LaunchUtilTest : public ::testing::Test {
+ protected:
+  LaunchUtilTest() {}
+
+  virtual ~LaunchUtilTest() {}
+
+  void SetUp() override {
+    char* tmpdir = getenv("TEST_TMPDIR");
+    if (tmpdir != NULL) {
+      test_tmpdir = string(tmpdir);
+    } else {
+      tmpdir = getenv("TEMP");
+      ASSERT_FALSE(tmpdir == NULL);
+      test_tmpdir = string(tmpdir);
+    }
+  }
+
+  void TearDown() override {}
+
+  string GetTmpDir() { return this->test_tmpdir; }
+
+  // Create an empty file at path
+  static void CreateEmptyFile(const string& path) {
+    ofstream file_stream(path.c_str(), ios::out | ios::binary);
+    file_stream.put('\0');
+  }
+
+ private:
+  string test_tmpdir;
+};
+
+TEST_F(LaunchUtilTest, GetBinaryPathWithoutExtensionTest) {
+  ASSERT_EQ("foo", GetBinaryPathWithoutExtension("foo.exe"));
+  ASSERT_EQ("foo.sh", GetBinaryPathWithoutExtension("foo.sh.exe"));
+  ASSERT_EQ("foo.sh", GetBinaryPathWithoutExtension("foo.sh"));
+}
+
+TEST_F(LaunchUtilTest, GetBinaryPathWithExtensionTest) {
+  ASSERT_EQ("foo.exe", GetBinaryPathWithExtension("foo"));
+  ASSERT_EQ("foo.sh.exe", GetBinaryPathWithExtension("foo.sh.exe"));
+  ASSERT_EQ("foo.sh.exe", GetBinaryPathWithExtension("foo.sh"));
+}
+
+TEST_F(LaunchUtilTest, GetEscapedArgumentTest) {
+  ASSERT_EQ("foo", GetEscapedArgument("foo"));
+  ASSERT_EQ("\"foo bar\"", GetEscapedArgument("foo bar"));
+  ASSERT_EQ("\"\\\"foo bar\\\"\"", GetEscapedArgument("\"foo bar\""));
+  ASSERT_EQ("foo\\\\bar", GetEscapedArgument("foo\\bar"));
+  ASSERT_EQ("foo\\\"bar", GetEscapedArgument("foo\"bar"));
+  ASSERT_EQ("C:\\\\foo\\\\bar\\\\", GetEscapedArgument("C:\\foo\\bar\\"));
+  ASSERT_EQ("\"C:\\\\foo foo\\\\bar\\\\\"",
+            GetEscapedArgument("C:\\foo foo\\bar\\"));
+}
+
+TEST_F(LaunchUtilTest, DoesFilePathExistTest) {
+  string file1 = GetTmpDir() + "/foo";
+  string file2 = GetTmpDir() + "/bar";
+  CreateEmptyFile(file1);
+  ASSERT_TRUE(DoesFilePathExist(file1));
+  ASSERT_FALSE(DoesFilePathExist(file2));
+}
+
+}  // namespace launcher
+}  // namespace bazel