12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
+ #include < memory>
15
16
#include < sstream>
16
17
#include < string>
18
+ #include < unordered_map>
17
19
#include < vector>
18
20
21
+ #include " src/main/cpp/util/file.h"
19
22
#include " src/main/cpp/util/file_platform.h"
23
+ #include " src/main/cpp/util/strings.h"
24
+ #include " src/main/native/windows/file.h"
20
25
#include " src/tools/launcher/java_launcher.h"
21
26
#include " src/tools/launcher/util/launcher_util.h"
22
27
@@ -29,6 +34,7 @@ using std::ostringstream;
29
34
using std::string;
30
35
using std::stringstream;
31
36
using std::vector;
37
+ using std::wstring;
32
38
33
39
// The runfile path of java binary, eg. local_jdk/bin/java.exe
34
40
static constexpr const char * JAVA_BIN_PATH = " java_bin_path" ;
@@ -135,6 +141,52 @@ static string GetManifestJarDir(const string& binary_base_path) {
135
141
return result;
136
142
}
137
143
144
+ static void WriteJarClasspath (const string& jar_path,
145
+ ostringstream* manifest_classpath) {
146
+ *manifest_classpath << ' ' ;
147
+ if (jar_path.find_first_of (" \\ " ) != string::npos) {
148
+ for (const auto & x : jar_path) {
149
+ if (x == ' ' ) {
150
+ *manifest_classpath << " %20" ;
151
+ }
152
+ if (x == ' \\ ' ) {
153
+ *manifest_classpath << " /" ;
154
+ } else {
155
+ *manifest_classpath << x;
156
+ }
157
+ }
158
+ } else {
159
+ *manifest_classpath << jar_path;
160
+ }
161
+ }
162
+
163
+ string JavaBinaryLauncher::GetJunctionBaseDir () {
164
+ string binary_base_path =
165
+ GetBinaryPathWithExtension (this ->GetCommandlineArguments ()[0 ]);
166
+ string result;
167
+ if (!NormalizePath (binary_base_path + " .j" , &result)) {
168
+ die (" Failed to get normalized junction base directory." );
169
+ }
170
+ return result;
171
+ }
172
+
173
+ void JavaBinaryLauncher::DeleteJunctionBaseDir () {
174
+ string junction_base_dir_norm = GetJunctionBaseDir ();
175
+ if (!DoesDirectoryPathExist (junction_base_dir_norm.c_str ())) {
176
+ return ;
177
+ }
178
+ vector<string> junctions;
179
+ blaze_util::GetAllFilesUnder (junction_base_dir_norm, &junctions);
180
+ for (const auto & junction : junctions) {
181
+ if (!DeleteDirectoryByPath (junction.c_str ())) {
182
+ PrintError (GetLastErrorString ().c_str ());
183
+ }
184
+ }
185
+ if (!DeleteDirectoryByPath (junction_base_dir_norm.c_str ())) {
186
+ PrintError (GetLastErrorString ().c_str ());
187
+ }
188
+ }
189
+
138
190
string JavaBinaryLauncher::CreateClasspathJar (const string& classpath) {
139
191
string binary_base_path =
140
192
GetBinaryPathWithoutExtension (this ->GetCommandlineArguments ()[0 ]);
@@ -144,28 +196,55 @@ string JavaBinaryLauncher::CreateClasspathJar(const string& classpath) {
144
196
manifest_classpath << " Class-Path:" ;
145
197
stringstream classpath_ss (classpath);
146
198
string path, path_norm;
199
+
200
+ // A set to store all junctions created.
201
+ // The key is the target path, the value is the junction path.
202
+ std::unordered_map<string, string> jar_dirs;
203
+ string junction_base_dir_norm = GetJunctionBaseDir ();
204
+ int junction_count = 0 ;
205
+ // Make sure the junction base directory doesn't exist already.
206
+ DeleteJunctionBaseDir ();
207
+ blaze_util::MakeDirectories (junction_base_dir_norm, 0755 );
208
+
147
209
while (getline (classpath_ss, path, ' ;' )) {
148
- manifest_classpath << ' ' ;
149
210
if (blaze_util::IsAbsolute (path)) {
150
- if (!NormalizePath (path, &path_norm) ||
151
- !RelativeTo (path_norm, abs_manifest_jar_dir_norm, &path)) {
211
+ if (!NormalizePath (path, &path_norm)) {
152
212
die (" CreateClasspathJar failed" );
153
213
}
154
- }
155
- if (path.find_first_of (" \\ " ) != string::npos) {
156
- for (const auto & x : path) {
157
- if (x == ' ' ) {
158
- manifest_classpath << " %20" ;
159
- }
160
- if (x == ' \\ ' ) {
161
- manifest_classpath << " /" ;
214
+
215
+ // If two paths are under different drives, we should create a junction to
216
+ // the jar's directory
217
+ if (path_norm[0 ] != abs_manifest_jar_dir_norm[0 ]) {
218
+ string jar_dir = GetParentDirFromPath (path_norm);
219
+ string jar_base_name = GetBaseNameFromPath (path_norm);
220
+ string junction;
221
+ auto search = jar_dirs.find (jar_dir);
222
+ if (search == jar_dirs.end ()) {
223
+ junction =
224
+ junction_base_dir_norm + " \\ " + std::to_string (junction_count++);
225
+
226
+ wstring wjar_dir (
227
+ blaze_util::CstringToWstring (junction.c_str ()).get ());
228
+ wstring wjunction (
229
+ blaze_util::CstringToWstring (jar_dir.c_str ()).get ());
230
+ wstring werror (bazel::windows::CreateJunction (wjar_dir, wjunction));
231
+ if (!werror.empty ()) {
232
+ string error (werror.begin (), werror.end ());
233
+ die (" CreateClasspathJar failed: %s" , error.c_str ());
234
+ }
235
+
236
+ jar_dirs.insert (std::make_pair (jar_dir, junction));
162
237
} else {
163
- manifest_classpath << x ;
238
+ junction = search-> second ;
164
239
}
240
+ path_norm = junction + " \\ " + jar_base_name;
241
+ }
242
+
243
+ if (!RelativeTo (path_norm, abs_manifest_jar_dir_norm, &path)) {
244
+ die (" CreateClasspathJar failed" );
165
245
}
166
- } else {
167
- manifest_classpath << path;
168
246
}
247
+ WriteJarClasspath (path, &manifest_classpath);
169
248
}
170
249
171
250
string rand_id = " -" + GetRandomStr (10 );
@@ -335,6 +414,7 @@ ExitCode JavaBinaryLauncher::Launch() {
335
414
// Delete classpath jar file after execution.
336
415
if (!classpath_jar.empty ()) {
337
416
DeleteFileByPath (classpath_jar.c_str ());
417
+ DeleteJunctionBaseDir ();
338
418
}
339
419
340
420
return exit_code;
0 commit comments