1
+
.../rules/jvm/external/jar/MergeJars.java | 87 ++++++++++++-------
2
+
tests/com/jvm/external/jar/MergeJarsTest.java | 33 ++++++-
3
+
2 files changed, 87 insertions(+), 33 deletions(-)
4
+
5
+
diff --git a/private/tools/java/rules/jvm/external/jar/MergeJars.java b/private/tools/java/rules/jvm/external/jar/MergeJars.java
6
+
index bd278d0..9aaa069 100644
7
+
--- a/private/tools/java/rules/jvm/external/jar/MergeJars.java
8
+
+++ b/private/tools/java/rules/jvm/external/jar/MergeJars.java
9
+
@@ -19,12 +19,7 @@ package rules.jvm.external.jar;
10
+
11
+
import rules.jvm.external.ByteStreams;
12
+
13
+
-import java.io.BufferedInputStream;
14
+
-import java.io.ByteArrayOutputStream;
15
+
-import java.io.File;
16
+
-import java.io.IOException;
17
+
-import java.io.InputStream;
18
+
-import java.io.OutputStream;
19
+
+import java.io.*;
20
+
import java.nio.file.Files;
21
+
import java.nio.file.Path;
22
+
import java.nio.file.Paths;
23
+
@@ -32,16 +27,7 @@ import java.nio.file.attribute.FileTime;
24
+
import java.security.MessageDigest;
25
+
import java.security.NoSuchAlgorithmException;
26
+
import java.time.Instant;
27
+
-import java.util.Arrays;
28
+
-import java.util.Comparator;
29
+
-import java.util.HashMap;
30
+
-import java.util.HashSet;
31
+
-import java.util.LinkedHashSet;
32
+
-import java.util.Map;
33
+
-import java.util.Objects;
34
+
-import java.util.Set;
35
+
-import java.util.TreeMap;
36
+
-import java.util.TreeSet;
37
+
+import java.util.*;
38
+
import java.util.jar.Attributes;
39
+
import java.util.jar.JarEntry;
40
+
import java.util.jar.JarOutputStream;
41
+
@@ -105,6 +91,7 @@ public class MergeJars {
42
+
// Just write an empty jar and leave
43
+
try (OutputStream fos = Files.newOutputStream(out);
44
+
JarOutputStream jos = new JarOutputStream(fos)) {
45
+
+ // This space left blank deliberately
46
+
}
47
+
return;
48
+
}
49
+
@@ -193,6 +180,7 @@ public class MergeJars {
50
+
entry = resetTime(entry);
51
+
jos.putNextEntry(entry);
52
+
jos.closeEntry();
53
+
+ createdDirectories.add(entry.getName());
54
+
55
+
entry = new JarEntry("META-INF/MANIFEST.MF");
56
+
entry = resetTime(entry);
57
+
@@ -209,6 +197,7 @@ public class MergeJars {
58
+
entry = resetTime(entry);
59
+
jos.putNextEntry(entry);
60
+
jos.closeEntry();
61
+
+ createdDirectories.add(entry.getName());
62
+
}
63
+
for (Map.Entry<String, Set<String>> kv : allServices.entrySet()) {
64
+
entry = new JarEntry("META-INF/services/" + kv.getKey());
65
+
@@ -227,27 +216,39 @@ public class MergeJars {
66
+
67
+
// We should never enter this loop without there being any sources
68
+
for (Map.Entry<String, Path> pathAndSource : fileToSourceJar.entrySet()) {
69
+
- // Get the original entry
70
+
- JarEntry je = new JarEntry(pathAndSource.getKey());
71
+
- je = resetTime(je);
72
+
- jos.putNextEntry(je);
73
+
+ String name = pathAndSource.getKey();
74
+
75
+
- if (je.isDirectory()) {
76
+
- jos.closeEntry();
77
+
+ // Make sure we're not trying to create root entries.
78
+
+ if (name.startsWith("/")) {
79
+
+ if (name.length() == 1) {
80
+
+ continue;
81
+
+ }
82
+
+ name = name.substring(1);
83
+
+ }
84
+
+
85
+
+ createDirectories(jos, name, createdDirectories);
86
+
+
87
+
+ if (createdDirectories.contains(name)) {
88
+
continue;
89
+
}
90
+
91
+
if (!Objects.equals(previousSource, pathAndSource.getValue())) {
92
+
- source.close();
93
+
+ if (source != null) {
94
+
+ source.close();
95
+
+ }
96
+
source = new ZipFile(pathAndSource.getValue().toFile());
97
+
previousSource = pathAndSource.getValue();
98
+
}
99
+
100
+
- ZipEntry original = source.getEntry(pathAndSource.getKey());
101
+
+ ZipEntry original = source.getEntry(name);
102
+
if (original == null) {
103
+
continue;
104
+
}
105
+
106
+
+ JarEntry je = new JarEntry(name);
107
+
+ je = resetTime(je);
108
+
+ jos.putNextEntry(je);
109
+
+
110
+
try (InputStream is = source.getInputStream(original)) {
111
+
ByteStreams.copy(is, jos);
112
+
}
113
+
@@ -259,6 +260,37 @@ public class MergeJars {
114
+
}
115
+
}
116
+
117
+
+ private static void createDirectories(JarOutputStream jos, String name, Set<String> createdDirs) throws IOException {
118
+
+ if (!name.endsWith("/")) {
119
+
+ int slashIndex = name.lastIndexOf('/');
120
+
+ if (slashIndex != -1) {
121
+
+ createDirectories(jos, name.substring(0, slashIndex + 1), createdDirs);
122
+
+ }
123
+
+ return;
124
+
+ }
125
+
+
126
+
+ if (createdDirs.contains(name)) {
127
+
+ return;
128
+
+ }
129
+
+
130
+
+ String[] segments = name.split("/");
131
+
+ StringBuilder path = new StringBuilder();
132
+
+ for (String segment : segments) {
133
+
+ path.append(segment).append('/');
134
+
+
135
+
+ String newPath = path.toString();
136
+
+ if (createdDirs.contains(newPath)) {
137
+
+ continue;
138
+
+ }
139
+
+
140
+
+ JarEntry entry = new JarEntry(newPath);
141
+
+ entry = resetTime(entry);
142
+
+ jos.putNextEntry(entry);
143
+
+ jos.closeEntry();
144
+
+ createdDirs.add(newPath);
145
+
+ }
146
+
+ }
147
+
+
148
+
private static Set<String> readExcludedFileNames(Set<Path> excludes) throws IOException {
149
+
Set<String> paths = new HashSet<>();
150
+
151
+
@@ -292,13 +324,6 @@ public class MergeJars {
152
+
return path;
153
+
}
154
+
155
+
- private static void delete(Path toDelete) throws IOException {
156
+
- Files.walk(toDelete)
157
+
- .sorted(Comparator.reverseOrder())
158
+
- .map(Path::toFile)
159
+
- .forEach(File::delete);
160
+
- }
161
+
-
162
+
// Returns the normalized timestamp for a jar entry based on its name. This is necessary since
163
+
// javac will, when loading a class X, prefer a source file to a class file, if both files have
164
+
// the same timestamp. Therefore, we need to adjust the timestamp for class files to slightly
165
+
diff --git a/tests/com/jvm/external/jar/MergeJarsTest.java b/tests/com/jvm/external/jar/MergeJarsTest.java
166
+
index b247e15..0869233 100644
167
+
--- a/tests/com/jvm/external/jar/MergeJarsTest.java
168
+
+++ b/tests/com/jvm/external/jar/MergeJarsTest.java
169
+
@@ -20,12 +20,12 @@ import java.util.Set;
170
+
import java.util.jar.Attributes;
171
+
import java.util.jar.Manifest;
172
+
import java.util.zip.ZipEntry;
173
+
+import java.util.zip.ZipFile;
174
+
import java.util.zip.ZipInputStream;
175
+
import java.util.zip.ZipOutputStream;
176
+
177
+
import static java.nio.charset.StandardCharsets.UTF_8;
178
+
-import static org.junit.Assert.assertEquals;
179
+
-import static org.junit.Assert.assertTrue;
180
+
+import static org.junit.Assert.*;
181
+
182
+
public class MergeJarsTest {
183
+
184
+
@@ -351,6 +351,35 @@ public class MergeJarsTest {
185
+
}
186
+
}
187
+
188
+
+ @Test
189
+
+ public void shouldAddMissingDirectories() throws IOException {
190
+
+ Path input = temp.newFile("example.jar").toPath();
191
+
+ createJar(input, ImmutableMap.of("foo/bar/baz.txt", "Hello, World!"));
192
+
+
193
+
+ Path output = temp.newFile("out.jar").toPath();
194
+
+
195
+
+ MergeJars.main(new String[] {
196
+
+ "--output", output.toAbsolutePath().toString(),
197
+
+ "--sources", input.toAbsolutePath().toString(),
198
+
+ });
199
+
+
200
+
+ Set<String> dirNames = new HashSet<>();
201
+
+ try (InputStream is = Files.newInputStream(output);
202
+
+ ZipInputStream zis = new ZipInputStream(is)) {
203
+
+ ZipEntry entry = zis.getNextEntry();
204
+
+ while (entry != null) {
205
+
+ if (entry.isDirectory()) {
206
+
+ dirNames.add(entry.getName());
207
+
+ }
208
+
+ entry = zis.getNextEntry();
209
+
+ }
210
+
+
211
+
+ assertTrue(dirNames.toString(), dirNames.contains("foo/"));
212
+
+ assertTrue(dirNames.toString(), dirNames.contains("foo/bar/"));
213
+
+ }
214
+
+
215
+
+ }
216
+
+
217
+
private void createJar(Path outputTo, Map<String, String> pathToContents) throws IOException {
218
+
try (OutputStream os = Files.newOutputStream(outputTo);
219
+
ZipOutputStream zos = new ZipOutputStream(os)) {
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4