Windows: Native launcher now works with unicode.

The native launcher can now launch Java and Bash binary in
directory with non-English characters.

Unfortunately, python doesn't support running python zip file under
directory with non-English characters. eg. python ./??/bin.zip will
still fail.

See https://github.com/bazelbuild/bazel/issues/4473

Change-Id: I77fe9cdaabffc2e0d25c7097da5c0c9333a9c4a3
PiperOrigin-RevId: 201939391
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index d7538fd..76e94d1 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -19,6 +19,7 @@
 
 #include <cinttypes>
 #include <string>
+#include <vector>
 
 namespace blaze_util {
 
@@ -206,6 +207,38 @@
 
 #if defined(_WIN32) || defined(__CYGWIN__)
 std::wstring GetCwdW();
+bool MakeDirectoriesW(const std::wstring &path, unsigned int mode);
+
+// Interface to be implemented by ForEachDirectoryEntryW clients.
+class DirectoryEntryConsumerW {
+ public:
+  virtual ~DirectoryEntryConsumerW() {}
+
+  // This method is called for each entry in a directory.
+  // `name` is the full path of the entry.
+  // `is_directory` is true if this entry is a directory (but false if this is a
+  // symlink pointing to a directory).
+  virtual void Consume(const std::wstring &name, bool is_directory) = 0;
+};
+
+// Lists all files in `path` and all of its subdirectories.
+//
+// Does not follow symlinks / junctions.
+//
+// Populates `result` with the full paths of the files. Every entry will have
+// `path` as its prefix. If `path` is a file, `result` contains just this
+// file.
+void GetAllFilesUnderW(const std::wstring &path,
+                       std::vector<std::wstring> *result);
+
+// Visible for testing only.
+typedef void (*_ForEachDirectoryEntryW)(const std::wstring &path,
+                                        DirectoryEntryConsumerW *consume);
+
+// Visible for testing only.
+void _GetAllFilesUnderW(const std::wstring &path,
+                        std::vector<std::wstring> *result,
+                        _ForEachDirectoryEntryW walk_entries);
 #endif  // defined(_WIN32) || defined(__CYGWIN__)
 
 }  // namespace blaze_util
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index d81ada1..d266f96 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -32,17 +32,16 @@
 
 namespace blaze_util {
 
-using std::basic_string;
-using std::pair;
-using std::string;
-using std::unique_ptr;
-using std::wstring;
 using bazel::windows::AutoHandle;
 using bazel::windows::GetLongPath;
 using bazel::windows::HasUncPrefix;
 using bazel::windows::OpenDirectory;
-
-
+using std::basic_string;
+using std::pair;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using std::wstring;
 
 // Returns true if `path` refers to a directory or (non-dangling) junction.
 // `path` must be a normalized Windows path, with UNC prefix (and absolute) if
@@ -55,8 +54,6 @@
 // necessary.
 static bool UnlinkPathW(const wstring& path);
 
-static bool MakeDirectoriesW(const wstring& path);
-
 static bool CanReadFileW(const wstring& path);
 
 template <typename char_type>
@@ -806,7 +803,7 @@
   // fsync always fails on Cygwin with "Permission denied" for some reason.
 }
 
-static bool MakeDirectoriesW(const wstring& path) {
+bool MakeDirectoriesW(const wstring& path, unsigned int mode) {
   if (path.empty()) {
     return false;
   }
@@ -821,7 +818,7 @@
         << "MakeDirectoriesW(" << blaze_util::WstringToString(path)
         << ") could not find dirname: " << GetLastErrorString();
   }
-  return MakeDirectoriesW(parent) &&
+  return MakeDirectoriesW(parent, mode) &&
          ::CreateDirectoryW(path.c_str(), NULL) == TRUE;
 }
 
@@ -841,7 +838,7 @@
         << "): AsAbsoluteWindowsPath failed: " << error;
     return false;
   }
-  return MakeDirectoriesW(wpath);
+  return MakeDirectoriesW(wpath, mode);
 }
 
 std::wstring GetCwdW() {
@@ -872,6 +869,79 @@
   return ::SetCurrentDirectoryA(spath.c_str()) == TRUE;
 }
 
+class DirectoryTreeWalkerW : public DirectoryEntryConsumerW {
+ public:
+  DirectoryTreeWalkerW(vector<wstring>* files,
+                       _ForEachDirectoryEntryW walk_entries)
+      : _files(files), _walk_entries(walk_entries) {}
+
+  void Consume(const wstring& path, bool follow_directory) override {
+    if (follow_directory) {
+      Walk(path);
+    } else {
+      _files->push_back(path);
+    }
+  }
+
+  void Walk(const wstring& path) { _walk_entries(path, this); }
+
+ private:
+  vector<wstring>* _files;
+  _ForEachDirectoryEntryW _walk_entries;
+};
+
+void ForEachDirectoryEntryW(const wstring& path,
+                            DirectoryEntryConsumerW* consume) {
+  wstring wpath;
+  if (path.empty() || IsDevNull(path.c_str())) {
+    return;
+  }
+  string error;
+  if (!AsWindowsPath(path, &wpath, &error)) {
+    BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+        << "ForEachDirectoryEntryW(" << path
+        << "): AsWindowsPath failed: " << GetLastErrorString();
+  }
+
+  static const wstring kUncPrefix(L"\\\\?\\");
+  static const wstring kDot(L".");
+  static const wstring kDotDot(L"..");
+  // Always add an UNC prefix to ensure we can work with long paths.
+  if (!HasUncPrefix(wpath.c_str())) {
+    wpath = kUncPrefix + wpath;
+  }
+  // Unconditionally add a trailing backslash. We know `wpath` has no trailing
+  // backslash because it comes from AsWindowsPath whose output is always
+  // normalized (see NormalizeWindowsPath).
+  wpath.append(L"\\");
+  WIN32_FIND_DATAW metadata;
+  HANDLE handle = ::FindFirstFileW((wpath + L"*").c_str(), &metadata);
+  if (handle == INVALID_HANDLE_VALUE) {
+    return;  // directory does not exist or is empty
+  }
+
+  do {
+    if (kDot != metadata.cFileName && kDotDot != metadata.cFileName) {
+      wstring wname = wpath + metadata.cFileName;
+      wstring name(/* omit prefix */ 4 + wname.c_str());
+      bool is_dir = (metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+      bool is_junc =
+          (metadata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+      consume->Consume(name, is_dir && !is_junc);
+    }
+  } while (::FindNextFileW(handle, &metadata));
+  ::FindClose(handle);
+}
+
+void GetAllFilesUnderW(const wstring& path, vector<wstring>* result) {
+  _GetAllFilesUnderW(path, result, &ForEachDirectoryEntryW);
+}
+
+void _GetAllFilesUnderW(const wstring& path, vector<wstring>* result,
+                        _ForEachDirectoryEntryW walk_entries) {
+  DirectoryTreeWalkerW(result, walk_entries).Walk(path);
+}
+
 void ForEachDirectoryEntry(const string &path,
                            DirectoryEntryConsumer *consume) {
   wstring wpath;
diff --git a/src/main/cpp/util/path_platform.h b/src/main/cpp/util/path_platform.h
index e2efe09..b0fb88d 100644
--- a/src/main/cpp/util/path_platform.h
+++ b/src/main/cpp/util/path_platform.h
@@ -70,7 +70,12 @@
 // is that this would add more complexity to the implementation file(s)? of
 // path.h, which would have to have the platform-specific implementations.
 #if defined(_WIN32) || defined(__CYGWIN__)
+bool IsDevNull(const wchar_t *path);
+
+bool IsAbsolute(const std::wstring &path);
+
 const wchar_t *RemoveUncPrefixMaybe(const wchar_t *ptr);
+
 void AddUncPrefixMaybe(std::wstring *path);
 
 std::pair<std::wstring, std::wstring> SplitPathW(const std::wstring &path);
diff --git a/src/main/cpp/util/path_windows.cc b/src/main/cpp/util/path_windows.cc
index d115b6e..c7afe40 100644
--- a/src/main/cpp/util/path_windows.cc
+++ b/src/main/cpp/util/path_windows.cc
@@ -396,6 +396,10 @@
   return IsRootOrAbsolute(path, false);
 }
 
+bool IsAbsolute(const std::wstring& path) {
+  return IsRootOrAbsolute(path, false);
+}
+
 bool IsRootDirectoryW(const std::wstring& path) {
   return IsRootOrAbsolute(path, true);
 }
diff --git a/src/main/cpp/util/strings.cc b/src/main/cpp/util/strings.cc
index e98b5d2..649156a 100644
--- a/src/main/cpp/util/strings.cc
+++ b/src/main/cpp/util/strings.cc
@@ -308,4 +308,8 @@
   return UstringToVstring<char, wchar_t>(input, mbstowcs, "%s");
 }
 
+std::wstring CstringToWstring(const std::string &input) {
+  return wstring(CstringToWstring(input.c_str()).get());
+}
+
 }  // namespace blaze_util
diff --git a/src/main/cpp/util/strings.h b/src/main/cpp/util/strings.h
index 0a761e1..cd54181 100644
--- a/src/main/cpp/util/strings.h
+++ b/src/main/cpp/util/strings.h
@@ -115,6 +115,7 @@
 // Convert a char string to a wchar_t string. Useful when passing arguments to
 // widechar Windows API functions.
 std::unique_ptr<wchar_t[]> CstringToWstring(const char *input);
+std::wstring CstringToWstring(const std::string &input);
 
 }  // namespace blaze_util
 
diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc
index a7eb9c7..eeb1e2c 100644
--- a/src/test/cpp/util/file_test.cc
+++ b/src/test/cpp/util/file_test.cc
@@ -249,7 +249,7 @@
   ASSERT_FALSE(IsDevNull("dev/null"));
   ASSERT_FALSE(IsDevNull("/dev/nul"));
   ASSERT_FALSE(IsDevNull("/dev/nulll"));
-  ASSERT_FALSE(IsDevNull(NULL));
+  ASSERT_FALSE(IsDevNull((char *) nullptr));
   ASSERT_FALSE(IsDevNull(""));
 }
 
diff --git a/src/test/cpp/util/path_windows_test.cc b/src/test/cpp/util/path_windows_test.cc
index bcf6262..965d885 100644
--- a/src/test/cpp/util/path_windows_test.cc
+++ b/src/test/cpp/util/path_windows_test.cc
@@ -309,7 +309,7 @@
   ASSERT_FALSE(IsDevNull("/dev/nul"));
   ASSERT_FALSE(IsDevNull("/dev/nulll"));
   ASSERT_FALSE(IsDevNull("nu"));
-  ASSERT_FALSE(IsDevNull(NULL));
+  ASSERT_FALSE(IsDevNull((char *) nullptr));
   ASSERT_FALSE(IsDevNull(""));
 }
 
diff --git a/src/test/py/bazel/launcher_test.py b/src/test/py/bazel/launcher_test.py
index 42d6efc..95f38f3 100644
--- a/src/test/py/bazel/launcher_test.py
+++ b/src/test/py/bazel/launcher_test.py
@@ -540,6 +540,57 @@
     classpath = stdout[stdout.index('-classpath') + 1]
     self.assertRegexpMatches(classpath, r'foo-[A-Za-z0-9]+-classpath.jar$')
 
+  def testWindowsNativeLauncherInNonEnglishPath(self):
+    if not self.IsWindows():
+      return
+    self.ScratchFile('WORKSPACE')
+    self.ScratchFile('bin/BUILD', [
+        'java_binary(',
+        '  name = "bin_java",',
+        '  srcs = ["Main.java"],',
+        '  main_class = "Main",',
+        ')',
+        'sh_binary(',
+        '  name = "bin_sh",',
+        '  srcs = ["main.sh"],',
+        ')',
+    ])
+    self.ScratchFile('bin/Main.java', [
+        'public class Main {',
+        '  public static void main(String[] args) {'
+        '    System.out.println("helloworld");',
+        '  }',
+        '}',
+    ])
+    self.ScratchFile('bin/main.sh', [
+        'echo "helloworld"',
+    ])
+
+    exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
+    self.AssertExitCode(exit_code, 0, stderr)
+    bazel_bin = stdout[0]
+
+    exit_code, _, stderr = self.RunBazel(
+        ['build', '--windows_exe_launcher=1', '//bin/...'])
+    self.AssertExitCode(exit_code, 0, stderr)
+
+    for f in [
+        'bin_java.exe', 'bin_java.exe.runfiles_manifest', 'bin_sh.exe',
+        'bin_sh', 'bin_sh.exe.runfiles_manifest'
+    ]:
+      self.CopyFile(os.path.join(bazel_bin, 'bin', f),
+                    os.path.join(u'./\u6d4b\u8bd5', f))
+
+    unicode_binary_path = u'./\u6d4b\u8bd5/bin_java.exe'
+    exit_code, stdout, stderr = self.RunProgram([unicode_binary_path])
+    self.AssertExitCode(exit_code, 0, stderr)
+    self.assertEqual('helloworld', ''.join(stdout))
+
+    unicode_binary_path = u'./\u6d4b\u8bd5/bin_sh.exe'
+    exit_code, stdout, stderr = self.RunProgram([unicode_binary_path])
+    self.AssertExitCode(exit_code, 0, stderr)
+    self.assertEqual('helloworld', ''.join(stdout))
+
   def AssertRunfilesManifestContains(self, manifest, entry):
     with open(manifest, 'r') as f:
       for l in f:
diff --git a/src/tools/launcher/bash_launcher.cc b/src/tools/launcher/bash_launcher.cc
index 2692c17..cd714ab 100644
--- a/src/tools/launcher/bash_launcher.cc
+++ b/src/tools/launcher/bash_launcher.cc
@@ -22,33 +22,33 @@
 namespace bazel {
 namespace launcher {
 
-using std::ostringstream;
-using std::string;
 using std::vector;
+using std::wostringstream;
+using std::wstring;
 
 static constexpr const char* BASH_BIN_PATH = "bash_bin_path";
 
 ExitCode BashBinaryLauncher::Launch() {
-  string bash_binary = this->GetLaunchInfoByKey(BASH_BIN_PATH);
+  wstring 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.c_str())) {
-    bash_binary = "bash.exe";
+    bash_binary = L"bash.exe";
   }
 
-  vector<string> origin_args = this->GetCommandlineArguments();
-  ostringstream bash_command;
-  string bash_main_file = GetBinaryPathWithoutExtension(origin_args[0]);
+  vector<wstring> origin_args = this->GetCommandlineArguments();
+  wostringstream bash_command;
+  wstring bash_main_file = GetBinaryPathWithoutExtension(origin_args[0]);
   bash_command << GetEscapedArgument(bash_main_file,
                                      /*escape_backslash = */ true);
   for (int i = 1; i < origin_args.size(); i++) {
-    bash_command << ' ';
+    bash_command << L' ';
     bash_command << GetEscapedArgument(origin_args[i],
                                        /*escape_backslash = */ true);
   }
 
-  vector<string> args;
-  args.push_back("-c");
+  vector<wstring> args;
+  args.push_back(L"-c");
   args.push_back(
       GetEscapedArgument(bash_command.str(), /*escape_backslash = */ true));
   return this->LaunchProcess(bash_binary, args);
diff --git a/src/tools/launcher/bash_launcher.h b/src/tools/launcher/bash_launcher.h
index 7587355..38c9d0e 100644
--- a/src/tools/launcher/bash_launcher.h
+++ b/src/tools/launcher/bash_launcher.h
@@ -23,8 +23,8 @@
 class BashBinaryLauncher : public BinaryLauncherBase {
  public:
   BashBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc,
-                     char* argv[])
-      : BinaryLauncherBase(launch_info, argc, argv){}
+                     wchar_t* argv[])
+      : BinaryLauncherBase(launch_info, argc, argv) {}
   ExitCode Launch();
 };
 
diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc
index 053218d..82eeb0b 100644
--- a/src/tools/launcher/java_launcher.cc
+++ b/src/tools/launcher/java_launcher.cc
@@ -30,12 +30,12 @@
 namespace launcher {
 
 using std::getline;
-using std::ofstream;
-using std::ostringstream;
 using std::string;
-using std::stringstream;
 using std::vector;
+using std::wofstream;
+using std::wostringstream;
 using std::wstring;
+using std::wstringstream;
 
 // The runfile path of java binary, eg. local_jdk/bin/java.exe
 static constexpr const char* JAVA_BIN_PATH = "java_bin_path";
@@ -47,15 +47,15 @@
 // 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) {
+static bool GetFlagValue(const wstring& str, const wstring& prefix,
+                         wstring* value_ptr) {
   if (str.compare(0, prefix.length(), prefix)) {
     return false;
   }
-  string& value = *value_ptr;
+  wstring& value = *value_ptr;
   value = str.substr(prefix.length());
   int len = value.length();
-  if (len >= 2 && value[0] == '"' && value[len - 1] == '"') {
+  if (len >= 2 && value[0] == L'"' && value[len - 1] == L'"') {
     value = value.substr(1, len - 2);
   }
   return true;
@@ -64,34 +64,34 @@
 // 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)) {
+bool JavaBinaryLauncher::ProcessWrapperArgument(const wstring& argument) {
+  wstring flag_value;
+  if (argument.compare(L"--debug") == 0) {
+    wstring default_jvm_debug_port;
+    if (GetEnv(L"DEFAULT_JVM_DEBUG_PORT", &default_jvm_debug_port)) {
       this->jvm_debug_port = default_jvm_debug_port;
     } else {
-      this->jvm_debug_port = "5005";
+      this->jvm_debug_port = L"5005";
     }
-  } else if (GetFlagValue(argument, "--debug=", &flag_value)) {
+  } else if (GetFlagValue(argument, L"--debug=", &flag_value)) {
     this->jvm_debug_port = flag_value;
-  } else if (GetFlagValue(argument, "--main_advice=", &flag_value)) {
+  } else if (GetFlagValue(argument, L"--main_advice=", &flag_value)) {
     this->main_advice = flag_value;
-  } else if (GetFlagValue(argument, "--main_advice_classpath=", &flag_value)) {
+  } else if (GetFlagValue(argument, L"--main_advice_classpath=", &flag_value)) {
     this->main_advice_classpath = flag_value;
-  } else if (GetFlagValue(argument, "--jvm_flag=", &flag_value)) {
+  } else if (GetFlagValue(argument, L"--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, ' ')) {
+  } else if (GetFlagValue(argument, L"--jvm_flags=", &flag_value)) {
+    wstringstream flag_value_ss(flag_value);
+    wstring item;
+    while (getline(flag_value_ss, item, L' ')) {
       this->jvm_flags_cmdline.push_back(item);
     }
-  } else if (argument.compare("--singlejar") == 0) {
+  } else if (argument.compare(L"--singlejar") == 0) {
     this->singlejar = true;
-  } else if (argument.compare("--print_javabin") == 0) {
+  } else if (argument.compare(L"--print_javabin") == 0) {
     this->print_javabin = true;
-  } else if (GetFlagValue(argument, "--classpath_limit=", &flag_value)) {
+  } else if (GetFlagValue(argument, L"--classpath_limit=", &flag_value)) {
     this->classpath_limit = std::stoi(flag_value);
   } else {
     return false;
@@ -99,8 +99,8 @@
   return true;
 }
 
-vector<string> JavaBinaryLauncher::ProcessesCommandLine() {
-  vector<string> args;
+vector<wstring> JavaBinaryLauncher::ProcessesCommandLine() {
+  vector<wstring> args;
   bool first = 1;
   for (const auto& arg : this->GetCommandlineArguments()) {
     // Skip the first arugment.
@@ -108,13 +108,13 @@
       first = 0;
       continue;
     }
-    string flag_value;
+    wstring 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 (GetFlagValue(arg, L"--wrapper_script_flag=", &flag_value)) {
       if (!ProcessWrapperArgument(flag_value)) {
-        die("invalid wrapper argument '%s'", arg.c_str());
+        die(L"invalid wrapper argument '%s'", arg.c_str());
       }
     } else if (!args.empty() || !ProcessWrapperArgument(arg)) {
       args.push_back(arg);
@@ -124,34 +124,34 @@
 }
 
 // Return an absolute normalized path for the directory of manifest jar
-static string GetManifestJarDir(const string& binary_base_path) {
-  string abs_manifest_jar_dir;
-  std::size_t slash = binary_base_path.find_last_of("/\\");
-  if (slash == string::npos) {
-    abs_manifest_jar_dir = "";
+static wstring GetManifestJarDir(const wstring& binary_base_path) {
+  wstring abs_manifest_jar_dir;
+  std::size_t slash = binary_base_path.find_last_of(L"/\\");
+  if (slash == wstring::npos) {
+    abs_manifest_jar_dir = L"";
   } else {
     abs_manifest_jar_dir = binary_base_path.substr(0, slash);
   }
   if (!blaze_util::IsAbsolute(binary_base_path)) {
-    abs_manifest_jar_dir = blaze_util::GetCwd() + "\\" + abs_manifest_jar_dir;
+    abs_manifest_jar_dir = blaze_util::GetCwdW() + L"\\" + abs_manifest_jar_dir;
   }
-  string result;
+  wstring result;
   if (!NormalizePath(abs_manifest_jar_dir, &result)) {
-    die("GetManifestJarDir Failed");
+    die(L"GetManifestJarDir Failed");
   }
   return result;
 }
 
-static void WriteJarClasspath(const string& jar_path,
-                              ostringstream* manifest_classpath) {
-  *manifest_classpath << ' ';
-  if (jar_path.find_first_of(" \\") != string::npos) {
+static void WriteJarClasspath(const wstring& jar_path,
+                              wostringstream* manifest_classpath) {
+  *manifest_classpath << L' ';
+  if (jar_path.find_first_of(L" \\") != wstring::npos) {
     for (const auto& x : jar_path) {
-      if (x == ' ') {
-        *manifest_classpath << "%20";
+      if (x == L' ') {
+        *manifest_classpath << L"%20";
       }
-      if (x == '\\') {
-        *manifest_classpath << "/";
+      if (x == L'\\') {
+        *manifest_classpath << L"/";
       } else {
         *manifest_classpath << x;
       }
@@ -161,119 +161,117 @@
   }
 }
 
-string JavaBinaryLauncher::GetJunctionBaseDir() {
-  string binary_base_path =
+wstring JavaBinaryLauncher::GetJunctionBaseDir() {
+  wstring binary_base_path =
       GetBinaryPathWithExtension(this->GetCommandlineArguments()[0]);
-  string result;
-  if (!NormalizePath(binary_base_path + ".j", &result)) {
-    die("Failed to get normalized junction base directory.");
+  wstring result;
+  if (!NormalizePath(binary_base_path + L".j", &result)) {
+    die(L"Failed to get normalized junction base directory.");
   }
   return result;
 }
 
 void JavaBinaryLauncher::DeleteJunctionBaseDir() {
-  string junction_base_dir_norm = GetJunctionBaseDir();
+  wstring junction_base_dir_norm = GetJunctionBaseDir();
   if (!DoesDirectoryPathExist(junction_base_dir_norm.c_str())) {
     return;
   }
-  vector<string> junctions;
-  blaze_util::GetAllFilesUnder(junction_base_dir_norm, &junctions);
+  vector<wstring> junctions;
+  blaze_util::GetAllFilesUnderW(junction_base_dir_norm, &junctions);
   for (const auto& junction : junctions) {
     if (!DeleteDirectoryByPath(junction.c_str())) {
-      PrintError(GetLastErrorString().c_str());
+      PrintError(L"Failed to delete junction directory: %hs",
+                 GetLastErrorString().c_str());
     }
   }
   if (!DeleteDirectoryByPath(junction_base_dir_norm.c_str())) {
-    PrintError(GetLastErrorString().c_str());
+    PrintError(L"Failed to delete junction directory: %hs",
+               GetLastErrorString().c_str());
   }
 }
 
-string JavaBinaryLauncher::CreateClasspathJar(const string& classpath) {
-  string binary_base_path =
+wstring JavaBinaryLauncher::CreateClasspathJar(const wstring& classpath) {
+  wstring binary_base_path =
       GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]);
-  string abs_manifest_jar_dir_norm = GetManifestJarDir(binary_base_path);
+  wstring abs_manifest_jar_dir_norm = GetManifestJarDir(binary_base_path);
 
-  ostringstream manifest_classpath;
-  manifest_classpath << "Class-Path:";
-  stringstream classpath_ss(classpath);
-  string path, path_norm;
+  wostringstream manifest_classpath;
+  manifest_classpath << L"Class-Path:";
+  wstringstream classpath_ss(classpath);
+  wstring path, path_norm;
 
   // A set to store all junctions created.
   // The key is the target path, the value is the junction path.
-  std::unordered_map<string, string> jar_dirs;
-  string junction_base_dir_norm = GetJunctionBaseDir();
+  std::unordered_map<wstring, wstring> jar_dirs;
+  wstring junction_base_dir_norm = GetJunctionBaseDir();
   int junction_count = 0;
   // Make sure the junction base directory doesn't exist already.
   DeleteJunctionBaseDir();
-  blaze_util::MakeDirectories(junction_base_dir_norm, 0755);
+  blaze_util::MakeDirectoriesW(junction_base_dir_norm, 0755);
 
-  while (getline(classpath_ss, path, ';')) {
+  while (getline(classpath_ss, path, L';')) {
     if (blaze_util::IsAbsolute(path)) {
       if (!NormalizePath(path, &path_norm)) {
-        die("CreateClasspathJar failed");
+        die(L"CreateClasspathJar failed");
       }
 
       // If two paths are under different drives, we should create a junction to
       // the jar's directory
       if (path_norm[0] != abs_manifest_jar_dir_norm[0]) {
-        string jar_dir = GetParentDirFromPath(path_norm);
-        string jar_base_name = GetBaseNameFromPath(path_norm);
-        string junction;
+        wstring jar_dir = GetParentDirFromPath(path_norm);
+        wstring jar_base_name = GetBaseNameFromPath(path_norm);
+        wstring junction;
         auto search = jar_dirs.find(jar_dir);
         if (search == jar_dirs.end()) {
-          junction =
-              junction_base_dir_norm + "\\" + std::to_string(junction_count++);
+          junction = junction_base_dir_norm + L"\\" +
+                     std::to_wstring(junction_count++);
 
-          wstring wjar_dir(
-              blaze_util::CstringToWstring(junction.c_str()).get());
-          wstring wjunction(
-              blaze_util::CstringToWstring(jar_dir.c_str()).get());
-          wstring werror(bazel::windows::CreateJunction(wjar_dir, wjunction));
-          if (!werror.empty()) {
-            string error(werror.begin(), werror.end());
-            die("CreateClasspathJar failed: %s", error.c_str());
+          wstring error(bazel::windows::CreateJunction(junction, jar_dir));
+          if (!error.empty()) {
+            die(L"CreateClasspathJar failed: %s", error.c_str());
           }
 
           jar_dirs.insert(std::make_pair(jar_dir, junction));
         } else {
           junction = search->second;
         }
-        path_norm = junction + "\\" + jar_base_name;
+        path_norm = junction + L"\\" + jar_base_name;
       }
 
       if (!RelativeTo(path_norm, abs_manifest_jar_dir_norm, &path)) {
-        die("CreateClasspathJar failed");
+        die(L"CreateClasspathJar failed");
       }
     }
     WriteJarClasspath(path, &manifest_classpath);
   }
 
-  string rand_id = "-" + GetRandomStr(10);
-  string jar_manifest_file_path = binary_base_path + rand_id + ".jar_manifest";
-  ofstream jar_manifest_file(jar_manifest_file_path);
-  jar_manifest_file << "Manifest-Version: 1.0\n";
+  wstring rand_id = L"-" + GetRandomStr(10);
+  wstring jar_manifest_file_path =
+      binary_base_path + rand_id + L".jar_manifest";
+  wofstream jar_manifest_file(jar_manifest_file_path);
+  jar_manifest_file << L"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();
+  wstring 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 << L" ";
     }
     jar_manifest_file << manifest_classpath_str.substr(i, 71) << "\n";
   }
   jar_manifest_file.close();
 
   // Create the command for generating classpath jar.
-  string manifest_jar_path = binary_base_path + rand_id + "-classpath.jar";
-  string jar_bin = this->Rlocation(this->GetLaunchInfoByKey(JAR_BIN_PATH));
-  vector<string> arguments;
-  arguments.push_back("cvfm");
+  wstring manifest_jar_path = binary_base_path + rand_id + L"-classpath.jar";
+  wstring jar_bin = this->Rlocation(this->GetLaunchInfoByKey(JAR_BIN_PATH));
+  vector<wstring> arguments;
+  arguments.push_back(L"cvfm");
   arguments.push_back(manifest_jar_path);
   arguments.push_back(jar_manifest_file_path);
 
   if (this->LaunchProcess(jar_bin, arguments, /* suppressOutput */ true) != 0) {
-    die("Couldn't create classpath jar: %s", manifest_jar_path.c_str());
+    die(L"Couldn't create classpath jar: %s", manifest_jar_path.c_str());
   }
 
   // Delete jar_manifest_file after classpath jar is created.
@@ -284,95 +282,95 @@
 
 ExitCode JavaBinaryLauncher::Launch() {
   // Parse the original command line.
-  vector<string> remaining_args = this->ProcessesCommandLine();
+  vector<wstring> remaining_args = this->ProcessesCommandLine();
 
   // Set JAVA_RUNFILES
-  string java_runfiles;
-  if (!GetEnv("JAVA_RUNFILES", &java_runfiles)) {
+  wstring java_runfiles;
+  if (!GetEnv(L"JAVA_RUNFILES", &java_runfiles)) {
     java_runfiles = this->GetRunfilesPath();
   }
-  SetEnv("JAVA_RUNFILES", java_runfiles);
+  SetEnv(L"JAVA_RUNFILES", java_runfiles);
 
   // Print Java binary path if needed
-  string java_bin = this->Rlocation(this->GetLaunchInfoByKey(JAVA_BIN_PATH),
-                                    /*need_workspace_name =*/false);
+  wstring 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());
+      this->GetLaunchInfoByKey(JAVA_START_CLASS) == L"--print_javabin") {
+    wprintf(L"%s\n", java_bin.c_str());
     return 0;
   }
 
-  ostringstream classpath;
+  wostringstream classpath;
 
   // Run deploy jar if needed, otherwise generate the CLASSPATH by rlocation.
   if (this->singlejar) {
-    string deploy_jar =
+    wstring deploy_jar =
         GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]) +
-        "_deploy.jar";
+        L"_deploy.jar";
     if (!DoesFilePathExist(deploy_jar.c_str())) {
-      die("Option --singlejar was passed, but %s does not exist.\n  (You may "
+      die(L"Option --singlejar was passed, but %s does not exist.\n  (You may "
           "need to build it explicitly.)",
           deploy_jar.c_str());
     }
-    classpath << deploy_jar << ';';
+    classpath << deploy_jar << L';';
   } else {
     // Add main advice classpath if exists
     if (!this->main_advice_classpath.empty()) {
-      classpath << this->main_advice_classpath << ';';
+      classpath << this->main_advice_classpath << L';';
     }
-    string path;
-    stringstream classpath_ss(this->GetLaunchInfoByKey(CLASSPATH));
-    while (getline(classpath_ss, path, ';')) {
-      classpath << this->Rlocation(path) << ';';
+    wstring path;
+    wstringstream classpath_ss(this->GetLaunchInfoByKey(CLASSPATH));
+    while (getline(classpath_ss, path, L';')) {
+      classpath << this->Rlocation(path) << L';';
     }
   }
 
   // Set jvm debug options
-  ostringstream jvm_debug_flags;
+  wostringstream 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";
+    wstring jvm_debug_suspend;
+    if (!GetEnv(L"DEFAULT_JVM_DEBUG_SUSPEND", &jvm_debug_suspend)) {
+      jvm_debug_suspend = L"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;
+    jvm_debug_flags << L"-agentlib:jdwp=transport=dt_socket,server=y";
+    jvm_debug_flags << L",suspend=" << jvm_debug_suspend;
+    jvm_debug_flags << L",address=" << jvm_debug_port;
 
-    string value;
-    if (GetEnv("PERSISTENT_TEST_RUNNER", &value) && value == "true") {
-      jvm_debug_flags << ",quiet=y";
+    wstring value;
+    if (GetEnv(L"PERSISTENT_TEST_RUNNER", &value) && value == L"true") {
+      jvm_debug_flags << L",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, ' ')) {
+  vector<wstring> jvm_flags;
+  wstring jvm_flags_env;
+  GetEnv(L"JVM_FLAGS", &jvm_flags_env);
+  wstring flag;
+  wstringstream jvm_flags_env_ss(jvm_flags_env);
+  while (getline(jvm_flags_env_ss, flag, L' ')) {
     jvm_flags.push_back(flag);
   }
-  stringstream jvm_flags_launch_info_ss(this->GetLaunchInfoByKey(JVM_FLAGS));
-  while (getline(jvm_flags_launch_info_ss, flag, ' ')) {
+  wstringstream jvm_flags_launch_info_ss(this->GetLaunchInfoByKey(JVM_FLAGS));
+  while (getline(jvm_flags_launch_info_ss, flag, L' ')) {
     jvm_flags.push_back(flag);
   }
 
   // Check if TEST_TMPDIR is available to use for scratch.
-  string test_tmpdir;
-  if (GetEnv("TEST_TMPDIR", &test_tmpdir) &&
+  wstring test_tmpdir;
+  if (GetEnv(L"TEST_TMPDIR", &test_tmpdir) &&
       DoesDirectoryPathExist(test_tmpdir.c_str())) {
-    jvm_flags.push_back("-Djava.io.tmpdir=" + test_tmpdir);
+    jvm_flags.push_back(L"-Djava.io.tmpdir=" + test_tmpdir);
   }
 
   // Construct the final command line arguments
-  vector<string> arguments;
+  vector<wstring> arguments;
   // Add classpath flags
-  arguments.push_back("-classpath");
+  arguments.push_back(L"-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();
-  string classpath_jar = "";
+  wstring classpath_str = classpath.str();
+  wstring classpath_jar = L"";
   if (classpath_str.length() > this->classpath_limit) {
     classpath_jar = CreateClasspathJar(classpath_str);
     arguments.push_back(classpath_jar);
@@ -380,7 +378,7 @@
     arguments.push_back(classpath_str);
   }
   // Add JVM debug flags
-  string jvm_debug_flags_str = jvm_debug_flags.str();
+  wstring jvm_debug_flags_str = jvm_debug_flags.str();
   if (!jvm_debug_flags_str.empty()) {
     arguments.push_back(jvm_debug_flags_str);
   }
@@ -403,7 +401,7 @@
     arguments.push_back(arg);
   }
 
-  vector<string> escaped_arguments;
+  vector<wstring> escaped_arguments;
   // Quote the arguments if having spaces
   for (const auto& arg : arguments) {
     escaped_arguments.push_back(
diff --git a/src/tools/launcher/java_launcher.h b/src/tools/launcher/java_launcher.h
index 93366b3..d02a716 100644
--- a/src/tools/launcher/java_launcher.h
+++ b/src/tools/launcher/java_launcher.h
@@ -30,7 +30,7 @@
 class JavaBinaryLauncher : public BinaryLauncherBase {
  public:
   JavaBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc,
-                     char* argv[])
+                     wchar_t* argv[])
       : BinaryLauncherBase(launch_info, argc, argv),
         singlejar(false),
         print_javabin(false),
@@ -68,19 +68,19 @@
   //                       classpath jar.
   //
   // The remainder of the command line is passed to the program.
-  bool ProcessWrapperArgument(const std::string& argument);
+  bool ProcessWrapperArgument(const std::wstring& 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::vector<std::wstring> ProcessesCommandLine();
 
-  std::string jvm_debug_port;
-  std::string main_advice;
-  std::string main_advice_classpath;
-  std::vector<std::string> jvm_flags_cmdline;
+  std::wstring jvm_debug_port;
+  std::wstring main_advice;
+  std::wstring main_advice_classpath;
+  std::vector<std::wstring> jvm_flags_cmdline;
   bool singlejar;
   bool print_javabin;
   int classpath_limit;
@@ -89,11 +89,11 @@
   // limit.
   //
   // Return the path of the classpath jar created.
-  std::string CreateClasspathJar(const std::string& classpath);
+  std::wstring CreateClasspathJar(const std::wstring& classpath);
 
   // Creat a directory based on the binary path, all the junctions will be
   // generated under this directory.
-  std::string GetJunctionBaseDir();
+  std::wstring GetJunctionBaseDir();
 
   // Delete all the junction directory and all the junctions under it.
   void DeleteJunctionBaseDir();
diff --git a/src/tools/launcher/launcher.cc b/src/tools/launcher/launcher.cc
index 9bcc364..b663c2c 100644
--- a/src/tools/launcher/launcher.cc
+++ b/src/tools/launcher/launcher.cc
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "src/main/cpp/util/path_platform.h"
+#include "src/main/cpp/util/strings.h"
 #include "src/tools/launcher/launcher.h"
 #include "src/tools/launcher/util/data_parser.h"
 #include "src/tools/launcher/util/launcher_util.h"
@@ -29,25 +30,26 @@
 namespace launcher {
 
 using std::ifstream;
-using std::ostringstream;
 using std::string;
 using std::unordered_map;
 using std::vector;
+using std::wostringstream;
+using std::wstring;
 
-static std::string GetRunfilesDir(const char* argv0) {
-  string runfiles_dir;
+static wstring GetRunfilesDir(const wchar_t* argv0) {
+  wstring runfiles_dir;
   // If RUNFILES_DIR is already set (probably we are either in a test or in a
   // data dependency) then use it.
-  if (GetEnv("RUNFILES_DIR", &runfiles_dir)) {
+  if (GetEnv(L"RUNFILES_DIR", &runfiles_dir)) {
     return runfiles_dir;
   }
   // Otherwise this is probably a top-level non-test binary (e.g. a genrule
   // tool) and should look for its runfiles beside the executable.
-  return GetBinaryPathWithExtension(argv0) + ".runfiles";
+  return GetBinaryPathWithExtension(argv0) + L".runfiles";
 }
 
 BinaryLauncherBase::BinaryLauncherBase(
-    const LaunchDataParser::LaunchInfo& _launch_info, int argc, char* argv[])
+    const LaunchDataParser::LaunchInfo& _launch_info, int argc, wchar_t* argv[])
     : launch_info(_launch_info),
       manifest_file(FindManifestFile(argv[0])),
       runfiles_dir(GetRunfilesDir(argv[0])),
@@ -58,22 +60,22 @@
   // Prefer to use the runfiles manifest, if it exists, but otherwise the
   // runfiles directory will be used by default. On Windows, the manifest is
   // used locally, and the runfiles directory is used remotely.
-  if (manifest_file != "") {
+  if (!manifest_file.empty()) {
     ParseManifestFile(&manifest_file_map, manifest_file);
   }
 }
 
-static bool FindManifestFileImpl(const char* argv0, string* result) {
+static bool FindManifestFileImpl(const wchar_t* argv0, wstring* result) {
   // If this binary X runs as the data-dependency of some other binary Y, then
   // X has no runfiles manifest/directory and should use Y's.
-  if (GetEnv("RUNFILES_MANIFEST_FILE", result) &&
+  if (GetEnv(L"RUNFILES_MANIFEST_FILE", result) &&
       DoesFilePathExist(result->c_str())) {
     return true;
   }
 
-  string directory;
-  if (GetEnv("RUNFILES_DIR", &directory)) {
-    *result = directory + "/MANIFEST";
+  wstring directory;
+  if (GetEnv(L"RUNFILES_DIR", &directory)) {
+    *result = directory + L"/MANIFEST";
     if (DoesFilePathExist(result->c_str())) {
       return true;
     }
@@ -82,13 +84,13 @@
   // If this binary X runs by itself (not as a data-dependency of another
   // binary), then look for the manifest in a runfiles directory next to the
   // main binary, then look for it (the manifest) next to the main binary.
-  directory = GetBinaryPathWithExtension(argv0) + ".runfiles";
-  *result = directory + "/MANIFEST";
+  directory = GetBinaryPathWithExtension(argv0) + L".runfiles";
+  *result = directory + L"/MANIFEST";
   if (DoesFilePathExist(result->c_str())) {
     return true;
   }
 
-  *result = directory + "_manifest";
+  *result = directory + L"_manifest";
   if (DoesFilePathExist(result->c_str())) {
     return true;
   }
@@ -96,10 +98,10 @@
   return false;
 }
 
-string BinaryLauncherBase::FindManifestFile(const char* argv0) {
-  string manifest_file;
+wstring BinaryLauncherBase::FindManifestFile(const wchar_t* argv0) {
+  wstring manifest_file;
   if (!FindManifestFileImpl(argv0, &manifest_file)) {
-    return "";
+    return L"";
   }
   // The path will be set as the RUNFILES_MANIFEST_FILE envvar and used by the
   // shell script, so let's convert backslashes to forward slashes.
@@ -107,35 +109,36 @@
   return manifest_file;
 }
 
-string BinaryLauncherBase::GetRunfilesPath() const {
-  string runfiles_path =
-      GetBinaryPathWithExtension(this->commandline_arguments[0]) + ".runfiles";
-  std::replace(runfiles_path.begin(), runfiles_path.end(), '/', '\\');
+wstring BinaryLauncherBase::GetRunfilesPath() const {
+  wstring runfiles_path =
+      GetBinaryPathWithExtension(this->commandline_arguments[0]) + L".runfiles";
+  std::replace(runfiles_path.begin(), runfiles_path.end(), L'/', L'\\');
   return runfiles_path;
 }
 
 void BinaryLauncherBase::ParseManifestFile(ManifestFileMap* manifest_file_map,
-                                           const string& manifest_path) {
+                                           const wstring& manifest_path) {
   ifstream manifest_file(AsAbsoluteWindowsPath(manifest_path.c_str()).c_str());
 
   if (!manifest_file) {
-    die("Couldn't open MANIFEST file: %s", manifest_path.c_str());
+    die(L"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());
+      die(L"Wrong MANIFEST format at line: %s", line.c_str());
     }
-    string key = line.substr(0, space_pos);
-    string value = line.substr(space_pos + 1);
+    wstring wline = blaze_util::CstringToWstring(line);
+    wstring key = wline.substr(0, space_pos);
+    wstring value = wline.substr(space_pos + 1);
     manifest_file_map->insert(make_pair(key, value));
   }
 }
 
-string BinaryLauncherBase::Rlocation(const string& path,
-                                     bool need_workspace_name) const {
+wstring BinaryLauncherBase::Rlocation(const wstring& path,
+                                      bool need_workspace_name) const {
   // If the manifest file map is empty, then we're using the runfiles directory
   // instead.
   if (manifest_file_map.empty()) {
@@ -143,106 +146,111 @@
       return path;
     }
 
-    string query_path = runfiles_dir;
+    wstring query_path = runfiles_dir;
     if (need_workspace_name) {
-      query_path += "/" + this->workspace_name;
+      query_path += L"/" + this->workspace_name;
     }
-    query_path += "/" + path;
+    query_path += L"/" + path;
     return query_path;
   }
 
-  string query_path = path;
+  wstring query_path = path;
   if (need_workspace_name) {
-    query_path = this->workspace_name + "/" + path;
+    query_path = this->workspace_name + L"/" + 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",
+    die(L"Rlocation failed on %s, path doesn't exist in MANIFEST file",
         query_path.c_str());
   }
   return entry->second;
 }
 
-string BinaryLauncherBase::GetLaunchInfoByKey(const string& key) {
+wstring BinaryLauncherBase::Rlocation(const string& path,
+                                      bool need_workspace_name) const {
+  return this->Rlocation(blaze_util::CstringToWstring(path),
+                         need_workspace_name);
+}
+
+wstring 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());
+    die(L"Cannot find key \"%hs\" from launch data.\n", key.c_str());
   }
   return item->second;
 }
 
-const vector<string>& BinaryLauncherBase::GetCommandlineArguments() const {
+const vector<wstring>& BinaryLauncherBase::GetCommandlineArguments() const {
   return this->commandline_arguments;
 }
 
 void BinaryLauncherBase::CreateCommandLine(
-    CmdLine* result, const string& executable,
-    const vector<string>& arguments) const {
-  ostringstream cmdline;
-  cmdline << '\"' << executable << '\"';
+    CmdLine* result, const wstring& executable,
+    const vector<wstring>& arguments) const {
+  wostringstream cmdline;
+  cmdline << L'\"' << executable << L'\"';
   for (const auto& s : arguments) {
-    cmdline << ' ' << s;
+    cmdline << L' ' << s;
   }
 
-  string cmdline_str = cmdline.str();
+  wstring cmdline_str = cmdline.str();
   if (cmdline_str.size() >= MAX_CMDLINE_LENGTH) {
-    die("Command line too long: %s", cmdline_str.c_str());
+    die(L"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);
+  wcsncpy(result->cmdline, cmdline_str.c_str(), MAX_CMDLINE_LENGTH - 1);
   result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0;
 }
 
 bool BinaryLauncherBase::PrintLauncherCommandLine(
-    const string& executable, const vector<string>& arguments) const {
+    const wstring& executable, const vector<wstring>& arguments) const {
   bool has_print_cmd_flag = false;
   for (const auto& arg : arguments) {
-    has_print_cmd_flag |= (arg == "--print_launcher_command");
+    has_print_cmd_flag |= (arg == L"--print_launcher_command");
   }
   if (has_print_cmd_flag) {
-    printf("%s\n", executable.c_str());
+    wprintf(L"%s\n", executable.c_str());
     for (const auto& arg : arguments) {
-      printf("%s\n", arg.c_str());
+      wprintf(L"%s\n", arg.c_str());
     }
   }
   return has_print_cmd_flag;
 }
 
-ExitCode BinaryLauncherBase::LaunchProcess(const string& executable,
-                                           const vector<string>& arguments,
+ExitCode BinaryLauncherBase::LaunchProcess(const wstring& executable,
+                                           const vector<wstring>& arguments,
                                            bool suppressOutput) const {
   if (PrintLauncherCommandLine(executable, arguments)) {
     return 0;
   }
-  if (manifest_file != "") {
-    SetEnv("RUNFILES_MANIFEST_ONLY", "1");
-    SetEnv("RUNFILES_MANIFEST_FILE", manifest_file);
+  if (!manifest_file.empty()) {
+    SetEnv(L"RUNFILES_MANIFEST_ONLY", L"1");
+    SetEnv(L"RUNFILES_MANIFEST_FILE", manifest_file);
   } else {
-    SetEnv("RUNFILES_DIR", runfiles_dir);
+    SetEnv(L"RUNFILES_DIR", runfiles_dir);
   }
   CmdLine cmdline;
   CreateCommandLine(&cmdline, executable, arguments);
   PROCESS_INFORMATION processInfo = {0};
-  STARTUPINFOA startupInfo = {0};
+  STARTUPINFOW startupInfo = {0};
   startupInfo.cb = sizeof(startupInfo);
-  BOOL ok = CreateProcessA(
+  BOOL ok = CreateProcessW(
       /* lpApplicationName */ NULL,
       /* lpCommandLine */ cmdline.cmdline,
       /* lpProcessAttributes */ NULL,
       /* lpThreadAttributes */ NULL,
       /* bInheritHandles */ FALSE,
       /* dwCreationFlags */
-          suppressOutput ? CREATE_NO_WINDOW  // no console window => no output
-                         : 0,
+      suppressOutput ? CREATE_NO_WINDOW  // no console window => no output
+                     : 0,
       /* lpEnvironment */ NULL,
       /* lpCurrentDirectory */ NULL,
       /* lpStartupInfo */ &startupInfo,
       /* lpProcessInformation */ &processInfo);
   if (!ok) {
-    PrintError("Cannot launch process: %s\nReason: %s",
-               cmdline.cmdline,
+    PrintError(L"Cannot launch process: %s\nReason: %hs", cmdline.cmdline,
                GetLastErrorString().c_str());
     return GetLastError();
   }
diff --git a/src/tools/launcher/launcher.h b/src/tools/launcher/launcher.h
index 4dca6e0..107ac17 100644
--- a/src/tools/launcher/launcher.h
+++ b/src/tools/launcher/launcher.h
@@ -32,21 +32,21 @@
 static const int MAX_CMDLINE_LENGTH = 32768;
 
 struct CmdLine {
-  char cmdline[MAX_CMDLINE_LENGTH];
+  wchar_t cmdline[MAX_CMDLINE_LENGTH];
 };
 
 class BinaryLauncherBase {
-  typedef std::unordered_map<std::string, std::string> ManifestFileMap;
+  typedef std::unordered_map<std::wstring, std::wstring> ManifestFileMap;
 
  public:
   BinaryLauncherBase(const LaunchDataParser::LaunchInfo& launch_info, int argc,
-                     char* argv[]);
+                     wchar_t* argv[]);
 
   // Get launch information based on a launch info key.
-  std::string GetLaunchInfoByKey(const std::string& key);
+  std::wstring GetLaunchInfoByKey(const std::string& key);
 
   // Get the original command line arguments passed to this binary.
-  const std::vector<std::string>& GetCommandlineArguments() const;
+  const std::vector<std::wstring>& GetCommandlineArguments() const;
 
   // Map a runfile path to its absolute path.
   //
@@ -54,8 +54,10 @@
   // 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;
+  std::wstring Rlocation(const std::wstring& path,
+                         bool need_workspace_name = true) const;
+  std::wstring 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
@@ -65,8 +67,8 @@
   // arguments:  the command line arguments to be passed to the exectuable,
   //             it doesn't include the exectuable itself.
   //             The arguments are expected to be quoted if having spaces.
-  ExitCode LaunchProcess(const std::string& executable,
-                         const std::vector<std::string>& arguments,
+  ExitCode LaunchProcess(const std::wstring& executable,
+                         const std::vector<std::wstring>& arguments,
                          bool suppressOutput = false) const;
 
   // A launch function to be implemented for a specific language.
@@ -76,54 +78,54 @@
   //
   // The method appends ".exe.runfiles" to the first command line argument,
   // converts forward slashes to back slashes, then returns that.
-  std::string GetRunfilesPath() const;
+  std::wstring GetRunfilesPath() const;
 
  private:
   // A map to store all the launch information.
   const LaunchDataParser::LaunchInfo& launch_info;
 
   // Absolute path to the runfiles manifest file, if one exists.
-  const std::string manifest_file;
+  const std::wstring manifest_file;
 
   // Path to the runfiles directory, if one exists.
-  const std::string runfiles_dir;
+  const std::wstring runfiles_dir;
 
   // The commandline arguments recieved.
   // The first argument is the path of this launcher itself.
-  std::vector<std::string> commandline_arguments;
+  std::vector<std::wstring> commandline_arguments;
 
   // The workspace name of the repository this target belongs to.
-  const std::string workspace_name;
+  const std::wstring workspace_name;
 
   // A map to store all entries of the manifest file.
-  std::unordered_map<std::string, std::string> manifest_file_map;
+  ManifestFileMap 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;
+      const std::wstring& executable,
+      const std::vector<std::wstring>& arguments) const;
 
   // 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;
+  void CreateCommandLine(CmdLine* result, const std::wstring& executable,
+                         const std::vector<std::wstring>& 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
-  static std::string FindManifestFile(const char* argv0);
+  static std::wstring FindManifestFile(const wchar_t* argv0);
 
   // Parse manifest file into a map
   static void ParseManifestFile(ManifestFileMap* manifest_file_map,
-                                const std::string& manifest_path);
+                                const std::wstring& manifest_path);
 };
 
 }  // namespace launcher
diff --git a/src/tools/launcher/launcher_main.cc b/src/tools/launcher/launcher_main.cc
index 6f9133b..5e0e142 100644
--- a/src/tools/launcher/launcher_main.cc
+++ b/src/tools/launcher/launcher_main.cc
@@ -33,30 +33,30 @@
 using std::make_unique;
 using std::unique_ptr;
 
-int main(int argc, char* argv[]) {
+int wmain(int argc, wchar_t* argv[]) {
   LaunchDataParser::LaunchInfo launch_info;
 
   if (!LaunchDataParser::GetLaunchInfo(GetBinaryPathWithExtension(argv[0]),
                                        &launch_info)) {
-    die("Failed to parse launch info.");
+    die(L"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);
+    die(L"Cannot find key \"%hs\" from launch data.", BINARY_TYPE);
   }
 
   unique_ptr<BinaryLauncherBase> binary_launcher;
 
-  if (result->second == "Python") {
+  if (result->second == L"Python") {
     binary_launcher =
         make_unique<PythonBinaryLauncher>(launch_info, argc, argv);
-  } else if (result->second == "Bash") {
+  } else if (result->second == L"Bash") {
     binary_launcher = make_unique<BashBinaryLauncher>(launch_info, argc, argv);
-  } else if (result->second == "Java") {
+  } else if (result->second == L"Java") {
     binary_launcher = make_unique<JavaBinaryLauncher>(launch_info, argc, argv);
   } else {
-    die("Unknown binary type, cannot launch anything.");
+    die(L"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
index 08533ff..a0f8775 100644
--- a/src/tools/launcher/python_launcher.cc
+++ b/src/tools/launcher/python_launcher.cc
@@ -21,21 +21,21 @@
 namespace bazel {
 namespace launcher {
 
-using std::string;
 using std::vector;
+using std::wstring;
 
 static constexpr const char* PYTHON_BIN_PATH = "python_bin_path";
 
 ExitCode PythonBinaryLauncher::Launch() {
-  string python_binary = this->GetLaunchInfoByKey(PYTHON_BIN_PATH);
+  wstring python_binary = this->GetLaunchInfoByKey(PYTHON_BIN_PATH);
   // If specified python binary path doesn't exist, then fall back to
   // python.exe and hope it's in PATH.
   if (!DoesFilePathExist(python_binary.c_str())) {
-    python_binary = "python.exe";
+    python_binary = L"python.exe";
   }
 
-  vector<string> args = this->GetCommandlineArguments();
-  string python_zip_file = GetBinaryPathWithoutExtension(args[0]) + ".zip";
+  vector<wstring> args = this->GetCommandlineArguments();
+  wstring python_zip_file = GetBinaryPathWithoutExtension(args[0]) + L".zip";
 
   // Replace the first argument with python zip file path
   args[0] = python_zip_file;
diff --git a/src/tools/launcher/python_launcher.h b/src/tools/launcher/python_launcher.h
index 1a21ca3..ab624da 100644
--- a/src/tools/launcher/python_launcher.h
+++ b/src/tools/launcher/python_launcher.h
@@ -23,8 +23,8 @@
 class PythonBinaryLauncher : public BinaryLauncherBase {
  public:
   PythonBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info,
-                       int argc, char* argv[])
-      : BinaryLauncherBase(launch_info, argc, argv){}
+                       int argc, wchar_t* argv[])
+      : BinaryLauncherBase(launch_info, argc, argv) {}
   ExitCode Launch();
 };
 
diff --git a/src/tools/launcher/util/data_parser.cc b/src/tools/launcher/util/data_parser.cc
index 98c8b1d..cb77abe 100644
--- a/src/tools/launcher/util/data_parser.cc
+++ b/src/tools/launcher/util/data_parser.cc
@@ -17,6 +17,7 @@
 #include <string>
 #include <unordered_map>
 
+#include "src/main/cpp/util/strings.h"
 #include "src/tools/launcher/util/data_parser.h"
 #include "src/tools/launcher/util/launcher_util.h"
 
@@ -28,6 +29,7 @@
 using std::make_unique;
 using std::string;
 using std::unique_ptr;
+using std::wstring;
 
 int64_t LaunchDataParser::ReadDataSize(ifstream* binary) {
   int64_t data_size;
@@ -63,34 +65,34 @@
       end++;
     }
     if (equal == -1) {
-      PrintError("Cannot find equal symbol in line: %s",
+      PrintError(L"Cannot find equal symbol in line: %hs",
                  string(launch_data + start, end - start).c_str());
       return false;
     } else if (start == equal) {
-      PrintError("Key is empty string in line: %s",
+      PrintError(L"Key is empty string in line: %hs",
                  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());
+        PrintError(L"Duplicated launch info key: %hs", key.c_str());
         return false;
       }
-      launch_info->insert(make_pair(key, value));
+      launch_info->insert(make_pair(key, blaze_util::CstringToWstring(value)));
     }
     start = end + 1;
   }
   return true;
 }
 
-bool LaunchDataParser::GetLaunchInfo(const string& binary_path,
+bool LaunchDataParser::GetLaunchInfo(const wstring& 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!");
+    PrintError(L"No data appended, cannot launch anything!");
     return false;
   }
   unique_ptr<char[]> launch_data(new char[data_size]);
diff --git a/src/tools/launcher/util/data_parser.h b/src/tools/launcher/util/data_parser.h
index ecaf12a..586da42 100644
--- a/src/tools/launcher/util/data_parser.h
+++ b/src/tools/launcher/util/data_parser.h
@@ -25,10 +25,10 @@
 
 class LaunchDataParser {
  public:
-  typedef std::unordered_map<std::string, std::string> LaunchInfo;
+  typedef std::unordered_map<std::string, std::wstring> LaunchInfo;
   LaunchDataParser() = delete;
   ~LaunchDataParser() = delete;
-  static bool GetLaunchInfo(const std::string& binary_path,
+  static bool GetLaunchInfo(const std::wstring& binary_path,
                             LaunchInfo* launch_info);
 
  private:
diff --git a/src/tools/launcher/util/data_parser_test.cc b/src/tools/launcher/util/data_parser_test.cc
index 1adaf3a..be270aa 100644
--- a/src/tools/launcher/util/data_parser_test.cc
+++ b/src/tools/launcher/util/data_parser_test.cc
@@ -18,6 +18,7 @@
 #include <memory>
 #include <vector>
 
+#include "src/main/cpp/util/strings.h"
 #include "src/tools/launcher/util/data_parser.h"
 #include "gtest/gtest.h"
 #include "src/tools/launcher/util/launcher_util.h"
@@ -89,7 +90,8 @@
   static bool ParseBinaryFile(
       const string& binary_file,
       LaunchDataParser::LaunchInfo* parsed_launch_info) {
-    if (LaunchDataParser::GetLaunchInfo(binary_file, parsed_launch_info)) {
+    if (LaunchDataParser::GetLaunchInfo(
+        blaze_util::CstringToWstring(binary_file), parsed_launch_info)) {
       return true;
     }
     exit(-1);
@@ -100,7 +102,7 @@
     if (item == parsed_launch_info->end()) {
       return "Cannot find key: " + key;
     }
-    return item->second;
+    return blaze_util::WstringToString(item->second);
   }
 
   string test_tmpdir;
diff --git a/src/tools/launcher/util/launcher_util.cc b/src/tools/launcher/util/launcher_util.cc
index 3509ce3..f14009c 100644
--- a/src/tools/launcher/util/launcher_util.cc
+++ b/src/tools/launcher/util/launcher_util.cc
@@ -14,6 +14,8 @@
 
 // For rand_s function, https://msdn.microsoft.com/en-us/library/sxtz2fa8.aspx
 #define _CRT_RAND_S
+#include <fcntl.h>
+#include <io.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -29,10 +31,10 @@
 namespace bazel {
 namespace launcher {
 
-using std::ostringstream;
 using std::string;
-using std::wstring;
 using std::stringstream;
+using std::wostringstream;
+using std::wstring;
 
 string GetLastErrorString() {
   DWORD last_error = GetLastError();
@@ -53,88 +55,99 @@
   return result.str();
 }
 
-void die(const char* format, ...) {
+void die(const wchar_t* format, ...) {
+  // Set translation mode to _O_U8TEXT so that we can display
+  // error message containing unicode correctly.
+  _setmode(_fileno(stderr), _O_U8TEXT);
   va_list ap;
   va_start(ap, format);
-  fputs("LAUNCHER ERROR: ", stderr);
-  vfprintf(stderr, format, ap);
+  fputws(L"LAUNCHER ERROR: ", stderr);
+  vfwprintf(stderr, format, ap);
   va_end(ap);
-  fputc('\n', stderr);
+  fputwc(L'\n', stderr);
   exit(1);
 }
 
-void PrintError(const char* format, ...) {
+void PrintError(const wchar_t* format, ...) {
+  // Set translation mode to _O_U8TEXT so that we can display
+  // error message containing unicode correctly.
+  // _setmode returns -1 if it fails to set the mode.
+  int previous_mode = _setmode(_fileno(stderr), _O_U8TEXT);
   va_list ap;
   va_start(ap, format);
-  fputs("LAUNCHER ERROR: ", stderr);
-  vfprintf(stderr, format, ap);
+  fputws(L"LAUNCHER ERROR: ", stderr);
+  vfwprintf(stderr, format, ap);
   va_end(ap);
-  fputc('\n', stderr);
+  fputwc(L'\n', stderr);
+  // Set translation mode back to the original one if it's changed.
+  if (previous_mode != -1) {
+    _setmode(_fileno(stderr), previous_mode);
+  }
 }
 
-wstring AsAbsoluteWindowsPath(const char* path) {
+wstring AsAbsoluteWindowsPath(const wchar_t* path) {
   wstring wpath;
   string error;
   if (!blaze_util::AsAbsoluteWindowsPath(path, &wpath, &error)) {
-    die("Couldn't convert %s to absolute Windows path: %s", path,
+    die(L"Couldn't convert %s to absolute Windows path: %hs", path,
         error.c_str());
   }
   return wpath;
 }
 
-bool DoesFilePathExist(const char* path) {
+bool DoesFilePathExist(const wchar_t* path) {
   DWORD dwAttrib = GetFileAttributesW(AsAbsoluteWindowsPath(path).c_str());
 
   return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
           !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
 }
 
-bool DoesDirectoryPathExist(const char* path) {
+bool DoesDirectoryPathExist(const wchar_t* path) {
   DWORD dwAttrib = GetFileAttributesW(AsAbsoluteWindowsPath(path).c_str());
 
   return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
           (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
 }
 
-bool DeleteFileByPath(const char* path) {
+bool DeleteFileByPath(const wchar_t* path) {
   return DeleteFileW(AsAbsoluteWindowsPath(path).c_str());
 }
 
-bool DeleteDirectoryByPath(const char* path) {
+bool DeleteDirectoryByPath(const wchar_t* path) {
   return RemoveDirectoryW(AsAbsoluteWindowsPath(path).c_str());
 }
 
-string GetBinaryPathWithoutExtension(const string& binary) {
-  if (binary.find(".exe", binary.size() - 4) != string::npos) {
+wstring GetBinaryPathWithoutExtension(const wstring& binary) {
+  if (binary.find(L".exe", binary.size() - 4) != wstring::npos) {
     return binary.substr(0, binary.length() - 4);
   }
   return binary;
 }
 
-string GetBinaryPathWithExtension(const string& binary) {
-  return GetBinaryPathWithoutExtension(binary) + ".exe";
+wstring GetBinaryPathWithExtension(const wstring& binary) {
+  return GetBinaryPathWithoutExtension(binary) + L".exe";
 }
 
-string GetEscapedArgument(const string& argument, bool escape_backslash) {
-  string escaped_arg;
+wstring GetEscapedArgument(const wstring& argument, bool escape_backslash) {
+  wstring escaped_arg;
   // escaped_arg will be at least this long
   escaped_arg.reserve(argument.size());
-  bool has_space = argument.find_first_of(' ') != string::npos;
+  bool has_space = argument.find_first_of(L' ') != wstring::npos;
 
   if (has_space) {
-    escaped_arg += '\"';
+    escaped_arg += L'\"';
   }
 
-  for (const char ch : argument) {
+  for (const wchar_t ch : argument) {
     switch (ch) {
-      case '"':
+      case L'"':
         // Escape double quotes
-        escaped_arg += "\\\"";
+        escaped_arg += L"\\\"";
         break;
 
-      case '\\':
+      case L'\\':
         // Escape back slashes if escape_backslash is true
-        escaped_arg += (escape_backslash ? "\\\\" : "\\");
+        escaped_arg += (escape_backslash ? L"\\\\" : L"\\");
         break;
 
       default:
@@ -143,7 +156,7 @@
   }
 
   if (has_space) {
-    escaped_arg += '\"';
+    escaped_arg += L'\"';
   }
   return escaped_arg;
 }
@@ -152,54 +165,54 @@
 // 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)) {
+bool GetEnv(const wstring& env_name, wstring* value) {
+  wchar_t buffer[BUFFER_SIZE];
+  if (!GetEnvironmentVariableW(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());
+bool SetEnv(const wstring& env_name, const wstring& value) {
+  return SetEnvironmentVariableW(env_name.c_str(), value.c_str());
 }
 
-string GetRandomStr(size_t len) {
-  static const char alphabet[] =
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-  string rand_str;
+wstring GetRandomStr(size_t len) {
+  static const wchar_t alphabet[] =
+      L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+  wstring rand_str;
   rand_str.reserve(len);
   unsigned int x;
   for (size_t i = 0; i < len; i++) {
     rand_s(&x);
-    rand_str += alphabet[x % strlen(alphabet)];
+    rand_str += alphabet[x % wcslen(alphabet)];
   }
   return rand_str;
 }
 
-bool NormalizePath(const string& path, string* result) {
+bool NormalizePath(const wstring& path, wstring* result) {
   string error;
   if (!blaze_util::AsWindowsPath(path, result, &error)) {
-    PrintError("Failed to normalize %s: %s", path.c_str(), error.c_str());
+    PrintError(L"Failed to normalize %s: %hs", path.c_str(), error.c_str());
     return false;
   }
   std::transform(result->begin(), result->end(), result->begin(), ::tolower);
   return true;
 }
 
-string GetBaseNameFromPath(const string& path) {
-  return path.substr(path.find_last_of("\\/") + 1);
+wstring GetBaseNameFromPath(const wstring& path) {
+  return path.substr(path.find_last_of(L"\\/") + 1);
 }
 
-string GetParentDirFromPath(const string& path) {
-  return path.substr(0, path.find_last_of("\\/"));
+wstring GetParentDirFromPath(const wstring& path) {
+  return path.substr(0, path.find_last_of(L"\\/"));
 }
 
-bool RelativeTo(const string& path, const string& base, string* result) {
+bool RelativeTo(const wstring& path, const wstring& base, wstring* result) {
   if (blaze_util::IsAbsolute(path) != blaze_util::IsAbsolute(base)) {
     PrintError(
-        "Cannot calculate relative path from an absolute and a non-absolute"
+        L"Cannot calculate relative path from an absolute and a non-absolute"
         " path.\npath = %s\nbase = %s",
         path.c_str(), base.c_str());
     return false;
@@ -208,7 +221,7 @@
   if (blaze_util::IsAbsolute(path) && blaze_util::IsAbsolute(base) &&
       path[0] != base[0]) {
     PrintError(
-        "Cannot calculate relative path from absolute path under different "
+        L"Cannot calculate relative path from absolute path under different "
         "drives."
         "\npath = %s\nbase = %s",
         path.c_str(), base.c_str());
@@ -218,21 +231,21 @@
   // Record the back slash position after the last matched path fragment
   int pos = 0;
   int back_slash_pos = -1;
-  while (path[pos] == base[pos] && base[pos] != '\0') {
-    if (path[pos] == '\\') {
+  while (path[pos] == base[pos] && base[pos] != L'\0') {
+    if (path[pos] == L'\\') {
       back_slash_pos = pos;
     }
     pos++;
   }
 
-  if (base[pos] == '\0' && path[pos] == '\0') {
+  if (base[pos] == L'\0' && path[pos] == L'\0') {
     // base == path in this case
-    result->assign("");
+    result->assign(L"");
     return true;
   }
 
-  if ((base[pos] == '\0' && path[pos] == '\\') ||
-      (base[pos] == '\\' && path[pos] == '\0')) {
+  if ((base[pos] == L'\0' && path[pos] == L'\\') ||
+      (base[pos] == L'\\' && path[pos] == L'\0')) {
     // In this case, one of the paths is the parent of another one.
     // We should move back_slash_pos to the end of the shorter path.
     // eg. path = c:\foo\bar, base = c:\foo => back_slash_pos = 6
@@ -240,7 +253,7 @@
     back_slash_pos = pos;
   }
 
-  ostringstream buffer;
+  wostringstream buffer;
 
   // Create the ..\\ prefix
   // eg. path = C:\foo\bar1, base = C:\foo\bar2, we need ..\ prefix
@@ -249,11 +262,11 @@
   // back_slash_pos + 1 == base.length() is not possible because the last
   // character of a normalized path won't be back slash.
   if (back_slash_pos + 1 < base.length()) {
-    buffer << "..\\";
+    buffer << L"..\\";
   }
   for (int i = back_slash_pos + 1; i < base.length(); i++) {
-    if (base[i] == '\\') {
-      buffer << "..\\";
+    if (base[i] == L'\\') {
+      buffer << L"..\\";
     }
   }
 
diff --git a/src/tools/launcher/util/launcher_util.h b/src/tools/launcher/util/launcher_util.h
index 760eb72..deb4065 100644
--- a/src/tools/launcher/util/launcher_util.h
+++ b/src/tools/launcher/util/launcher_util.h
@@ -25,77 +25,78 @@
 std::string GetLastErrorString();
 
 // Prints the specified error message and exits nonzero.
-__declspec(noreturn) void die(const char* format, ...) PRINTF_ATTRIBUTE(1, 2);
+__declspec(noreturn) void die(const wchar_t* format, ...)
+    PRINTF_ATTRIBUTE(1, 2);
 
 // Prints the specified error message.
-void PrintError(const char* format, ...) PRINTF_ATTRIBUTE(1, 2);
+void PrintError(const wchar_t* 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);
+std::wstring GetBinaryPathWithoutExtension(const std::wstring& 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);
+std::wstring GetBinaryPathWithExtension(const std::wstring& binary);
 
 // Escape a command line argument.
 //
 // If the argument has space, then we quote it.
 // Escape " to \"
 // Escape \ to \\ if escape_backslash is true
-std::string GetEscapedArgument(const std::string& argument,
-                               bool escape_backslash);
+std::wstring GetEscapedArgument(const std::wstring& argument,
+                                bool escape_backslash);
 
 // Convert a path to an absolute Windows path with \\?\ prefix.
 // This method will print an error and exit if it cannot convert the path.
-std::wstring AsAbsoluteWindowsPath(const char* path);
+std::wstring AsAbsoluteWindowsPath(const wchar_t* path);
 
 // Check if a file exists at a given path.
-bool DoesFilePathExist(const char* path);
+bool DoesFilePathExist(const wchar_t* path);
 
 // Check if a directory exists at a given path.
-bool DoesDirectoryPathExist(const char* path);
+bool DoesDirectoryPathExist(const wchar_t* path);
 
 // Delete a file at a given path.
-bool DeleteFileByPath(const char* path);
+bool DeleteFileByPath(const wchar_t* path);
 
 // Delete a directory at a given path,.
 // If it's a real directory, it must be empty
 // If it's a junction, the target directory it points to doesn't have to be
 // empty, the junction will be deleted regardless of the state of the target.
-bool DeleteDirectoryByPath(const char* path);
+bool DeleteDirectoryByPath(const wchar_t* 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);
+bool GetEnv(const std::wstring& env_name, std::wstring* 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);
+bool SetEnv(const std::wstring& env_name, const std::wstring& value);
 
 // Return a random string with a given length.
 // The string consists of a-zA-Z0-9
-std::string GetRandomStr(size_t len);
+std::wstring GetRandomStr(size_t len);
 
 // Normalize a path to a Windows path in lower case
-bool NormalizePath(const std::string& path, std::string* result);
+bool NormalizePath(const std::wstring& path, std::wstring* result);
 
 // Get the base name from a normalized absoulute path
-std::string GetBaseNameFromPath(const std::string& path);
+std::wstring GetBaseNameFromPath(const std::wstring& path);
 
 // Get parent directory from a normalized absoulute path
-std::string GetParentDirFromPath(const std::string& path);
+std::wstring GetParentDirFromPath(const std::wstring& path);
 
 // Calculate a relative path from `path` to `base`.
 // This function expects normalized Windows path in lower case.
 // `path` and `base` should be both absolute or both relative.
-bool RelativeTo(const std::string& path, const std::string& base,
-                std::string* result);
+bool RelativeTo(const std::wstring& path, const std::wstring& base,
+                std::wstring* result);
 
 }  // 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 f8ddf80..82b3f98 100644
--- a/src/tools/launcher/util/launcher_util_test.cc
+++ b/src/tools/launcher/util/launcher_util_test.cc
@@ -18,6 +18,7 @@
 #include <iostream>
 #include <string>
 
+#include "src/main/cpp/util/strings.h"
 #include "src/tools/launcher/util/launcher_util.h"
 #include "gtest/gtest.h"
 
@@ -28,6 +29,7 @@
 using std::ios;
 using std::ofstream;
 using std::string;
+using std::wstring;
 
 class LaunchUtilTest : public ::testing::Test {
  protected:
@@ -38,141 +40,142 @@
   void SetUp() override {
     char* tmpdir = getenv("TEST_TMPDIR");
     if (tmpdir != NULL) {
-      test_tmpdir = string(tmpdir);
+      test_tmpdir = blaze_util::CstringToWstring(string(tmpdir));
     } else {
       tmpdir = getenv("TEMP");
       ASSERT_FALSE(tmpdir == NULL);
-      test_tmpdir = string(tmpdir);
+      test_tmpdir = blaze_util::CstringToWstring(string(tmpdir));
     }
   }
 
   void TearDown() override {}
 
-  string GetTmpDir() { return this->test_tmpdir; }
+  wstring GetTmpDir() { return this->test_tmpdir; }
 
   // Create an empty file at path
-  static void CreateEmptyFile(const string& path) {
+  static void CreateEmptyFile(const wstring& path) {
     ofstream file_stream(path.c_str(), ios::out | ios::binary);
     file_stream.put('\0');
   }
 
  private:
-  string test_tmpdir;
+  wstring 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"));
+  ASSERT_EQ(L"foo", GetBinaryPathWithoutExtension(L"foo.exe"));
+  ASSERT_EQ(L"foo.sh", GetBinaryPathWithoutExtension(L"foo.sh.exe"));
+  ASSERT_EQ(L"foo.sh", GetBinaryPathWithoutExtension(L"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"));
+  ASSERT_EQ(L"foo.exe", GetBinaryPathWithExtension(L"foo"));
+  ASSERT_EQ(L"foo.sh.exe", GetBinaryPathWithExtension(L"foo.sh.exe"));
+  ASSERT_EQ(L"foo.sh.exe", GetBinaryPathWithExtension(L"foo.sh"));
 }
 
 TEST_F(LaunchUtilTest, GetEscapedArgumentTest) {
-  ASSERT_EQ("foo", GetEscapedArgument("foo", true));
-  ASSERT_EQ("\"foo bar\"", GetEscapedArgument("foo bar", true));
-  ASSERT_EQ("\"\\\"foo bar\\\"\"", GetEscapedArgument("\"foo bar\"", true));
-  ASSERT_EQ("foo\\\\bar", GetEscapedArgument("foo\\bar", true));
-  ASSERT_EQ("foo\\\"bar", GetEscapedArgument("foo\"bar", true));
-  ASSERT_EQ("C:\\\\foo\\\\bar\\\\", GetEscapedArgument("C:\\foo\\bar\\", true));
-  ASSERT_EQ("\"C:\\\\foo foo\\\\bar\\\\\"",
-            GetEscapedArgument("C:\\foo foo\\bar\\", true));
+  ASSERT_EQ(L"foo", GetEscapedArgument(L"foo", true));
+  ASSERT_EQ(L"\"foo bar\"", GetEscapedArgument(L"foo bar", true));
+  ASSERT_EQ(L"\"\\\"foo bar\\\"\"", GetEscapedArgument(L"\"foo bar\"", true));
+  ASSERT_EQ(L"foo\\\\bar", GetEscapedArgument(L"foo\\bar", true));
+  ASSERT_EQ(L"foo\\\"bar", GetEscapedArgument(L"foo\"bar", true));
+  ASSERT_EQ(L"C:\\\\foo\\\\bar\\\\",
+            GetEscapedArgument(L"C:\\foo\\bar\\", true));
+  ASSERT_EQ(L"\"C:\\\\foo foo\\\\bar\\\\\"",
+            GetEscapedArgument(L"C:\\foo foo\\bar\\", true));
 
-  ASSERT_EQ("foo\\bar", GetEscapedArgument("foo\\bar", false));
-  ASSERT_EQ("C:\\foo\\bar\\", GetEscapedArgument("C:\\foo\\bar\\", false));
-  ASSERT_EQ("\"C:\\foo foo\\bar\\\"",
-            GetEscapedArgument("C:\\foo foo\\bar\\", false));
+  ASSERT_EQ(L"foo\\bar", GetEscapedArgument(L"foo\\bar", false));
+  ASSERT_EQ(L"C:\\foo\\bar\\", GetEscapedArgument(L"C:\\foo\\bar\\", false));
+  ASSERT_EQ(L"\"C:\\foo foo\\bar\\\"",
+            GetEscapedArgument(L"C:\\foo foo\\bar\\", false));
 }
 
 TEST_F(LaunchUtilTest, DoesFilePathExistTest) {
-  string file1 = GetTmpDir() + "/foo";
-  string file2 = GetTmpDir() + "/bar";
+  wstring file1 = GetTmpDir() + L"/foo";
+  wstring file2 = GetTmpDir() + L"/bar";
   CreateEmptyFile(file1);
   ASSERT_TRUE(DoesFilePathExist(file1.c_str()));
   ASSERT_FALSE(DoesFilePathExist(file2.c_str()));
 }
 
 TEST_F(LaunchUtilTest, DoesDirectoryPathExistTest) {
-  string dir1 = GetTmpDir() + "/dir1";
-  string dir2 = GetTmpDir() + "/dir2";
-  CreateDirectory(dir1.c_str(), NULL);
+  wstring dir1 = GetTmpDir() + L"/dir1";
+  wstring dir2 = GetTmpDir() + L"/dir2";
+  CreateDirectoryW(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));
+  ASSERT_TRUE(SetEnv(L"foo", L"bar"));
+  wstring value;
+  ASSERT_TRUE(GetEnv(L"foo", &value));
+  ASSERT_EQ(value, L"bar");
+  SetEnv(L"FOO", L"");
+  ASSERT_FALSE(GetEnv(L"FOO", &value));
 }
 
 TEST_F(LaunchUtilTest, NormalizePathTest) {
-  string value;
-  ASSERT_TRUE(NormalizePath("C:\\foo\\bar\\", &value));
-  ASSERT_EQ("c:\\foo\\bar", value);
-  ASSERT_TRUE(NormalizePath("c:/foo/bar/", &value));
-  ASSERT_EQ("c:\\foo\\bar", value);
-  ASSERT_TRUE(NormalizePath("FoO\\\\bAr\\", &value));
-  ASSERT_EQ("foo\\bar", value);
-  ASSERT_TRUE(NormalizePath("X\\Y/Z\\", &value));
-  ASSERT_EQ("x\\y\\z", value);
-  ASSERT_TRUE(NormalizePath("c://foo//bar", &value));
-  ASSERT_EQ("c:\\foo\\bar", value);
-  ASSERT_FALSE(NormalizePath("c:foo\\bar", &value));
+  wstring value;
+  ASSERT_TRUE(NormalizePath(L"C:\\foo\\bar\\", &value));
+  ASSERT_EQ(L"c:\\foo\\bar", value);
+  ASSERT_TRUE(NormalizePath(L"c:/foo/bar/", &value));
+  ASSERT_EQ(L"c:\\foo\\bar", value);
+  ASSERT_TRUE(NormalizePath(L"FoO\\\\bAr\\", &value));
+  ASSERT_EQ(L"foo\\bar", value);
+  ASSERT_TRUE(NormalizePath(L"X\\Y/Z\\", &value));
+  ASSERT_EQ(L"x\\y\\z", value);
+  ASSERT_TRUE(NormalizePath(L"c://foo//bar", &value));
+  ASSERT_EQ(L"c:\\foo\\bar", value);
+  ASSERT_FALSE(NormalizePath(L"c:foo\\bar", &value));
 }
 
 TEST_F(LaunchUtilTest, RelativeToTest) {
-  string value;
-  ASSERT_TRUE(RelativeTo("c:\\foo\\bar1", "c:\\foo\\bar2", &value));
-  ASSERT_EQ("..\\bar1", value);
-  ASSERT_TRUE(RelativeTo("c:\\foo\\bar", "c:\\", &value));
-  ASSERT_EQ("foo\\bar", value);
-  ASSERT_TRUE(RelativeTo("c:\\foo\\bar", "c:\\foo\\bar", &value));
-  ASSERT_EQ("", value);
-  ASSERT_TRUE(RelativeTo("c:\\foo\\bar", "c:\\foo", &value));
-  ASSERT_EQ("bar", value);
-  ASSERT_TRUE(RelativeTo("c:\\foo\\bar", "c:\\foo\\ba", &value));
-  ASSERT_EQ("..\\bar", value);
-  ASSERT_TRUE(RelativeTo("c:\\", "c:\\foo", &value));
-  ASSERT_EQ("..\\", value);
-  ASSERT_TRUE(RelativeTo("c:\\", "c:\\a\\b\\c", &value));
-  ASSERT_EQ("..\\..\\..\\", value);
-  ASSERT_TRUE(RelativeTo("c:\\aa\\bb\\cc", "c:\\a\\b", &value));
-  ASSERT_EQ("..\\..\\aa\\bb\\cc", value);
+  wstring value;
+  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar1", L"c:\\foo\\bar2", &value));
+  ASSERT_EQ(L"..\\bar1", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\", &value));
+  ASSERT_EQ(L"foo\\bar", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\foo\\bar", &value));
+  ASSERT_EQ(L"", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\foo", &value));
+  ASSERT_EQ(L"bar", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\foo\\ba", &value));
+  ASSERT_EQ(L"..\\bar", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\", L"c:\\foo", &value));
+  ASSERT_EQ(L"..\\", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\", L"c:\\a\\b\\c", &value));
+  ASSERT_EQ(L"..\\..\\..\\", value);
+  ASSERT_TRUE(RelativeTo(L"c:\\aa\\bb\\cc", L"c:\\a\\b", &value));
+  ASSERT_EQ(L"..\\..\\aa\\bb\\cc", value);
 
-  ASSERT_TRUE(RelativeTo("foo\\bar", "foo\\bar", &value));
-  ASSERT_EQ("", value);
-  ASSERT_TRUE(RelativeTo("foo\\bar1", "foo\\bar2", &value));
-  ASSERT_EQ("..\\bar1", value);
-  ASSERT_TRUE(RelativeTo("foo\\bar1", "foo\\bar", &value));
-  ASSERT_EQ("..\\bar1", value);
-  ASSERT_TRUE(RelativeTo("foo\\bar1", "foo", &value));
-  ASSERT_EQ("bar1", value);
-  ASSERT_TRUE(RelativeTo("foo\\bar1", "fo", &value));
-  ASSERT_EQ("..\\foo\\bar1", value);
-  ASSERT_TRUE(RelativeTo("foo\\ba", "foo\\bar", &value));
-  ASSERT_EQ("..\\ba", value);
-  ASSERT_TRUE(RelativeTo("foo", "foo\\bar", &value));
-  ASSERT_EQ("..\\", value);
-  ASSERT_TRUE(RelativeTo("fo", "foo\\bar", &value));
-  ASSERT_EQ("..\\..\\fo", value);
-  ASSERT_TRUE(RelativeTo("", "foo\\bar", &value));
-  ASSERT_EQ("..\\..\\", value);
-  ASSERT_TRUE(RelativeTo("foo\\bar", "", &value));
-  ASSERT_EQ("foo\\bar", value);
-  ASSERT_TRUE(RelativeTo("a\\b\\c", "x\\y", &value));
-  ASSERT_EQ("..\\..\\a\\b\\c", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\bar", L"foo\\bar", &value));
+  ASSERT_EQ(L"", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"foo\\bar2", &value));
+  ASSERT_EQ(L"..\\bar1", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"foo\\bar", &value));
+  ASSERT_EQ(L"..\\bar1", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"foo", &value));
+  ASSERT_EQ(L"bar1", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"fo", &value));
+  ASSERT_EQ(L"..\\foo\\bar1", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\ba", L"foo\\bar", &value));
+  ASSERT_EQ(L"..\\ba", value);
+  ASSERT_TRUE(RelativeTo(L"foo", L"foo\\bar", &value));
+  ASSERT_EQ(L"..\\", value);
+  ASSERT_TRUE(RelativeTo(L"fo", L"foo\\bar", &value));
+  ASSERT_EQ(L"..\\..\\fo", value);
+  ASSERT_TRUE(RelativeTo(L"", L"foo\\bar", &value));
+  ASSERT_EQ(L"..\\..\\", value);
+  ASSERT_TRUE(RelativeTo(L"foo\\bar", L"", &value));
+  ASSERT_EQ(L"foo\\bar", value);
+  ASSERT_TRUE(RelativeTo(L"a\\b\\c", L"x\\y", &value));
+  ASSERT_EQ(L"..\\..\\a\\b\\c", value);
 
-  ASSERT_FALSE(RelativeTo("c:\\foo\\bar1", "foo\\bar2", &value));
-  ASSERT_FALSE(RelativeTo("c:foo\\bar1", "c:\\foo\\bar2", &value));
-  ASSERT_FALSE(RelativeTo("c:\\foo\\bar1", "d:\\foo\\bar2", &value));
+  ASSERT_FALSE(RelativeTo(L"c:\\foo\\bar1", L"foo\\bar2", &value));
+  ASSERT_FALSE(RelativeTo(L"c:foo\\bar1", L"c:\\foo\\bar2", &value));
+  ASSERT_FALSE(RelativeTo(L"c:\\foo\\bar1", L"d:\\foo\\bar2", &value));
 }
 
 }  // namespace launcher