aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/launcher
diff options
context:
space:
mode:
authorGravatar Yun Peng <pcloudy@google.com>2017-07-21 15:57:05 +0200
committerGravatar Jakob Buchgraber <buchgr@google.com>2017-07-24 09:51:07 +0200
commit09dd8c0330a3e18951d9481751ef99d3668115c0 (patch)
tree2308b2881af63db841ee66ae9e9800b262df170d /src/tools/launcher
parent0736aead33ba3d144f6cb2498ec175c3bfce9556 (diff)
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
Diffstat (limited to 'src/tools/launcher')
-rw-r--r--src/tools/launcher/BUILD53
-rw-r--r--src/tools/launcher/bash_launcher.cc54
-rw-r--r--src/tools/launcher/bash_launcher.h37
-rw-r--r--src/tools/launcher/java_launcher.cc26
-rw-r--r--src/tools/launcher/java_launcher.h34
-rw-r--r--src/tools/launcher/launcher.cc158
-rw-r--r--src/tools/launcher/launcher.h102
-rw-r--r--src/tools/launcher/launcher_main.cc63
-rw-r--r--src/tools/launcher/python_launcher.cc26
-rw-r--r--src/tools/launcher/python_launcher.h34
-rw-r--r--src/tools/launcher/util/BUILD38
-rw-r--r--src/tools/launcher/util/data_parser.cc105
-rw-r--r--src/tools/launcher/util/data_parser.h50
-rw-r--r--src/tools/launcher/util/data_parser_test.cc189
-rw-r--r--src/tools/launcher/util/launcher_util.cc122
-rw-r--r--src/tools/launcher/util/launcher_util.h56
-rw-r--r--src/tools/launcher/util/launcher_util_test.cc94
17 files changed, 1241 insertions, 0 deletions
diff --git a/src/tools/launcher/BUILD b/src/tools/launcher/BUILD
new file mode 100644
index 0000000000..d6617ce172
--- /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 0000000000..e1203a3f58
--- /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 0000000000..8070016800
--- /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 0000000000..ce626d347e
--- /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 0000000000..5ffe5881ab
--- /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 0000000000..86e9d8d473
--- /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 0000000000..e9f461b324
--- /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 0000000000..6f9133b6f2
--- /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 0000000000..b5dded9d23
--- /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 0000000000..1a21ca308e
--- /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 0000000000..7ecee600b6
--- /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 0000000000..98c8b1daf6
--- /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 0000000000..ecaf12a9b9
--- /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 0000000000..1adaf3aebd
--- /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 0000000000..4ef64eaa70
--- /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 0000000000..10f5f53000
--- /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 0000000000..2e528a88a3
--- /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