aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Yun Peng <pcloudy@google.com>2018-05-14 02:53:01 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-05-14 02:54:17 -0700
commitf96f037f8f77335dc444844abcc31a372a3e1849 (patch)
tree27e5f8adededa0817c2758dc965aa42dcb2db67a /src
parent43181c9363473baed3de071a320e1da23ac45d26 (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')
-rw-r--r--src/main/cpp/util/file.cc4
-rw-r--r--src/main/cpp/util/file_windows.cc4
-rwxr-xr-xsrc/test/shell/bazel/bazel_windows_example_test.sh38
-rw-r--r--src/tools/launcher/java_launcher.cc108
-rw-r--r--src/tools/launcher/java_launcher.h7
-rw-r--r--src/tools/launcher/util/launcher_util.cc12
-rw-r--r--src/tools/launcher/util/launcher_util.h12
7 files changed, 167 insertions, 18 deletions
diff --git a/src/main/cpp/util/file.cc b/src/main/cpp/util/file.cc
index 16e8a08a52..3eb614c0dc 100644
--- a/src/main/cpp/util/file.cc
+++ b/src/main/cpp/util/file.cc
@@ -124,8 +124,8 @@ class DirectoryTreeWalker : public DirectoryEntryConsumer {
_ForEachDirectoryEntry walk_entries)
: _files(files), _walk_entries(walk_entries) {}
- void Consume(const string &path, bool is_directory) override {
- if (is_directory) {
+ void Consume(const string &path, bool follow_directory) override {
+ if (follow_directory) {
Walk(path);
} else {
_files->push_back(path);
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 124ae7ed17..39160c9c22 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -1219,7 +1219,9 @@ void ForEachDirectoryEntry(const string &path,
wstring wname = wpath + metadata.cFileName;
string name(WstringToCstring(/* omit prefix */ 4 + wname.c_str()).get());
bool is_dir = (metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- consume->Consume(name, is_dir);
+ bool is_junc =
+ (metadata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ consume->Consume(name, is_dir && !is_junc);
}
} while (::FindNextFileW(handle, &metadata));
::FindClose(handle);
diff --git a/src/test/shell/bazel/bazel_windows_example_test.sh b/src/test/shell/bazel/bazel_windows_example_test.sh
index 97de66108d..b36d30148e 100755
--- a/src/test/shell/bazel/bazel_windows_example_test.sh
+++ b/src/test/shell/bazel/bazel_windows_example_test.sh
@@ -39,7 +39,6 @@ function set_up() {
startup --host_jvm_args=-Dbazel.windows_unix_root=C:/fake/msys
startup --batch
-build --cpu=x64_windows_msvc
EOF
export MSYS_NO_PATHCONV=1
export MSYS2_ARG_CONV_EXCL="*"
@@ -129,6 +128,43 @@ function test_java() {
assert_binary_run_from_subdir "bazel-bin/${java_pkg}/hello-world foo" "Hello foo"
}
+function create_tmp_drive() {
+ mkdir "$TEST_TMPDIR/tmp_drive"
+
+ TMP_DRIVE_PATH=$(cygpath -w "$TEST_TMPDIR\\tmp_drive")
+ for X in {A..Z}
+ do
+ TMP_DRIVE=${X}
+ subst ${TMP_DRIVE}: ${TMP_DRIVE_PATH} >NUL || TMP_DRIVE=""
+ if [ -n "${TMP_DRIVE}" ]; then
+ break
+ fi
+ done
+
+ if [ -z "${TMP_DRIVE}" ]; then
+ fail "Cannot create temporary drive."
+ fi
+
+ export TMP_DRIVE
+}
+
+function delete_tmp_drive() {
+ if [ -n "${TMP_DRIVE}" ]; then
+ subst ${TMP_DRIVE}: /D
+ fi
+}
+
+function test_java_with_jar_under_different_drive() {
+ create_tmp_drive
+
+ trap delete_tmp_drive EXIT
+
+ local java_pkg=examples/java-native/src/main/java/com/example/myproject
+ bazel --output_user_root=${TMP_DRIVE}:/tmp build ${java_pkg}:hello-world
+
+ assert_binary_run_from_subdir "bazel-bin/${java_pkg}/hello-world --classpath_limit=0" "Hello world"
+}
+
function test_java_test() {
setup_javatest_support
local java_native_tests=//examples/java-native/src/test/java/com/example/myproject
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.