8063145: ToolBox should support extracting classes from a JavaFileManager/Location

Reviewed-by: ksrini
This commit is contained in:
Jonathan Gibbons 2014-11-07 14:51:35 -08:00
parent 3f3f44af47
commit de2b97f133
2 changed files with 194 additions and 64 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,8 @@
* @bug 6508981 * @bug 6508981
* @summary cleanup file separator handling in JavacFileManager * @summary cleanup file separator handling in JavacFileManager
* (This test is specifically to test the new impl of inferBinaryName) * (This test is specifically to test the new impl of inferBinaryName)
* @build p.A * @library /tools/lib
* @build ToolBox p.A
* @run main TestInferBinaryName * @run main TestInferBinaryName
*/ */
@ -61,52 +62,76 @@ public class TestInferBinaryName {
//System.err.println(System.getProperties()); //System.err.println(System.getProperties());
testDirectory(); testDirectory();
testSymbolArchive(); testSymbolArchive();
testZipArchive();
testZipFileIndexArchive(); File testJar = createJar();
testZipFileIndexArchive2();
testZipArchive(testJar);
testZipFileIndexArchive(testJar);
testZipFileIndexArchive2(testJar);
if (errors > 0) if (errors > 0)
throw new Exception(errors + " error found"); throw new Exception(errors + " error found");
} }
File createJar() throws IOException {
File f = new File("test.jar");
try (JavaFileManager fm = new JavacFileManager(new Context(), false, null)) {
ToolBox tb = new ToolBox();
tb.new JarTask(f.getPath())
.files(fm, StandardLocation.PLATFORM_CLASS_PATH, "java.lang.*")
.run();
}
return f;
}
void testDirectory() throws IOException { void testDirectory() throws IOException {
String testClassName = "p.A"; String testClassName = "p.A";
JavaFileManager fm = List<File> testClasses = Arrays.asList(new File(System.getProperty("test.classes")));
getFileManager("test.classes", USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX); try (JavaFileManager fm =
getFileManager(testClasses, USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) {
test("testDirectory", test("testDirectory",
fm, testClassName, "com.sun.tools.javac.file.RegularFileObject"); fm, testClassName, "com.sun.tools.javac.file.RegularFileObject");
} }
}
void testSymbolArchive() throws IOException { void testSymbolArchive() throws IOException {
String testClassName = "java.lang.String"; String testClassName = "java.lang.String";
JavaFileManager fm = List<File> path = getPath(System.getProperty("sun.boot.class.path"));
getFileManager("sun.boot.class.path", USE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX); try (JavaFileManager fm =
getFileManager(path, USE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX)) {
test("testSymbolArchive", test("testSymbolArchive",
fm, testClassName, "com.sun.tools.javac.file.SymbolArchive$SymbolFileObject"); fm, testClassName, "com.sun.tools.javac.file.SymbolArchive$SymbolFileObject");
} }
}
void testZipArchive() throws IOException { void testZipArchive(File testJar) throws IOException {
String testClassName = "java.lang.String"; String testClassName = "java.lang.String";
JavaFileManager fm = List<File> path = Arrays.asList(testJar);
getFileManager("sun.boot.class.path", IGNORE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX); try (JavaFileManager fm =
getFileManager(path, IGNORE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX)) {
test("testZipArchive", test("testZipArchive",
fm, testClassName, "com.sun.tools.javac.file.ZipArchive$ZipFileObject"); fm, testClassName, "com.sun.tools.javac.file.ZipArchive$ZipFileObject");
} }
}
void testZipFileIndexArchive() throws IOException { void testZipFileIndexArchive(File testJar) throws IOException {
String testClassName = "java.lang.String"; String testClassName = "java.lang.String";
JavaFileManager fm = List<File> path = Arrays.asList(testJar);
getFileManager("sun.boot.class.path", USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX); try (JavaFileManager fm =
getFileManager(path, USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) {
test("testZipFileIndexArchive", test("testZipFileIndexArchive",
fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject"); fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject");
} }
}
void testZipFileIndexArchive2() throws IOException { void testZipFileIndexArchive2(File testJar) throws IOException {
String testClassName = "java.lang.String"; String testClassName = "java.lang.String";
JavaFileManager fm = List<File> path = Arrays.asList(testJar);
getFileManager("sun.boot.class.path", IGNORE_SYMBOL_FILE, USE_ZIP_FILE_INDEX); try (JavaFileManager fm =
getFileManager(path, IGNORE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) {
test("testZipFileIndexArchive2", test("testZipFileIndexArchive2",
fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject"); fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject");
} }
}
/** /**
* @param testName for debugging * @param testName for debugging
@ -133,7 +158,7 @@ public class TestInferBinaryName {
System.err.println("OK"); System.err.println("OK");
} }
JavaFileManager getFileManager(String classpathProperty, JavaFileManager getFileManager(List<File> path,
boolean symFileKind, boolean symFileKind,
boolean zipFileIndexKind) boolean zipFileIndexKind)
throws IOException { throws IOException {
@ -145,7 +170,6 @@ public class TestInferBinaryName {
if (symFileKind == IGNORE_SYMBOL_FILE) if (symFileKind == IGNORE_SYMBOL_FILE)
options.put("ignore.symbol.file", "true"); options.put("ignore.symbol.file", "true");
JavacFileManager fm = new JavacFileManager(ctx, false, null); JavacFileManager fm = new JavacFileManager(ctx, false, null);
List<File> path = getPath(System.getProperty(classpathProperty));
fm.setLocation(CLASS_PATH, path); fm.setLocation(CLASS_PATH, path);
return fm; return fm;
} }

View file

@ -21,6 +21,7 @@
* questions. * questions.
*/ */
import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -28,6 +29,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.FilterWriter; import java.io.FilterWriter;
import java.io.IOError;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -49,12 +51,15 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
@ -64,18 +69,20 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import javax.tools.FileObject; import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager; import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager; import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.JavaFileManager.Location;
import javax.tools.SimpleJavaFileObject; import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager; import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
import com.sun.tools.javac.api.JavacTaskImpl; import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.api.JavacTool;
import java.io.IOError;
/** /**
* Utility methods and classes for writing jtreg tests for * Utility methods and classes for writing jtreg tests for
@ -1308,6 +1315,7 @@ public class ToolBox {
private String mainClass; private String mainClass;
private Path baseDir; private Path baseDir;
private List<Path> paths; private List<Path> paths;
private Set<FileObject> fileObjects;
/** /**
* Creates a task to write jar files, using API mode. * Creates a task to write jar files, using API mode.
@ -1315,6 +1323,7 @@ public class ToolBox {
public JarTask() { public JarTask() {
super(Mode.API); super(Mode.API);
paths = Collections.emptyList(); paths = Collections.emptyList();
fileObjects = new LinkedHashSet<>();
} }
/** /**
@ -1391,6 +1400,53 @@ public class ToolBox {
return this; 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, 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, 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, Location l, String path) throws IOException {
JavaFileObject fo = fm.getJavaFileForInput(l, path, Kind.CLASS);
fileObjects.add(fo);
}
/** /**
* Provides limited jar command-like functionality. * Provides limited jar command-like functionality.
* The supported commands are: * The supported commands are:
@ -1464,42 +1520,19 @@ public class ToolBox {
StreamOutput sysOut = new StreamOutput(System.out, System::setOut); StreamOutput sysOut = new StreamOutput(System.out, System::setOut);
StreamOutput sysErr = new StreamOutput(System.err, System::setErr); StreamOutput sysErr = new StreamOutput(System.err, System::setErr);
int rc;
Map<OutputKind, String> outputMap = new HashMap<>(); Map<OutputKind, String> outputMap = new HashMap<>();
try (OutputStream os = Files.newOutputStream(jar); try (OutputStream os = Files.newOutputStream(jar);
JarOutputStream jos = openJar(os, m)) { JarOutputStream jos = openJar(os, m)) {
Path base = (baseDir == null) ? currDir : baseDir; writeFiles(jos);
for (Path path: paths) { writeFileObjects(jos);
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);
jos.write(Files.readAllBytes(file));
jos.closeEntry();
return FileVisitResult.CONTINUE;
} catch (IOException e) { } catch (IOException e) {
System.err.println("Error adding " + file + " to jar file: " + e); error("Exception while opening " + jar, e);
return FileVisitResult.TERMINATE;
}
}
});
}
rc = 0;
} catch (IOException e) {
System.err.println("Error opening " + jar + ": " + e);
rc = 1;
} finally { } finally {
outputMap.put(OutputKind.STDOUT, sysOut.close()); outputMap.put(OutputKind.STDOUT, sysOut.close());
outputMap.put(OutputKind.STDERR, sysErr.close()); outputMap.put(OutputKind.STDERR, sysErr.close());
} }
return checkExit(new Result(this, rc, outputMap)); return checkExit(new Result(this, (errors == 0) ? 0 : 1, outputMap));
} }
private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException { private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException {
@ -1512,6 +1545,79 @@ public class ToolBox {
} }
} }
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/[^/]+/)?(.*)");
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;
}
throw new IllegalArgumentException(fo.getName());
}
private void error(String message, Throwable t) {
out.println("Error: " + message + ": " + t);
errors++;
}
private int errors;
} }
/** /**