mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 02:54:35 +02:00
8152897: refactor ToolBox to allow reduced documented dependencies
Reviewed-by: vromero
This commit is contained in:
parent
208d93e110
commit
7812306bc0
212 changed files with 3955 additions and 3485 deletions
423
langtools/test/tools/lib/toolbox/JarTask.java
Normal file
423
langtools/test/tools/lib/toolbox/JarTask.java
Normal file
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package toolbox;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import static toolbox.ToolBox.currDir;
|
||||
|
||||
/**
|
||||
* A task to configure and run the jar file utility.
|
||||
*/
|
||||
public class JarTask extends AbstractTask<JarTask> {
|
||||
private Path jar;
|
||||
private Manifest manifest;
|
||||
private String classpath;
|
||||
private String mainClass;
|
||||
private Path baseDir;
|
||||
private List<Path> paths;
|
||||
private Set<FileObject> fileObjects;
|
||||
|
||||
/**
|
||||
* Creates a task to write jar files, using API mode.
|
||||
* @param toolBox the {@code ToolBox} to use
|
||||
*/
|
||||
public JarTask(ToolBox toolBox) {
|
||||
super(toolBox, Task.Mode.API);
|
||||
paths = Collections.emptyList();
|
||||
fileObjects = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JarTask for use with a given jar file.
|
||||
* @param toolBox the {@code ToolBox} to use
|
||||
* @param path the file
|
||||
*/
|
||||
public JarTask(ToolBox toolBox, String path) {
|
||||
this(toolBox);
|
||||
jar = Paths.get(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JarTask for use with a given jar file.
|
||||
* @param toolBox the {@code ToolBox} to use
|
||||
* @param path the file
|
||||
*/
|
||||
public JarTask(ToolBox toolBox, Path path) {
|
||||
this(toolBox);
|
||||
jar = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a manifest for the jar file.
|
||||
* @param manifest the manifest
|
||||
* @return this task object
|
||||
*/
|
||||
public JarTask manifest(Manifest manifest) {
|
||||
this.manifest = manifest;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a manifest for the jar file.
|
||||
* @param manifest a string containing the contents of the manifest
|
||||
* @return this task object
|
||||
* @throws IOException if there is a problem creating the manifest
|
||||
*/
|
||||
public JarTask manifest(String manifest) throws IOException {
|
||||
this.manifest = new Manifest(new ByteArrayInputStream(manifest.getBytes()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the classpath to be written to the {@code Class-Path}
|
||||
* entry in the manifest.
|
||||
* @param classpath the classpath
|
||||
* @return this task object
|
||||
*/
|
||||
public JarTask classpath(String classpath) {
|
||||
this.classpath = classpath;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class to be written to the {@code Main-Class}
|
||||
* entry in the manifest..
|
||||
* @param mainClass the name of the main class
|
||||
* @return this task object
|
||||
*/
|
||||
public JarTask mainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base directory for files to be written into the jar file.
|
||||
* @param baseDir the base directory
|
||||
* @return this task object
|
||||
*/
|
||||
public JarTask baseDir(String baseDir) {
|
||||
this.baseDir = Paths.get(baseDir);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base directory for files to be written into the jar file.
|
||||
* @param baseDir the base directory
|
||||
* @return this task object
|
||||
*/
|
||||
public JarTask baseDir(Path baseDir) {
|
||||
this.baseDir = baseDir;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the files to be written into the jar file.
|
||||
* @param files the files
|
||||
* @return this task object
|
||||
*/
|
||||
public JarTask files(String... files) {
|
||||
this.paths = Stream.of(files)
|
||||
.map(file -> Paths.get(file))
|
||||
.collect(Collectors.toList());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of file objects to be written into the jar file, by copying them
|
||||
* from a Location in a JavaFileManager.
|
||||
* The file objects to be written are specified by a series of paths;
|
||||
* each path can be in one of the following forms:
|
||||
* <ul>
|
||||
* <li>The name of a class. For example, java.lang.Object.
|
||||
* In this case, the corresponding .class file will be written to the jar file.
|
||||
* <li>the name of a package followed by {@code .*}. For example, {@code java.lang.*}.
|
||||
* In this case, all the class files in the specified package will be written to
|
||||
* the jar file.
|
||||
* <li>the name of a package followed by {@code .**}. For example, {@code java.lang.**}.
|
||||
* In this case, all the class files in the specified package, and any subpackages
|
||||
* will be written to the jar file.
|
||||
* </ul>
|
||||
*
|
||||
* @param fm the file manager in which to find the file objects
|
||||
* @param l the location in which to find the file objects
|
||||
* @param paths the paths specifying the file objects to be copied
|
||||
* @return this task object
|
||||
* @throws IOException if errors occur while determining the set of file objects
|
||||
*/
|
||||
public JarTask files(JavaFileManager fm, JavaFileManager.Location l, String... paths)
|
||||
throws IOException {
|
||||
for (String p : paths) {
|
||||
if (p.endsWith(".**"))
|
||||
addPackage(fm, l, p.substring(0, p.length() - 3), true);
|
||||
else if (p.endsWith(".*"))
|
||||
addPackage(fm, l, p.substring(0, p.length() - 2), false);
|
||||
else
|
||||
addFile(fm, l, p);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addPackage(JavaFileManager fm, JavaFileManager.Location l, String pkg, boolean recurse)
|
||||
throws IOException {
|
||||
for (JavaFileObject fo : fm.list(l, pkg, EnumSet.allOf(JavaFileObject.Kind.class), recurse)) {
|
||||
fileObjects.add(fo);
|
||||
}
|
||||
}
|
||||
|
||||
private void addFile(JavaFileManager fm, JavaFileManager.Location l, String path) throws IOException {
|
||||
JavaFileObject fo = fm.getJavaFileForInput(l, path, JavaFileObject.Kind.CLASS);
|
||||
fileObjects.add(fo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides limited jar command-like functionality.
|
||||
* The supported commands are:
|
||||
* <ul>
|
||||
* <li> jar cf jarfile -C dir files...
|
||||
* <li> jar cfm jarfile manifestfile -C dir files...
|
||||
* </ul>
|
||||
* Any values specified by other configuration methods will be ignored.
|
||||
* @param args arguments in the style of those for the jar command
|
||||
* @return a Result object containing the results of running the task
|
||||
*/
|
||||
public Task.Result run(String... args) {
|
||||
if (args.length < 2)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
ListIterator<String> iter = Arrays.asList(args).listIterator();
|
||||
String first = iter.next();
|
||||
switch (first) {
|
||||
case "cf":
|
||||
jar = Paths.get(iter.next());
|
||||
break;
|
||||
case "cfm":
|
||||
jar = Paths.get(iter.next());
|
||||
try (InputStream in = Files.newInputStream(Paths.get(iter.next()))) {
|
||||
manifest = new Manifest(in);
|
||||
} catch (IOException e) {
|
||||
throw new IOError(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (iter.hasNext()) {
|
||||
if (iter.next().equals("-C"))
|
||||
baseDir = Paths.get(iter.next());
|
||||
else
|
||||
iter.previous();
|
||||
}
|
||||
|
||||
paths = new ArrayList<>();
|
||||
while (iter.hasNext())
|
||||
paths.add(Paths.get(iter.next()));
|
||||
|
||||
return run();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @return the name "jar"
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return "jar";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a jar file with the arguments as currently configured.
|
||||
* @return a Result object indicating the outcome of the compilation
|
||||
* and the content of any output written to stdout, stderr, or the
|
||||
* main stream by the compiler.
|
||||
* @throws TaskError if the outcome of the task is not as expected.
|
||||
*/
|
||||
@Override
|
||||
public Task.Result run() {
|
||||
Manifest m = (manifest == null) ? new Manifest() : manifest;
|
||||
Attributes mainAttrs = m.getMainAttributes();
|
||||
if (mainClass != null)
|
||||
mainAttrs.put(Attributes.Name.MAIN_CLASS, mainClass);
|
||||
if (classpath != null)
|
||||
mainAttrs.put(Attributes.Name.CLASS_PATH, classpath);
|
||||
|
||||
AbstractTask.StreamOutput sysOut = new AbstractTask.StreamOutput(System.out, System::setOut);
|
||||
AbstractTask.StreamOutput sysErr = new AbstractTask.StreamOutput(System.err, System::setErr);
|
||||
|
||||
Map<Task.OutputKind, String> outputMap = new HashMap<>();
|
||||
|
||||
try (OutputStream os = Files.newOutputStream(jar);
|
||||
JarOutputStream jos = openJar(os, m)) {
|
||||
writeFiles(jos);
|
||||
writeFileObjects(jos);
|
||||
} catch (IOException e) {
|
||||
error("Exception while opening " + jar, e);
|
||||
} finally {
|
||||
outputMap.put(Task.OutputKind.STDOUT, sysOut.close());
|
||||
outputMap.put(Task.OutputKind.STDERR, sysErr.close());
|
||||
}
|
||||
return checkExit(new Task.Result(toolBox, this, (errors == 0) ? 0 : 1, outputMap));
|
||||
}
|
||||
|
||||
private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException {
|
||||
if (m == null || m.getMainAttributes().isEmpty() && m.getEntries().isEmpty()) {
|
||||
return new JarOutputStream(os);
|
||||
} else {
|
||||
if (m.getMainAttributes().get(Attributes.Name.MANIFEST_VERSION) == null)
|
||||
m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
return new JarOutputStream(os, m);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFiles(JarOutputStream jos) throws IOException {
|
||||
Path base = (baseDir == null) ? currDir : baseDir;
|
||||
for (Path path : paths) {
|
||||
Files.walkFileTree(base.resolve(path), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
try {
|
||||
String p = base.relativize(file)
|
||||
.normalize()
|
||||
.toString()
|
||||
.replace(File.separatorChar, '/');
|
||||
JarEntry e = new JarEntry(p);
|
||||
jos.putNextEntry(e);
|
||||
try {
|
||||
jos.write(Files.readAllBytes(file));
|
||||
} finally {
|
||||
jos.closeEntry();
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
} catch (IOException e) {
|
||||
error("Exception while adding " + file + " to jar file", e);
|
||||
return FileVisitResult.TERMINATE;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileObjects(JarOutputStream jos) throws IOException {
|
||||
for (FileObject fo : fileObjects) {
|
||||
String p = guessPath(fo);
|
||||
JarEntry e = new JarEntry(p);
|
||||
jos.putNextEntry(e);
|
||||
try {
|
||||
byte[] buf = new byte[1024];
|
||||
try (BufferedInputStream in = new BufferedInputStream(fo.openInputStream())) {
|
||||
int n;
|
||||
while ((n = in.read(buf)) > 0)
|
||||
jos.write(buf, 0, n);
|
||||
} catch (IOException ex) {
|
||||
error("Exception while adding " + fo.getName() + " to jar file", ex);
|
||||
}
|
||||
} finally {
|
||||
jos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A jar: URL is of the form jar:URL!/<entry> where URL is a URL for the .jar file itself.
|
||||
* In Symbol files (i.e. ct.sym) the underlying entry is prefixed META-INF/sym/<base>.
|
||||
*/
|
||||
private final Pattern jarEntry = Pattern.compile(".*!/(?:META-INF/sym/[^/]+/)?(.*)");
|
||||
|
||||
/*
|
||||
* A jrt: URL is of the form jrt:/modules/<module>/<package>/<file>
|
||||
*/
|
||||
private final Pattern jrtEntry = Pattern.compile("/modules/([^/]+)/(.*)");
|
||||
|
||||
/*
|
||||
* A file: URL is of the form file:/path/to/{modules,patches}/<module>/<package>/<file>
|
||||
*/
|
||||
private final Pattern fileEntry = Pattern.compile(".*/(?:modules|patches)/([^/]+)/(.*)");
|
||||
|
||||
private String guessPath(FileObject fo) {
|
||||
URI u = fo.toUri();
|
||||
switch (u.getScheme()) {
|
||||
case "jar": {
|
||||
Matcher m = jarEntry.matcher(u.getSchemeSpecificPart());
|
||||
if (m.matches()) {
|
||||
return m.group(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "jrt": {
|
||||
Matcher m = jrtEntry.matcher(u.getSchemeSpecificPart());
|
||||
if (m.matches()) {
|
||||
return m.group(2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "file": {
|
||||
Matcher m = fileEntry.matcher(u.getSchemeSpecificPart());
|
||||
if (m.matches()) {
|
||||
return m.group(2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(fo.getName() + "--" + fo.toUri());
|
||||
}
|
||||
|
||||
private void error(String message, Throwable t) {
|
||||
toolBox.out.println("Error: " + message + ": " + t);
|
||||
errors++;
|
||||
}
|
||||
|
||||
private int errors;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue