diff options
author | Yun Peng <pcloudy@google.com> | 2018-05-14 02:53:01 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-05-14 02:54:17 -0700 |
commit | f96f037f8f77335dc444844abcc31a372a3e1849 (patch) | |
tree | 27e5f8adededa0817c2758dc965aa42dcb2db67a /src/tools/launcher | |
parent | 43181c9363473baed3de071a320e1da23ac45d26 (diff) |
Windows, Java launcher: Support jar files under different drives
Create junctions to jar's directory when java launcher and its jar are under different drives
Fixed https://github.com/bazelbuild/bazel/issues/5135
Change-Id: I21c5b28f5f36c1fe234f8b781fe40d526db846cc
PiperOrigin-RevId: 196477704
Diffstat (limited to 'src/tools/launcher')
-rw-r--r-- | src/tools/launcher/java_launcher.cc | 108 | ||||
-rw-r--r-- | src/tools/launcher/java_launcher.h | 7 | ||||
-rw-r--r-- | src/tools/launcher/util/launcher_util.cc | 12 | ||||
-rw-r--r-- | src/tools/launcher/util/launcher_util.h | 12 |
4 files changed, 125 insertions, 14 deletions
diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc index af60de2fed..5b5a498b71 100644 --- a/src/tools/launcher/java_launcher.cc +++ b/src/tools/launcher/java_launcher.cc @@ -12,11 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <memory> #include <sstream> #include <string> +#include <unordered_map> #include <vector> +#include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" +#include "src/main/cpp/util/strings.h" +#include "src/main/native/windows/file.h" #include "src/tools/launcher/java_launcher.h" #include "src/tools/launcher/util/launcher_util.h" @@ -29,6 +34,7 @@ using std::ostringstream; using std::string; using std::stringstream; using std::vector; +using std::wstring; // The runfile path of java binary, eg. local_jdk/bin/java.exe static constexpr const char* JAVA_BIN_PATH = "java_bin_path"; @@ -135,6 +141,52 @@ static string GetManifestJarDir(const string& binary_base_path) { return result; } +static void WriteJarClasspath(const string& jar_path, + ostringstream* manifest_classpath) { + *manifest_classpath << ' '; + if (jar_path.find_first_of(" \\") != string::npos) { + for (const auto& x : jar_path) { + if (x == ' ') { + *manifest_classpath << "%20"; + } + if (x == '\\') { + *manifest_classpath << "/"; + } else { + *manifest_classpath << x; + } + } + } else { + *manifest_classpath << jar_path; + } +} + +string JavaBinaryLauncher::GetJunctionBaseDir() { + string binary_base_path = + GetBinaryPathWithExtension(this->GetCommandlineArguments()[0]); + string result; + if (!NormalizePath(binary_base_path + ".j", &result)) { + die("Failed to get normalized junction base directory."); + } + return result; +} + +void JavaBinaryLauncher::DeleteJunctionBaseDir() { + string 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); + for (const auto& junction : junctions) { + if (!DeleteDirectoryByPath(junction.c_str())) { + PrintError(GetLastErrorString().c_str()); + } + } + if (!DeleteDirectoryByPath(junction_base_dir_norm.c_str())) { + PrintError(GetLastErrorString().c_str()); + } +} + string JavaBinaryLauncher::CreateClasspathJar(const string& classpath) { string binary_base_path = GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]); @@ -144,28 +196,55 @@ string JavaBinaryLauncher::CreateClasspathJar(const string& classpath) { manifest_classpath << "Class-Path:"; stringstream classpath_ss(classpath); string 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(); + int junction_count = 0; + // Make sure the junction base directory doesn't exist already. + DeleteJunctionBaseDir(); + blaze_util::MakeDirectories(junction_base_dir_norm, 0755); + while (getline(classpath_ss, path, ';')) { - manifest_classpath << ' '; if (blaze_util::IsAbsolute(path)) { - if (!NormalizePath(path, &path_norm) || - !RelativeTo(path_norm, abs_manifest_jar_dir_norm, &path)) { + if (!NormalizePath(path, &path_norm)) { die("CreateClasspathJar failed"); } - } - if (path.find_first_of(" \\") != string::npos) { - for (const auto& x : path) { - if (x == ' ') { - manifest_classpath << "%20"; - } - if (x == '\\') { - manifest_classpath << "/"; + + // 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; + auto search = jar_dirs.find(jar_dir); + if (search == jar_dirs.end()) { + junction = + junction_base_dir_norm + "\\" + std::to_string(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()); + } + + jar_dirs.insert(std::make_pair(jar_dir, junction)); } else { - manifest_classpath << x; + junction = search->second; } + path_norm = junction + "\\" + jar_base_name; + } + + if (!RelativeTo(path_norm, abs_manifest_jar_dir_norm, &path)) { + die("CreateClasspathJar failed"); } - } else { - manifest_classpath << path; } + WriteJarClasspath(path, &manifest_classpath); } string rand_id = "-" + GetRandomStr(10); @@ -335,6 +414,7 @@ ExitCode JavaBinaryLauncher::Launch() { // Delete classpath jar file after execution. if (!classpath_jar.empty()) { DeleteFileByPath(classpath_jar.c_str()); + DeleteJunctionBaseDir(); } return exit_code; diff --git a/src/tools/launcher/java_launcher.h b/src/tools/launcher/java_launcher.h index 896aacd74a..93366b3a8a 100644 --- a/src/tools/launcher/java_launcher.h +++ b/src/tools/launcher/java_launcher.h @@ -90,6 +90,13 @@ class JavaBinaryLauncher : public BinaryLauncherBase { // // Return the path of the classpath jar created. std::string CreateClasspathJar(const std::string& classpath); + + // Creat a directory based on the binary path, all the junctions will be + // generated under this directory. + std::string GetJunctionBaseDir(); + + // Delete all the junction directory and all the junctions under it. + void DeleteJunctionBaseDir(); }; } // namespace launcher diff --git a/src/tools/launcher/util/launcher_util.cc b/src/tools/launcher/util/launcher_util.cc index 573a99669a..2ef25a352f 100644 --- a/src/tools/launcher/util/launcher_util.cc +++ b/src/tools/launcher/util/launcher_util.cc @@ -100,6 +100,10 @@ bool DeleteFileByPath(const char* path) { return DeleteFileW(AsAbsoluteWindowsPath(path).c_str()); } +bool DeleteDirectoryByPath(const char* path) { + return RemoveDirectoryW(AsAbsoluteWindowsPath(path).c_str()); +} + string GetBinaryPathWithoutExtension(const string& binary) { if (binary.find(".exe", binary.size() - 4) != string::npos) { return binary.substr(0, binary.length() - 4); @@ -184,6 +188,14 @@ bool NormalizePath(const string& path, string* result) { return true; } +string GetBaseNameFromPath(const string& path) { + return path.substr(path.find_last_of("\\/") + 1); +} + +string GetParentDirFromPath(const string& path) { + return path.substr(0, path.find_last_of("\\/")); +} + bool RelativeTo(const string& path, const string& base, string* result) { if (blaze_util::IsAbsolute(path) != blaze_util::IsAbsolute(base)) { PrintError( diff --git a/src/tools/launcher/util/launcher_util.h b/src/tools/launcher/util/launcher_util.h index ae254bd09a..760eb7266e 100644 --- a/src/tools/launcher/util/launcher_util.h +++ b/src/tools/launcher/util/launcher_util.h @@ -61,6 +61,12 @@ bool DoesDirectoryPathExist(const char* path); // Delete a file at a given path. bool DeleteFileByPath(const char* 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); + // Get the value of a specific environment variable // // Return true if succeeded and the result is stored in buffer. @@ -79,6 +85,12 @@ std::string GetRandomStr(size_t len); // Normalize a path to a Windows path in lower case bool NormalizePath(const std::string& path, std::string* result); +// Get the base name from a normalized absoulute path +std::string GetBaseNameFromPath(const std::string& path); + +// Get parent directory from a normalized absoulute path +std::string GetParentDirFromPath(const std::string& 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. |