Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/langtools/tools/lib/toolbox/JarTask.java
41149 views
1
/*
2
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
package toolbox;
25
26
import java.io.BufferedInputStream;
27
import java.io.ByteArrayInputStream;
28
import java.io.File;
29
import java.io.IOError;
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.OutputStream;
33
import java.net.URI;
34
import java.nio.file.FileVisitResult;
35
import java.nio.file.Files;
36
import java.nio.file.Path;
37
import java.nio.file.Paths;
38
import java.nio.file.SimpleFileVisitor;
39
import java.nio.file.attribute.BasicFileAttributes;
40
import java.util.ArrayList;
41
import java.util.Arrays;
42
import java.util.Collections;
43
import java.util.EnumSet;
44
import java.util.HashMap;
45
import java.util.LinkedHashSet;
46
import java.util.List;
47
import java.util.ListIterator;
48
import java.util.Map;
49
import java.util.Set;
50
import java.util.jar.Attributes;
51
import java.util.jar.JarEntry;
52
import java.util.jar.JarOutputStream;
53
import java.util.jar.Manifest;
54
import java.util.regex.Matcher;
55
import java.util.regex.Pattern;
56
import java.util.stream.Collectors;
57
import java.util.stream.Stream;
58
import javax.tools.FileObject;
59
import javax.tools.JavaFileManager;
60
import javax.tools.JavaFileObject;
61
import static toolbox.ToolBox.currDir;
62
63
/**
64
* A task to configure and run the jar file utility.
65
*/
66
public class JarTask extends AbstractTask<JarTask> {
67
private Path jar;
68
private Manifest manifest;
69
private String classpath;
70
private String mainClass;
71
private Path baseDir;
72
private List<Path> paths;
73
private Set<FileObject> fileObjects;
74
75
/**
76
* Creates a task to write jar files, using API mode.
77
* @param toolBox the {@code ToolBox} to use
78
*/
79
public JarTask(ToolBox toolBox) {
80
super(toolBox, Task.Mode.API);
81
paths = Collections.emptyList();
82
fileObjects = new LinkedHashSet<>();
83
}
84
85
/**
86
* Creates a JarTask for use with a given jar file.
87
* @param toolBox the {@code ToolBox} to use
88
* @param path the file
89
*/
90
public JarTask(ToolBox toolBox, String path) {
91
this(toolBox);
92
jar = Paths.get(path);
93
}
94
95
/**
96
* Creates a JarTask for use with a given jar file.
97
* @param toolBox the {@code ToolBox} to use
98
* @param path the file
99
*/
100
public JarTask(ToolBox toolBox, Path path) {
101
this(toolBox);
102
jar = path;
103
}
104
105
/**
106
* Sets a manifest for the jar file.
107
* @param manifest the manifest
108
* @return this task object
109
*/
110
public JarTask manifest(Manifest manifest) {
111
this.manifest = manifest;
112
return this;
113
}
114
115
/**
116
* Sets a manifest for the jar file.
117
* @param manifest a string containing the contents of the manifest
118
* @return this task object
119
* @throws IOException if there is a problem creating the manifest
120
*/
121
public JarTask manifest(String manifest) throws IOException {
122
this.manifest = new Manifest(new ByteArrayInputStream(manifest.getBytes()));
123
return this;
124
}
125
126
/**
127
* Sets the classpath to be written to the {@code Class-Path}
128
* entry in the manifest.
129
* @param classpath the classpath
130
* @return this task object
131
*/
132
public JarTask classpath(String classpath) {
133
this.classpath = classpath;
134
return this;
135
}
136
137
/**
138
* Sets the class to be written to the {@code Main-Class}
139
* entry in the manifest..
140
* @param mainClass the name of the main class
141
* @return this task object
142
*/
143
public JarTask mainClass(String mainClass) {
144
this.mainClass = mainClass;
145
return this;
146
}
147
148
/**
149
* Sets the base directory for files to be written into the jar file.
150
* @param baseDir the base directory
151
* @return this task object
152
*/
153
public JarTask baseDir(String baseDir) {
154
this.baseDir = Paths.get(baseDir);
155
return this;
156
}
157
158
/**
159
* Sets the base directory for files to be written into the jar file.
160
* @param baseDir the base directory
161
* @return this task object
162
*/
163
public JarTask baseDir(Path baseDir) {
164
this.baseDir = baseDir;
165
return this;
166
}
167
168
/**
169
* Sets the files to be written into the jar file.
170
* @param files the files
171
* @return this task object
172
*/
173
public JarTask files(String... files) {
174
this.paths = Stream.of(files)
175
.map(file -> Paths.get(file))
176
.collect(Collectors.toList());
177
return this;
178
}
179
180
/**
181
* Adds a set of file objects to be written into the jar file, by copying them
182
* from a Location in a JavaFileManager.
183
* The file objects to be written are specified by a series of paths;
184
* each path can be in one of the following forms:
185
* <ul>
186
* <li>The name of a class. For example, java.lang.Object.
187
* In this case, the corresponding .class file will be written to the jar file.
188
* <li>the name of a package followed by {@code .*}. For example, {@code java.lang.*}.
189
* In this case, all the class files in the specified package will be written to
190
* the jar file.
191
* <li>the name of a package followed by {@code .**}. For example, {@code java.lang.**}.
192
* In this case, all the class files in the specified package, and any subpackages
193
* will be written to the jar file.
194
* </ul>
195
*
196
* @param fm the file manager in which to find the file objects
197
* @param l the location in which to find the file objects
198
* @param paths the paths specifying the file objects to be copied
199
* @return this task object
200
* @throws IOException if errors occur while determining the set of file objects
201
*/
202
public JarTask files(JavaFileManager fm, JavaFileManager.Location l, String... paths)
203
throws IOException {
204
for (String p : paths) {
205
if (p.endsWith(".**"))
206
addPackage(fm, l, p.substring(0, p.length() - 3), true);
207
else if (p.endsWith(".*"))
208
addPackage(fm, l, p.substring(0, p.length() - 2), false);
209
else
210
addFile(fm, l, p);
211
}
212
return this;
213
}
214
215
private void addPackage(JavaFileManager fm, JavaFileManager.Location l, String pkg, boolean recurse)
216
throws IOException {
217
for (JavaFileObject fo : fm.list(l, pkg, EnumSet.allOf(JavaFileObject.Kind.class), recurse)) {
218
fileObjects.add(fo);
219
}
220
}
221
222
private void addFile(JavaFileManager fm, JavaFileManager.Location l, String path) throws IOException {
223
JavaFileObject fo = fm.getJavaFileForInput(l, path, JavaFileObject.Kind.CLASS);
224
fileObjects.add(fo);
225
}
226
227
/**
228
* Provides limited jar command-like functionality.
229
* The supported commands are:
230
* <ul>
231
* <li> jar cf jarfile -C dir files...
232
* <li> jar cfm jarfile manifestfile -C dir files...
233
* </ul>
234
* Any values specified by other configuration methods will be ignored.
235
* @param args arguments in the style of those for the jar command
236
* @return a Result object containing the results of running the task
237
*/
238
public Task.Result run(String... args) {
239
if (args.length < 2)
240
throw new IllegalArgumentException();
241
242
ListIterator<String> iter = Arrays.asList(args).listIterator();
243
String first = iter.next();
244
switch (first) {
245
case "cf":
246
jar = Paths.get(iter.next());
247
break;
248
case "cfm":
249
jar = Paths.get(iter.next());
250
try (InputStream in = Files.newInputStream(Paths.get(iter.next()))) {
251
manifest = new Manifest(in);
252
} catch (IOException e) {
253
throw new IOError(e);
254
}
255
break;
256
}
257
258
if (iter.hasNext()) {
259
if (iter.next().equals("-C"))
260
baseDir = Paths.get(iter.next());
261
else
262
iter.previous();
263
}
264
265
paths = new ArrayList<>();
266
while (iter.hasNext())
267
paths.add(Paths.get(iter.next()));
268
269
return run();
270
}
271
272
/**
273
* {@inheritDoc}
274
* @return the name "jar"
275
*/
276
@Override
277
public String name() {
278
return "jar";
279
}
280
281
/**
282
* Creates a jar file with the arguments as currently configured.
283
* @return a Result object indicating the outcome of the compilation
284
* and the content of any output written to stdout, stderr, or the
285
* main stream by the compiler.
286
* @throws TaskError if the outcome of the task is not as expected.
287
*/
288
@Override
289
public Task.Result run() {
290
Manifest m = (manifest == null) ? new Manifest() : manifest;
291
Attributes mainAttrs = m.getMainAttributes();
292
if (mainClass != null)
293
mainAttrs.put(Attributes.Name.MAIN_CLASS, mainClass);
294
if (classpath != null)
295
mainAttrs.put(Attributes.Name.CLASS_PATH, classpath);
296
297
AbstractTask.StreamOutput sysOut = new AbstractTask.StreamOutput(System.out, System::setOut);
298
AbstractTask.StreamOutput sysErr = new AbstractTask.StreamOutput(System.err, System::setErr);
299
300
Map<Task.OutputKind, String> outputMap = new HashMap<>();
301
302
try (OutputStream os = Files.newOutputStream(jar);
303
JarOutputStream jos = openJar(os, m)) {
304
writeFiles(jos);
305
writeFileObjects(jos);
306
} catch (IOException e) {
307
error("Exception while opening " + jar, e);
308
} finally {
309
outputMap.put(Task.OutputKind.STDOUT, sysOut.close());
310
outputMap.put(Task.OutputKind.STDERR, sysErr.close());
311
}
312
return checkExit(new Task.Result(toolBox, this, (errors == 0) ? 0 : 1, outputMap));
313
}
314
315
private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException {
316
if (m == null || m.getMainAttributes().isEmpty() && m.getEntries().isEmpty()) {
317
return new JarOutputStream(os);
318
} else {
319
if (m.getMainAttributes().get(Attributes.Name.MANIFEST_VERSION) == null)
320
m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
321
return new JarOutputStream(os, m);
322
}
323
}
324
325
private void writeFiles(JarOutputStream jos) throws IOException {
326
Path base = (baseDir == null) ? currDir : baseDir;
327
for (Path path : paths) {
328
Files.walkFileTree(base.resolve(path), new SimpleFileVisitor<Path>() {
329
@Override
330
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
331
try {
332
String p = base.relativize(file)
333
.normalize()
334
.toString()
335
.replace(File.separatorChar, '/');
336
JarEntry e = new JarEntry(p);
337
jos.putNextEntry(e);
338
try {
339
jos.write(Files.readAllBytes(file));
340
} finally {
341
jos.closeEntry();
342
}
343
return FileVisitResult.CONTINUE;
344
} catch (IOException e) {
345
error("Exception while adding " + file + " to jar file", e);
346
return FileVisitResult.TERMINATE;
347
}
348
}
349
});
350
}
351
}
352
353
private void writeFileObjects(JarOutputStream jos) throws IOException {
354
for (FileObject fo : fileObjects) {
355
String p = guessPath(fo);
356
JarEntry e = new JarEntry(p);
357
jos.putNextEntry(e);
358
try {
359
byte[] buf = new byte[1024];
360
try (BufferedInputStream in = new BufferedInputStream(fo.openInputStream())) {
361
int n;
362
while ((n = in.read(buf)) > 0)
363
jos.write(buf, 0, n);
364
} catch (IOException ex) {
365
error("Exception while adding " + fo.getName() + " to jar file", ex);
366
}
367
} finally {
368
jos.closeEntry();
369
}
370
}
371
}
372
373
/*
374
* A jar: URL is of the form jar:URL!/<entry> where URL is a URL for the .jar file itself.
375
* In Symbol files (i.e. ct.sym) the underlying entry is prefixed META-INF/sym/<base>.
376
*/
377
private final Pattern jarEntry = Pattern.compile(".*!/(?:META-INF/sym/[^/]+/)?(.*)");
378
379
/*
380
* A jrt: URL is of the form jrt:/<module>/<package>/<file>
381
*/
382
private final Pattern jrtEntry = Pattern.compile("/([^/]+)/(.*)");
383
384
/*
385
* A file: URL is of the form file:/path/to/{modules,patches}/<module>/<package>/<file>
386
*/
387
private final Pattern fileEntry = Pattern.compile(".*/(?:modules|patches)/([^/]+)/(.*)");
388
389
private String guessPath(FileObject fo) {
390
URI u = fo.toUri();
391
switch (u.getScheme()) {
392
case "jar": {
393
Matcher m = jarEntry.matcher(u.getSchemeSpecificPart());
394
if (m.matches()) {
395
return m.group(1);
396
}
397
break;
398
}
399
case "jrt": {
400
Matcher m = jrtEntry.matcher(u.getSchemeSpecificPart());
401
if (m.matches()) {
402
return m.group(2);
403
}
404
break;
405
}
406
case "file": {
407
Matcher m = fileEntry.matcher(u.getSchemeSpecificPart());
408
if (m.matches()) {
409
return m.group(2);
410
}
411
break;
412
}
413
}
414
throw new IllegalArgumentException(fo.getName() + "--" + fo.toUri());
415
}
416
417
private void error(String message, Throwable t) {
418
toolBox.out.println("Error: " + message + ": " + t);
419
errors++;
420
}
421
422
private int errors;
423
}
424
425