8015912: jdeps support to output in dot file format
8026255: Switch jdeps to follow traditional Java option style Reviewed-by: alanb
This commit is contained in:
parent
c57660ca19
commit
1285dee32b
@ -25,9 +25,8 @@
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
import java.util.ArrayList;
|
||||
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -52,8 +51,8 @@ public class Analyzer {
|
||||
};
|
||||
|
||||
private final Type type;
|
||||
private final List<ArchiveDeps> results = new ArrayList<ArchiveDeps>();
|
||||
private final Map<String, Archive> map = new HashMap<String, Archive>();
|
||||
private final Map<Archive, ArchiveDeps> results = new HashMap<>();
|
||||
private final Map<String, Archive> map = new HashMap<>();
|
||||
private final Archive NOT_FOUND
|
||||
= new Archive(JdepsTask.getMessage("artifact.not.found"));
|
||||
|
||||
@ -78,27 +77,27 @@ public class Analyzer {
|
||||
deps = new PackageVisitor(archive);
|
||||
}
|
||||
archive.visit(deps);
|
||||
results.add(deps);
|
||||
results.put(archive, deps);
|
||||
}
|
||||
|
||||
// set the required dependencies
|
||||
for (ArchiveDeps result: results) {
|
||||
for (ArchiveDeps result: results.values()) {
|
||||
for (Set<String> set : result.deps.values()) {
|
||||
for (String target : set) {
|
||||
Archive source = getArchive(target);
|
||||
if (result.archive != source) {
|
||||
if (!result.requiredArchives.contains(source)) {
|
||||
result.requiredArchives.add(source);
|
||||
String profile = "";
|
||||
if (source instanceof JDKArchive) {
|
||||
profile = result.profile != null ? result.profile.toString() : "";
|
||||
if (result.getTargetProfile(target) == null) {
|
||||
profile += ", JDK internal API";
|
||||
// override the value if it accesses any JDK internal
|
||||
result.requireArchives.put(source, profile);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// either a profile name or the archive name
|
||||
String tname = result.getTargetProfile(target);
|
||||
if (tname.isEmpty()) {
|
||||
tname = PlatformClassPath.contains(source)
|
||||
? "JDK internal API (" + source.getFileName() + ")"
|
||||
: source.toString();
|
||||
}
|
||||
if (!result.targetNames.contains(tname)) {
|
||||
result.targetNames.add(tname);
|
||||
if (!result.requireArchives.containsKey(source)) {
|
||||
result.requireArchives.put(source, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,42 +105,46 @@ public class Analyzer {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasDependences(Archive archive) {
|
||||
if (results.containsKey(archive)) {
|
||||
return results.get(archive).deps.size() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public interface Visitor {
|
||||
/**
|
||||
* Visits the source archive to its destination archive of
|
||||
* a recorded dependency.
|
||||
*/
|
||||
void visitArchiveDependence(Archive origin, Archive target, String profile);
|
||||
/**
|
||||
* Visits a recorded dependency from origin to target which can be
|
||||
* a fully-qualified classname, a package name, a profile or
|
||||
* archive name depending on the Analyzer's type.
|
||||
*/
|
||||
void visit(String origin, String target, String profile);
|
||||
/**
|
||||
* Visits the source archive to its destination archive of
|
||||
* a recorded dependency.
|
||||
*/
|
||||
void visit(Archive source, Archive dest);
|
||||
void visitDependence(String origin, Archive source, String target, Archive archive, String profile);
|
||||
}
|
||||
|
||||
public void visitSummary(Visitor v) {
|
||||
for (ArchiveDeps r : results) {
|
||||
for (Archive a : r.requiredArchives) {
|
||||
v.visit(r.archive, a);
|
||||
}
|
||||
for (String name : r.targetNames) {
|
||||
v.visit(r.archive.getFileName(), name, name);
|
||||
}
|
||||
public void visitArchiveDependences(Archive source, Visitor v) {
|
||||
ArchiveDeps r = results.get(source);
|
||||
for (Map.Entry<Archive,String> e : r.requireArchives.entrySet()) {
|
||||
v.visitArchiveDependence(r.archive, e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(Visitor v) {
|
||||
for (ArchiveDeps r: results) {
|
||||
for (Archive a : r.requiredArchives) {
|
||||
v.visit(r.archive, a);
|
||||
}
|
||||
for (String origin : r.deps.keySet()) {
|
||||
for (String target : r.deps.get(origin)) {
|
||||
// filter intra-dependency unless in verbose mode
|
||||
if (type == Type.VERBOSE || getArchive(origin) != getArchive(target)) {
|
||||
v.visit(origin, target, r.getTargetProfile(target));
|
||||
}
|
||||
public void visitDependences(Archive source, Visitor v) {
|
||||
ArchiveDeps r = results.get(source);
|
||||
for (String origin : r.deps.keySet()) {
|
||||
for (String target : r.deps.get(origin)) {
|
||||
Archive archive = getArchive(target);
|
||||
assert source == getArchive(origin);
|
||||
Profile profile = r.getTargetProfile(target);
|
||||
|
||||
// filter intra-dependency unless in verbose mode
|
||||
if (type == Type.VERBOSE || archive != source) {
|
||||
v.visitDependence(origin, source, target, archive,
|
||||
profile != null ? profile.toString() : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,29 +154,15 @@ public class Analyzer {
|
||||
return map.containsKey(name) ? map.get(name) : NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name of the archive for non-JRE class or
|
||||
* internal JRE classes. It returns empty string for SE API.
|
||||
*/
|
||||
public String getArchiveName(String target, String profile) {
|
||||
Archive source = getArchive(target);
|
||||
String name = source.getFileName();
|
||||
if (PlatformClassPath.contains(source))
|
||||
return profile.isEmpty() ? "JDK internal API (" + name + ")" : "";
|
||||
return name;
|
||||
}
|
||||
|
||||
private abstract class ArchiveDeps implements Archive.Visitor {
|
||||
final Archive archive;
|
||||
final Set<Archive> requiredArchives;
|
||||
final SortedSet<String> targetNames;
|
||||
final Map<Archive,String> requireArchives;
|
||||
final SortedMap<String, SortedSet<String>> deps;
|
||||
|
||||
Profile profile = null;
|
||||
ArchiveDeps(Archive archive) {
|
||||
this.archive = archive;
|
||||
this.requiredArchives = new HashSet<Archive>();
|
||||
this.targetNames = new TreeSet<String>();
|
||||
this.deps = new TreeMap<String, SortedSet<String>>();
|
||||
this.requireArchives = new HashMap<>();
|
||||
this.deps = new TreeMap<>();
|
||||
}
|
||||
|
||||
void add(String loc) {
|
||||
@ -188,17 +177,19 @@ public class Analyzer {
|
||||
void add(String origin, String target) {
|
||||
SortedSet<String> set = deps.get(origin);
|
||||
if (set == null) {
|
||||
set = new TreeSet<String>();
|
||||
deps.put(origin, set);
|
||||
deps.put(origin, set = new TreeSet<>());
|
||||
}
|
||||
if (!set.contains(target)) {
|
||||
set.add(target);
|
||||
// find the corresponding profile
|
||||
Profile p = getTargetProfile(target);
|
||||
if (profile == null || (p != null && profile.profile < p.profile)) {
|
||||
profile = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void visit(Location o, Location t);
|
||||
public abstract String getTargetProfile(String target);
|
||||
|
||||
public abstract Profile getTargetProfile(String target);
|
||||
}
|
||||
|
||||
private class ClassVisitor extends ArchiveDeps {
|
||||
@ -211,9 +202,9 @@ public class Analyzer {
|
||||
public void visit(Location o, Location t) {
|
||||
add(o.getClassName(), t.getClassName());
|
||||
}
|
||||
public String getTargetProfile(String target) {
|
||||
public Profile getTargetProfile(String target) {
|
||||
int i = target.lastIndexOf('.');
|
||||
return (i > 0) ? Profiles.getProfileName(target.substring(0, i)) : "";
|
||||
return (i > 0) ? Profile.getProfile(target.substring(0, i)) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,8 +222,8 @@ public class Analyzer {
|
||||
String pkg = loc.getPackageName();
|
||||
return pkg.isEmpty() ? "<unnamed>" : pkg;
|
||||
}
|
||||
public String getTargetProfile(String target) {
|
||||
return Profiles.getProfileName(target);
|
||||
public Profile getTargetProfile(String target) {
|
||||
return Profile.getProfile(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -35,21 +35,20 @@ import java.util.Set;
|
||||
* Represents the source of the class files.
|
||||
*/
|
||||
public class Archive {
|
||||
private final File file;
|
||||
private final Path path;
|
||||
private final String filename;
|
||||
private final ClassFileReader reader;
|
||||
private final Map<Location, Set<Location>> deps
|
||||
= new HashMap<Location, Set<Location>>();
|
||||
private final Map<Location, Set<Location>> deps = new HashMap<>();
|
||||
|
||||
public Archive(String name) {
|
||||
this.file = null;
|
||||
this.path = null;
|
||||
this.filename = name;
|
||||
this.reader = null;
|
||||
}
|
||||
|
||||
public Archive(File f, ClassFileReader reader) {
|
||||
this.file = f;
|
||||
this.filename = f.getName();
|
||||
public Archive(Path p, ClassFileReader reader) {
|
||||
this.path = p;
|
||||
this.filename = path.getFileName().toString();
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
@ -64,14 +63,14 @@ public class Archive {
|
||||
public void addClass(Location origin) {
|
||||
Set<Location> set = deps.get(origin);
|
||||
if (set == null) {
|
||||
set = new HashSet<Location>();
|
||||
set = new HashSet<>();
|
||||
deps.put(origin, set);
|
||||
}
|
||||
}
|
||||
public void addClass(Location origin, Location target) {
|
||||
Set<Location> set = deps.get(origin);
|
||||
if (set == null) {
|
||||
set = new HashSet<Location>();
|
||||
set = new HashSet<>();
|
||||
deps.put(origin, set);
|
||||
}
|
||||
set.add(target);
|
||||
@ -87,7 +86,7 @@ public class Archive {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return file != null ? file.getPath() : filename;
|
||||
return path != null ? path.toString() : filename;
|
||||
}
|
||||
|
||||
interface Visitor {
|
||||
|
@ -45,17 +45,17 @@ public class ClassFileReader {
|
||||
/**
|
||||
* Returns a ClassFileReader instance of a given path.
|
||||
*/
|
||||
public static ClassFileReader newInstance(File path) throws IOException {
|
||||
if (!path.exists()) {
|
||||
throw new FileNotFoundException(path.getAbsolutePath());
|
||||
public static ClassFileReader newInstance(Path path) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
throw new FileNotFoundException(path.toString());
|
||||
}
|
||||
|
||||
if (path.isDirectory()) {
|
||||
return new DirectoryReader(path.toPath());
|
||||
} else if (path.getName().endsWith(".jar")) {
|
||||
return new JarFileReader(path.toPath());
|
||||
if (Files.isDirectory(path)) {
|
||||
return new DirectoryReader(path);
|
||||
} else if (path.getFileName().toString().endsWith(".jar")) {
|
||||
return new JarFileReader(path);
|
||||
} else {
|
||||
return new ClassFileReader(path.toPath());
|
||||
return new ClassFileReader(path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,16 +163,16 @@ public class ClassFileReader {
|
||||
int i = name.lastIndexOf('.');
|
||||
String pathname = name.replace('.', File.separatorChar) + ".class";
|
||||
Path p = path.resolve(pathname);
|
||||
if (!p.toFile().exists()) {
|
||||
if (!Files.exists(p)) {
|
||||
p = path.resolve(pathname.substring(0, i) + "$" +
|
||||
pathname.substring(i+1, pathname.length()));
|
||||
}
|
||||
if (p.toFile().exists()) {
|
||||
if (Files.exists(p)) {
|
||||
return readClassFile(p);
|
||||
}
|
||||
} else {
|
||||
Path p = path.resolve(name + ".class");
|
||||
if (p.toFile().exists()) {
|
||||
if (Files.exists(p)) {
|
||||
return readClassFile(p);
|
||||
}
|
||||
}
|
||||
@ -193,7 +193,7 @@ public class ClassFileReader {
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
if (file.toFile().getName().endsWith(".class")) {
|
||||
if (file.getFileName().toString().endsWith(".class")) {
|
||||
files.add(file);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
|
@ -24,12 +24,18 @@
|
||||
*/
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import com.sun.tools.classfile.AccessFlags;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.Dependencies;
|
||||
import com.sun.tools.classfile.Dependencies.ClassFileError;
|
||||
import com.sun.tools.classfile.Dependency;
|
||||
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
|
||||
import java.io.*;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
@ -67,11 +73,10 @@ class JdepsTask {
|
||||
|
||||
boolean matches(String opt) {
|
||||
for (String a : aliases) {
|
||||
if (a.equals(opt)) {
|
||||
if (a.equals(opt))
|
||||
return true;
|
||||
} else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
|
||||
if (hasArg && opt.startsWith(a + "="))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -96,62 +101,96 @@ class JdepsTask {
|
||||
}
|
||||
|
||||
static Option[] recognizedOptions = {
|
||||
new Option(false, "-h", "-?", "--help") {
|
||||
new Option(false, "-h", "-?", "-help") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.help = true;
|
||||
}
|
||||
},
|
||||
new Option(false, "-s", "--summary") {
|
||||
new Option(true, "-dotoutput") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Path p = Paths.get(arg);
|
||||
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
|
||||
throw new BadArgs("err.dot.output.path", arg);
|
||||
}
|
||||
task.options.dotOutputDir = arg;
|
||||
}
|
||||
},
|
||||
new Option(false, "-s", "-summary") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.showSummary = true;
|
||||
task.options.verbose = Analyzer.Type.SUMMARY;
|
||||
}
|
||||
},
|
||||
new Option(false, "-v", "--verbose") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.verbose = Analyzer.Type.VERBOSE;
|
||||
}
|
||||
},
|
||||
new Option(true, "-V", "--verbose-level") {
|
||||
new Option(false, "-v", "-verbose",
|
||||
"-verbose:package",
|
||||
"-verbose:class")
|
||||
{
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
if ("package".equals(arg)) {
|
||||
task.options.verbose = Analyzer.Type.PACKAGE;
|
||||
} else if ("class".equals(arg)) {
|
||||
task.options.verbose = Analyzer.Type.CLASS;
|
||||
} else {
|
||||
throw new BadArgs("err.invalid.arg.for.option", opt);
|
||||
switch (opt) {
|
||||
case "-v":
|
||||
case "-verbose":
|
||||
task.options.verbose = Analyzer.Type.VERBOSE;
|
||||
break;
|
||||
case "-verbose:package":
|
||||
task.options.verbose = Analyzer.Type.PACKAGE;
|
||||
break;
|
||||
case "-verbose:class":
|
||||
task.options.verbose = Analyzer.Type.CLASS;
|
||||
break;
|
||||
default:
|
||||
throw new BadArgs("err.invalid.arg.for.option", opt);
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(true, "-c", "--classpath") {
|
||||
new Option(true, "-cp", "-classpath") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.classpath = arg;
|
||||
}
|
||||
},
|
||||
new Option(true, "-p", "--package") {
|
||||
new Option(true, "-p", "-package") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.packageNames.add(arg);
|
||||
}
|
||||
},
|
||||
new Option(true, "-e", "--regex") {
|
||||
new Option(true, "-e", "-regex") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.regex = arg;
|
||||
}
|
||||
},
|
||||
new Option(false, "-P", "--profile") {
|
||||
new Option(true, "-include") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.includePattern = Pattern.compile(arg);
|
||||
}
|
||||
},
|
||||
new Option(false, "-P", "-profile") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.showProfile = true;
|
||||
if (Profiles.getProfileCount() == 0) {
|
||||
if (Profile.getProfileCount() == 0) {
|
||||
throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg"));
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(false, "-R", "--recursive") {
|
||||
new Option(false, "-apionly") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.apiOnly = true;
|
||||
}
|
||||
},
|
||||
new Option(false, "-R", "-recursive") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.depth = 0;
|
||||
}
|
||||
},
|
||||
new HiddenOption(true, "-d", "--depth") {
|
||||
new Option(false, "-version") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.version = true;
|
||||
}
|
||||
},
|
||||
new HiddenOption(false, "-fullversion") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.fullVersion = true;
|
||||
}
|
||||
},
|
||||
new HiddenOption(true, "-depth") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
try {
|
||||
task.options.depth = Integer.parseInt(arg);
|
||||
@ -160,16 +199,6 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(false, "--version") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.version = true;
|
||||
}
|
||||
},
|
||||
new HiddenOption(false, "--fullversion") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.fullVersion = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
private static final String PROGNAME = "jdeps";
|
||||
@ -202,7 +231,7 @@ class JdepsTask {
|
||||
if (options.version || options.fullVersion) {
|
||||
showVersion(options.fullVersion);
|
||||
}
|
||||
if (classes.isEmpty() && !options.wildcard) {
|
||||
if (classes.isEmpty() && options.includePattern == null) {
|
||||
if (options.help || options.version || options.fullVersion) {
|
||||
return EXIT_OK;
|
||||
} else {
|
||||
@ -233,19 +262,51 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Archive> sourceLocations = new ArrayList<Archive>();
|
||||
private final List<Archive> sourceLocations = new ArrayList<>();
|
||||
private boolean run() throws IOException {
|
||||
findDependencies();
|
||||
Analyzer analyzer = new Analyzer(options.verbose);
|
||||
analyzer.run(sourceLocations);
|
||||
if (options.verbose == Analyzer.Type.SUMMARY) {
|
||||
printSummary(log, analyzer);
|
||||
if (options.dotOutputDir != null) {
|
||||
Path dir = Paths.get(options.dotOutputDir);
|
||||
Files.createDirectories(dir);
|
||||
generateDotFiles(dir, analyzer);
|
||||
} else {
|
||||
printDependencies(log, analyzer);
|
||||
printRawOutput(log, analyzer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
|
||||
Path summary = dir.resolve("summary.dot");
|
||||
try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
|
||||
DotFileFormatter formatter = new DotFileFormatter(sw, "summary")) {
|
||||
for (Archive archive : sourceLocations) {
|
||||
analyzer.visitArchiveDependences(archive, formatter);
|
||||
}
|
||||
}
|
||||
if (options.verbose != Analyzer.Type.SUMMARY) {
|
||||
for (Archive archive : sourceLocations) {
|
||||
if (analyzer.hasDependences(archive)) {
|
||||
Path dotfile = dir.resolve(archive.getFileName() + ".dot");
|
||||
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
|
||||
DotFileFormatter formatter = new DotFileFormatter(pw, archive)) {
|
||||
analyzer.visitDependences(archive, formatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printRawOutput(PrintWriter writer, Analyzer analyzer) {
|
||||
for (Archive archive : sourceLocations) {
|
||||
RawOutputFormatter formatter = new RawOutputFormatter(writer);
|
||||
analyzer.visitArchiveDependences(archive, formatter);
|
||||
if (options.verbose != Analyzer.Type.SUMMARY) {
|
||||
analyzer.visitDependences(archive, formatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
private boolean isValidClassName(String name) {
|
||||
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||
return false;
|
||||
@ -259,27 +320,43 @@ class JdepsTask {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void findDependencies() throws IOException {
|
||||
Dependency.Finder finder = Dependencies.getClassDependencyFinder();
|
||||
Dependency.Filter filter;
|
||||
if (options.regex != null) {
|
||||
filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
|
||||
private Dependency.Filter getDependencyFilter() {
|
||||
if (options.regex != null) {
|
||||
return Dependencies.getRegexFilter(Pattern.compile(options.regex));
|
||||
} else if (options.packageNames.size() > 0) {
|
||||
filter = Dependencies.getPackageFilter(options.packageNames, false);
|
||||
return Dependencies.getPackageFilter(options.packageNames, false);
|
||||
} else {
|
||||
filter = new Dependency.Filter() {
|
||||
return new Dependency.Filter() {
|
||||
@Override
|
||||
public boolean accepts(Dependency dependency) {
|
||||
return !dependency.getOrigin().equals(dependency.getTarget());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
List<Archive> archives = new ArrayList<Archive>();
|
||||
Deque<String> roots = new LinkedList<String>();
|
||||
private boolean matches(String classname, AccessFlags flags) {
|
||||
if (options.apiOnly && !flags.is(AccessFlags.ACC_PUBLIC)) {
|
||||
return false;
|
||||
} else if (options.includePattern != null) {
|
||||
return options.includePattern.matcher(classname.replace('/', '.')).matches();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void findDependencies() throws IOException {
|
||||
Dependency.Finder finder =
|
||||
options.apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
|
||||
: Dependencies.getClassDependencyFinder();
|
||||
Dependency.Filter filter = getDependencyFilter();
|
||||
|
||||
List<Archive> archives = new ArrayList<>();
|
||||
Deque<String> roots = new LinkedList<>();
|
||||
for (String s : classes) {
|
||||
File f = new File(s);
|
||||
if (f.exists()) {
|
||||
archives.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
Path p = Paths.get(s);
|
||||
if (Files.exists(p)) {
|
||||
archives.add(new Archive(p, ClassFileReader.newInstance(p)));
|
||||
} else {
|
||||
if (isValidClassName(s)) {
|
||||
roots.add(s);
|
||||
@ -289,9 +366,8 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
|
||||
List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup
|
||||
if (options.wildcard) {
|
||||
// include all archives from classpath to the initial list
|
||||
List<Archive> classpaths = new ArrayList<>(); // for class file lookup
|
||||
if (options.includePattern != null) {
|
||||
archives.addAll(getClassPathArchives(options.classpath));
|
||||
} else {
|
||||
classpaths.addAll(getClassPathArchives(options.classpath));
|
||||
@ -305,8 +381,8 @@ class JdepsTask {
|
||||
// Work queue of names of classfiles to be searched.
|
||||
// Entries will be unique, and for classes that do not yet have
|
||||
// dependencies in the results map.
|
||||
Deque<String> deque = new LinkedList<String>();
|
||||
Set<String> doneClasses = new HashSet<String>();
|
||||
Deque<String> deque = new LinkedList<>();
|
||||
Set<String> doneClasses = new HashSet<>();
|
||||
|
||||
// get the immediate dependencies of the input files
|
||||
for (Archive a : archives) {
|
||||
@ -318,16 +394,18 @@ class JdepsTask {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
|
||||
if (!doneClasses.contains(classFileName)) {
|
||||
doneClasses.add(classFileName);
|
||||
}
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d)) {
|
||||
String cn = d.getTarget().getName();
|
||||
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
|
||||
deque.add(cn);
|
||||
if (matches(classFileName, cf.access_flags)) {
|
||||
if (!doneClasses.contains(classFileName)) {
|
||||
doneClasses.add(classFileName);
|
||||
}
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d)) {
|
||||
String cn = d.getTarget().getName();
|
||||
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
|
||||
deque.add(cn);
|
||||
}
|
||||
a.addClass(d.getOrigin(), d.getTarget());
|
||||
}
|
||||
a.addClass(d.getOrigin(), d.getTarget());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -379,46 +457,10 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
unresolved = deque;
|
||||
deque = new LinkedList<String>();
|
||||
deque = new LinkedList<>();
|
||||
} while (!unresolved.isEmpty() && depth-- > 0);
|
||||
}
|
||||
|
||||
private void printSummary(final PrintWriter out, final Analyzer analyzer) {
|
||||
Analyzer.Visitor visitor = new Analyzer.Visitor() {
|
||||
public void visit(String origin, String target, String profile) {
|
||||
if (options.showProfile) {
|
||||
out.format("%-30s -> %s%n", origin, target);
|
||||
}
|
||||
}
|
||||
public void visit(Archive origin, Archive target) {
|
||||
if (!options.showProfile) {
|
||||
out.format("%-30s -> %s%n", origin, target);
|
||||
}
|
||||
}
|
||||
};
|
||||
analyzer.visitSummary(visitor);
|
||||
}
|
||||
|
||||
private void printDependencies(final PrintWriter out, final Analyzer analyzer) {
|
||||
Analyzer.Visitor visitor = new Analyzer.Visitor() {
|
||||
private String pkg = "";
|
||||
public void visit(String origin, String target, String profile) {
|
||||
if (!origin.equals(pkg)) {
|
||||
pkg = origin;
|
||||
out.format(" %s (%s)%n", origin, analyzer.getArchive(origin).getFileName());
|
||||
}
|
||||
out.format(" -> %-50s %s%n", target,
|
||||
(options.showProfile && !profile.isEmpty())
|
||||
? profile
|
||||
: analyzer.getArchiveName(target, profile));
|
||||
}
|
||||
public void visit(Archive origin, Archive target) {
|
||||
out.format("%s -> %s%n", origin, target);
|
||||
}
|
||||
};
|
||||
analyzer.visit(visitor);
|
||||
}
|
||||
|
||||
public void handleOptions(String[] args) throws BadArgs {
|
||||
// process options
|
||||
for (int i=0; i < args.length; i++) {
|
||||
@ -427,7 +469,7 @@ class JdepsTask {
|
||||
Option option = getOption(name);
|
||||
String param = null;
|
||||
if (option.hasArg) {
|
||||
if (name.startsWith("--") && name.indexOf('=') > 0) {
|
||||
if (name.startsWith("-") && name.indexOf('=') > 0) {
|
||||
param = name.substring(name.indexOf('=') + 1, name.length());
|
||||
} else if (i + 1 < args.length) {
|
||||
param = args[++i];
|
||||
@ -447,11 +489,7 @@ class JdepsTask {
|
||||
if (name.charAt(0) == '-') {
|
||||
throw new BadArgs("err.option.after.class", name).showUsage(true);
|
||||
}
|
||||
if (name.equals("*") || name.equals("\"*\"")) {
|
||||
options.wildcard = true;
|
||||
} else {
|
||||
classes.add(name);
|
||||
}
|
||||
classes.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -518,13 +556,15 @@ class JdepsTask {
|
||||
boolean showProfile;
|
||||
boolean showSummary;
|
||||
boolean wildcard;
|
||||
String regex;
|
||||
boolean apiOnly;
|
||||
String dotOutputDir;
|
||||
String classpath = "";
|
||||
int depth = 1;
|
||||
Analyzer.Type verbose = Analyzer.Type.PACKAGE;
|
||||
Set<String> packageNames = new HashSet<String>();
|
||||
Set<String> packageNames = new HashSet<>();
|
||||
String regex; // apply to the dependences
|
||||
Pattern includePattern; // apply to classes
|
||||
}
|
||||
|
||||
private static class ResourceBundleHelper {
|
||||
static final ResourceBundle versionRB;
|
||||
static final ResourceBundle bundle;
|
||||
@ -547,9 +587,9 @@ class JdepsTask {
|
||||
private List<Archive> getArchives(List<String> filenames) throws IOException {
|
||||
List<Archive> result = new ArrayList<Archive>();
|
||||
for (String s : filenames) {
|
||||
File f = new File(s);
|
||||
if (f.exists()) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
Path p = Paths.get(s);
|
||||
if (Files.exists(p)) {
|
||||
result.add(new Archive(p, ClassFileReader.newInstance(p)));
|
||||
} else {
|
||||
warning("warn.file.not.exist", s);
|
||||
}
|
||||
@ -558,18 +598,131 @@ class JdepsTask {
|
||||
}
|
||||
|
||||
private List<Archive> getClassPathArchives(String paths) throws IOException {
|
||||
List<Archive> result = new ArrayList<Archive>();
|
||||
List<Archive> result = new ArrayList<>();
|
||||
if (paths.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (String p : paths.split(File.pathSeparator)) {
|
||||
if (p.length() > 0) {
|
||||
File f = new File(p);
|
||||
if (f.exists()) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
List<Path> files = new ArrayList<>();
|
||||
// wildcard to parse all JAR files e.g. -classpath dir/*
|
||||
int i = p.lastIndexOf(".*");
|
||||
if (i > 0) {
|
||||
Path dir = Paths.get(p.substring(0, i));
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
|
||||
for (Path entry : stream) {
|
||||
files.add(entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
files.add(Paths.get(p));
|
||||
}
|
||||
for (Path f : files) {
|
||||
if (Files.exists(f)) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the file name of the archive for non-JRE class or
|
||||
* internal JRE classes. It returns empty string for SE API.
|
||||
*/
|
||||
private static String getArchiveName(Archive source, String profile) {
|
||||
String name = source.getFileName();
|
||||
if (source instanceof JDKArchive)
|
||||
return profile.isEmpty() ? "JDK internal API (" + name + ")" : "";
|
||||
return name;
|
||||
}
|
||||
|
||||
class RawOutputFormatter implements Analyzer.Visitor {
|
||||
private final PrintWriter writer;
|
||||
RawOutputFormatter(PrintWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
private String pkg = "";
|
||||
@Override
|
||||
public void visitDependence(String origin, Archive source,
|
||||
String target, Archive archive, String profile) {
|
||||
if (!origin.equals(pkg)) {
|
||||
pkg = origin;
|
||||
writer.format(" %s (%s)%n", origin, source.getFileName());
|
||||
}
|
||||
String name = (options.showProfile && !profile.isEmpty())
|
||||
? profile
|
||||
: getArchiveName(archive, profile);
|
||||
writer.format(" -> %-50s %s%n", target, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArchiveDependence(Archive origin, Archive target, String profile) {
|
||||
writer.format("%s -> %s", origin, target);
|
||||
if (options.showProfile && !profile.isEmpty()) {
|
||||
writer.format(" (%s)%n", profile);
|
||||
} else {
|
||||
writer.format("%n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DotFileFormatter implements Analyzer.Visitor, AutoCloseable {
|
||||
private final PrintWriter writer;
|
||||
private final String name;
|
||||
DotFileFormatter(PrintWriter writer, String name) {
|
||||
this.writer = writer;
|
||||
this.name = name;
|
||||
writer.format("digraph \"%s\" {%n", name);
|
||||
}
|
||||
DotFileFormatter(PrintWriter writer, Archive archive) {
|
||||
this.writer = writer;
|
||||
this.name = archive.getFileName();
|
||||
writer.format("digraph \"%s\" {%n", name);
|
||||
writer.format(" // Path: %s%n", archive.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
writer.println("}");
|
||||
}
|
||||
|
||||
private final Set<String> edges = new HashSet<>();
|
||||
private String node = "";
|
||||
@Override
|
||||
public void visitDependence(String origin, Archive source,
|
||||
String target, Archive archive, String profile) {
|
||||
if (!node.equals(origin)) {
|
||||
edges.clear();
|
||||
node = origin;
|
||||
}
|
||||
// if -P option is specified, package name -> profile will
|
||||
// be shown and filter out multiple same edges.
|
||||
if (!edges.contains(target)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String name = options.showProfile && !profile.isEmpty()
|
||||
? profile
|
||||
: getArchiveName(archive, profile);
|
||||
writer.format(" %-50s -> %s;%n",
|
||||
String.format("\"%s\"", origin),
|
||||
name.isEmpty() ? String.format("\"%s\"", target)
|
||||
: String.format("\"%s (%s)\"", target, name));
|
||||
edges.add(target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArchiveDependence(Archive origin, Archive target, String profile) {
|
||||
String name = options.showProfile && !profile.isEmpty()
|
||||
? profile : "";
|
||||
writer.format(" %-30s -> \"%s\";%n",
|
||||
String.format("\"%s\"", origin.getFileName()),
|
||||
name.isEmpty()
|
||||
? target.getFileName()
|
||||
: String.format("%s (%s)", target.getFileName(), name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,11 @@
|
||||
*/
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
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.*;
|
||||
@ -38,45 +38,38 @@ import java.util.*;
|
||||
*/
|
||||
class PlatformClassPath {
|
||||
private final static List<Archive> javaHomeArchives = init();
|
||||
|
||||
static List<Archive> getArchives() {
|
||||
return javaHomeArchives;
|
||||
}
|
||||
|
||||
static boolean contains(Archive archive) {
|
||||
return javaHomeArchives.contains(archive);
|
||||
}
|
||||
|
||||
private static List<Archive> init() {
|
||||
List<Archive> result = new ArrayList<Archive>();
|
||||
String javaHome = System.getProperty("java.home");
|
||||
File jre = new File(javaHome, "jre");
|
||||
File lib = new File(javaHome, "lib");
|
||||
|
||||
List<Archive> result = new ArrayList<>();
|
||||
Path home = Paths.get(System.getProperty("java.home"));
|
||||
try {
|
||||
if (jre.exists() && jre.isDirectory()) {
|
||||
result.addAll(addJarFiles(new File(jre, "lib")));
|
||||
result.addAll(addJarFiles(lib));
|
||||
} else if (lib.exists() && lib.isDirectory()) {
|
||||
if (home.endsWith("jre")) {
|
||||
// jar files in <javahome>/jre/lib
|
||||
result.addAll(addJarFiles(home.resolve("lib")));
|
||||
} else if (Files.exists(home.resolve("lib"))) {
|
||||
// either a JRE or a jdk build image
|
||||
File classes = new File(javaHome, "classes");
|
||||
if (classes.exists() && classes.isDirectory()) {
|
||||
Path classes = home.resolve("classes");
|
||||
if (Files.isDirectory(classes)) {
|
||||
// jdk build outputdir
|
||||
result.add(new Archive(classes, ClassFileReader.newInstance(classes)));
|
||||
result.add(new JDKArchive(classes, ClassFileReader.newInstance(classes)));
|
||||
}
|
||||
// add other JAR files
|
||||
result.addAll(addJarFiles(lib));
|
||||
result.addAll(addJarFiles(home.resolve("lib")));
|
||||
} else {
|
||||
throw new RuntimeException("\"" + javaHome + "\" not a JDK home");
|
||||
throw new RuntimeException("\"" + home + "\" not a JDK home");
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new Error(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Archive> addJarFiles(File f) throws IOException {
|
||||
final List<Archive> result = new ArrayList<Archive>();
|
||||
final Path root = f.toPath();
|
||||
private static List<Archive> addJarFiles(final Path root) throws IOException {
|
||||
final List<Archive> result = new ArrayList<>();
|
||||
final Path ext = root.resolve("ext");
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
@ -91,17 +84,30 @@ class PlatformClassPath {
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
public FileVisitResult visitFile(Path p, BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
File f = file.toFile();
|
||||
String fn = f.getName();
|
||||
if (fn.endsWith(".jar") && !fn.equals("alt-rt.jar")) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
String fn = p.getFileName().toString();
|
||||
if (fn.endsWith(".jar")) {
|
||||
// JDK may cobundle with JavaFX that doesn't belong to any profile
|
||||
// Treat jfxrt.jar as regular Archive
|
||||
result.add(fn.equals("jfxrt.jar")
|
||||
? new Archive(p, ClassFileReader.newInstance(p))
|
||||
: new JDKArchive(p, ClassFileReader.newInstance(p)));
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A JDK archive is part of the JDK containing the Java SE API
|
||||
* or implementation classes (i.e. JDK internal API)
|
||||
*/
|
||||
static class JDKArchive extends Archive {
|
||||
JDKArchive(Path p, ClassFileReader reader) {
|
||||
super(p, reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
227
langtools/src/share/classes/com/sun/tools/jdeps/Profile.java
Normal file
227
langtools/src/share/classes/com/sun/tools/jdeps/Profile.java
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 com.sun.tools.jdeps;
|
||||
|
||||
import com.sun.tools.classfile.Annotation;
|
||||
import com.sun.tools.classfile.Annotation.*;
|
||||
import com.sun.tools.classfile.Attribute;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPool.*;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.RuntimeAnnotations_attribute;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* Build the profile information from ct.sym if exists.
|
||||
*/
|
||||
enum Profile {
|
||||
|
||||
COMPACT1("compact1", 1),
|
||||
COMPACT2("compact2", 2),
|
||||
COMPACT3("compact3", 3),
|
||||
FULL_JRE("Full JRE", 4);
|
||||
|
||||
final String name;
|
||||
final int profile;
|
||||
final Set<String> packages;
|
||||
final Set<String> proprietaryPkgs;
|
||||
|
||||
Profile(String name, int profile) {
|
||||
this.name = name;
|
||||
this.profile = profile;
|
||||
this.packages = new HashSet<>();
|
||||
this.proprietaryPkgs = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static int getProfileCount() {
|
||||
return PackageToProfile.map.values().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Profile for the given package name. It returns an empty
|
||||
* string if the given package is not in any profile.
|
||||
*/
|
||||
public static Profile getProfile(String pn) {
|
||||
Profile profile = PackageToProfile.map.get(pn);
|
||||
return (profile != null && profile.packages.contains(pn))
|
||||
? profile : null;
|
||||
}
|
||||
|
||||
static class PackageToProfile {
|
||||
static Map<String, Profile> map = initProfiles();
|
||||
|
||||
private static Map<String, Profile> initProfiles() {
|
||||
try {
|
||||
String profilesProps = System.getProperty("jdeps.profiles");
|
||||
if (profilesProps != null) {
|
||||
// for testing for JDK development build where ct.sym doesn't exist
|
||||
initProfilesFromProperties(profilesProps);
|
||||
} else {
|
||||
Path home = Paths.get(System.getProperty("java.home"));
|
||||
if (home.endsWith("jre")) {
|
||||
home = home.getParent();
|
||||
}
|
||||
Path ctsym = home.resolve("lib").resolve("ct.sym");
|
||||
if (Files.exists(ctsym)) {
|
||||
// parse ct.sym and load information about profiles
|
||||
try (JarFile jf = new JarFile(ctsym.toFile())) {
|
||||
ClassFileReader reader = ClassFileReader.newInstance(ctsym, jf);
|
||||
for (ClassFile cf : reader.getClassFiles()) {
|
||||
findProfile(cf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | ConstantPoolException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
HashMap<String,Profile> map = new HashMap<>();
|
||||
for (Profile profile : Profile.values()) {
|
||||
for (String pn : profile.packages) {
|
||||
if (!map.containsKey(pn)) {
|
||||
// split packages in the JRE: use the smaller compact
|
||||
map.put(pn, profile);
|
||||
}
|
||||
}
|
||||
for (String pn : profile.proprietaryPkgs) {
|
||||
if (!map.containsKey(pn)) {
|
||||
map.put(pn, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
private static final String PROFILE_ANNOTATION = "Ljdk/Profile+Annotation;";
|
||||
private static final String PROPRIETARY_ANNOTATION = "Lsun/Proprietary+Annotation;";
|
||||
private static Profile findProfile(ClassFile cf) throws ConstantPoolException {
|
||||
RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute)
|
||||
cf.attributes.get(Attribute.RuntimeInvisibleAnnotations);
|
||||
int index = 0;
|
||||
boolean proprietary = false;
|
||||
if (attr != null) {
|
||||
for (int i = 0; i < attr.annotations.length; i++) {
|
||||
Annotation ann = attr.annotations[i];
|
||||
String annType = cf.constant_pool.getUTF8Value(ann.type_index);
|
||||
if (PROFILE_ANNOTATION.equals(annType)) {
|
||||
for (int j = 0; j < ann.num_element_value_pairs; j++) {
|
||||
Annotation.element_value_pair pair = ann.element_value_pairs[j];
|
||||
Primitive_element_value ev = (Primitive_element_value) pair.value;
|
||||
CONSTANT_Integer_info info = (CONSTANT_Integer_info)
|
||||
cf.constant_pool.get(ev.const_value_index);
|
||||
index = info.value;
|
||||
break;
|
||||
}
|
||||
} else if (PROPRIETARY_ANNOTATION.equals(annType)) {
|
||||
proprietary = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Profile p = null; // default
|
||||
switch (index) {
|
||||
case 1:
|
||||
p = Profile.COMPACT1; break;
|
||||
case 2:
|
||||
p = Profile.COMPACT2; break;
|
||||
case 3:
|
||||
p = Profile.COMPACT3; break;
|
||||
case 4:
|
||||
p = Profile.FULL_JRE; break;
|
||||
default:
|
||||
// skip classes with profile=0
|
||||
// Inner classes are not annotated with the profile annotation
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = cf.getName();
|
||||
int i = name.lastIndexOf('/');
|
||||
name = (i > 0) ? name.substring(0, i).replace('/', '.') : "";
|
||||
if (proprietary) {
|
||||
p.proprietaryPkgs.add(name);
|
||||
} else {
|
||||
p.packages.add(name);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
private static void initProfilesFromProperties(String path) throws IOException {
|
||||
Properties props = new Properties();
|
||||
try (FileReader reader = new FileReader(path)) {
|
||||
props.load(reader);
|
||||
}
|
||||
for (Profile prof : Profile.values()) {
|
||||
int i = prof.profile;
|
||||
String key = props.getProperty("profile." + i + ".name");
|
||||
if (key == null) {
|
||||
throw new RuntimeException(key + " missing in " + path);
|
||||
}
|
||||
String n = props.getProperty("profile." + i + ".packages");
|
||||
String[] pkgs = n.split("\\s+");
|
||||
for (String p : pkgs) {
|
||||
if (p.isEmpty()) continue;
|
||||
prof.packages.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for debugging
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0) {
|
||||
if (Profile.getProfileCount() == 0) {
|
||||
System.err.println("No profile is present in this JDK");
|
||||
}
|
||||
for (Profile p : Profile.values()) {
|
||||
String profileName = p.name;
|
||||
SortedSet<String> set = new TreeSet<>(p.packages);
|
||||
for (String s : set) {
|
||||
// filter out the inner classes that are not annotated with
|
||||
// the profile annotation
|
||||
if (PackageToProfile.map.get(s) == p) {
|
||||
System.out.format("%2d: %-10s %s%n", p.profile, profileName, s);
|
||||
profileName = "";
|
||||
} else {
|
||||
System.err.format("Split package: %s in %s and %s %n",
|
||||
s, PackageToProfile.map.get(s).name, p.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String pn : args) {
|
||||
System.out.format("%s in %s%n", pn, getProfile(pn));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 com.sun.tools.jdeps;
|
||||
|
||||
import com.sun.tools.classfile.Annotation;
|
||||
import com.sun.tools.classfile.Annotation.*;
|
||||
import com.sun.tools.classfile.Attribute;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPool;
|
||||
import com.sun.tools.classfile.ConstantPool.*;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.RuntimeAnnotations_attribute;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* Build the profile information from ct.sym if exists.
|
||||
*/
|
||||
class Profiles {
|
||||
private static final Map<String,Profile> map = initProfiles();
|
||||
/**
|
||||
* Returns the name of the profile for the given package name.
|
||||
* It returns an empty string if the given package is not in any profile.
|
||||
*/
|
||||
public static String getProfileName(String pn) {
|
||||
Profile profile = map.get(pn);
|
||||
return (profile != null && profile.packages.contains(pn))
|
||||
? profile.name : "";
|
||||
}
|
||||
|
||||
public static int getProfileCount() {
|
||||
return new HashSet<Profile>(map.values()).size();
|
||||
}
|
||||
|
||||
private static Map<String,Profile> initProfiles() {
|
||||
List<Profile> profiles = new ArrayList<Profile>();
|
||||
try {
|
||||
String profilesProps = System.getProperty("jdeps.profiles");
|
||||
if (profilesProps != null) {
|
||||
// for testing for JDK development build where ct.sym doesn't exist
|
||||
initProfilesFromProperties(profiles, profilesProps);
|
||||
} else {
|
||||
Path home = Paths.get(System.getProperty("java.home"));
|
||||
if (home.endsWith("jre")) {
|
||||
home = home.getParent();
|
||||
}
|
||||
Path ctsym = home.resolve("lib").resolve("ct.sym");
|
||||
if (ctsym.toFile().exists()) {
|
||||
// add a default Full JRE
|
||||
profiles.add(0, new Profile("Full JRE", 0));
|
||||
// parse ct.sym and load information about profiles
|
||||
try (JarFile jf = new JarFile(ctsym.toFile())) {
|
||||
ClassFileReader reader = ClassFileReader.newInstance(ctsym, jf);
|
||||
for (ClassFile cf : reader.getClassFiles()) {
|
||||
findProfile(profiles, cf);
|
||||
}
|
||||
}
|
||||
|
||||
// merge the last Profile with the "Full JRE"
|
||||
if (profiles.size() > 1) {
|
||||
Profile fullJRE = profiles.get(0);
|
||||
Profile p = profiles.remove(profiles.size() - 1);
|
||||
for (String pn : fullJRE.packages) {
|
||||
// The last profile contains the packages determined from ct.sym.
|
||||
// Move classes annotated profile==0 or no attribute that are
|
||||
// added in the fullJRE profile to either supported or proprietary
|
||||
// packages appropriately
|
||||
if (p.proprietaryPkgs.contains(pn)) {
|
||||
p.proprietaryPkgs.add(pn);
|
||||
} else {
|
||||
p.packages.add(pn);
|
||||
}
|
||||
}
|
||||
fullJRE.packages.clear();
|
||||
fullJRE.proprietaryPkgs.clear();
|
||||
fullJRE.packages.addAll(p.packages);
|
||||
fullJRE.proprietaryPkgs.addAll(p.proprietaryPkgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | ConstantPoolException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
HashMap<String,Profile> map = new HashMap<String,Profile>();
|
||||
for (Profile profile : profiles) {
|
||||
// Inner classes are not annotated with the profile annotation
|
||||
// packages may be in one profile but also appear in the Full JRE
|
||||
// Full JRE is always the first element in profiles list and
|
||||
// so the map will contain the appropriate Profile
|
||||
for (String pn : profile.packages) {
|
||||
map.put(pn, profile);
|
||||
}
|
||||
for (String pn : profile.proprietaryPkgs) {
|
||||
map.put(pn, profile);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static final String PROFILE_ANNOTATION = "Ljdk/Profile+Annotation;";
|
||||
private static final String PROPRIETARY_ANNOTATION = "Lsun/Proprietary+Annotation;";
|
||||
private static Profile findProfile(List<Profile> profiles, ClassFile cf)
|
||||
throws ConstantPoolException
|
||||
{
|
||||
RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute)
|
||||
cf.attributes.get(Attribute.RuntimeInvisibleAnnotations);
|
||||
int index = 0;
|
||||
boolean proprietary = false;
|
||||
if (attr != null) {
|
||||
for (int i = 0; i < attr.annotations.length; i++) {
|
||||
Annotation ann = attr.annotations[i];
|
||||
String annType = cf.constant_pool.getUTF8Value(ann.type_index);
|
||||
if (PROFILE_ANNOTATION.equals(annType)) {
|
||||
for (int j = 0; j < ann.num_element_value_pairs; j++) {
|
||||
Annotation.element_value_pair pair = ann.element_value_pairs[j];
|
||||
Primitive_element_value ev = (Primitive_element_value)pair.value;
|
||||
CONSTANT_Integer_info info = (CONSTANT_Integer_info)
|
||||
cf.constant_pool.get(ev.const_value_index);
|
||||
index = info.value;
|
||||
break;
|
||||
}
|
||||
} else if (PROPRIETARY_ANNOTATION.equals(annType)) {
|
||||
proprietary = true;
|
||||
}
|
||||
}
|
||||
if (index >= profiles.size()) {
|
||||
Profile p = null;
|
||||
for (int i = profiles.size(); i <= index; i++) {
|
||||
p = new Profile(i);
|
||||
profiles.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Profile p = profiles.get(index);
|
||||
String name = cf.getName();
|
||||
int i = name.lastIndexOf('/');
|
||||
name = (i > 0) ? name.substring(0, i).replace('/','.') : "";
|
||||
if (proprietary) {
|
||||
p.proprietaryPkgs.add(name);
|
||||
} else {
|
||||
p.packages.add(name);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
private static void initProfilesFromProperties(List<Profile> profiles, String path)
|
||||
throws IOException
|
||||
{
|
||||
Properties props = new Properties();
|
||||
try (FileReader reader = new FileReader(path)) {
|
||||
props.load(reader);
|
||||
}
|
||||
int i=1;
|
||||
String key;
|
||||
while (props.containsKey((key = "profile." + i + ".name"))) {
|
||||
Profile profile = new Profile(props.getProperty(key), i);
|
||||
profiles.add(profile);
|
||||
String n = props.getProperty("profile." + i + ".packages");
|
||||
String[] pkgs = n.split("\\s+");
|
||||
for (String p : pkgs) {
|
||||
if (p.isEmpty()) continue;
|
||||
profile.packages.add(p);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Profile {
|
||||
final String name;
|
||||
final int profile;
|
||||
final Set<String> packages;
|
||||
final Set<String> proprietaryPkgs;
|
||||
Profile(int profile) {
|
||||
this("compact" + profile, profile);
|
||||
}
|
||||
Profile(String name, int profile) {
|
||||
this.name = name;
|
||||
this.profile = profile;
|
||||
this.packages = new HashSet<String>();
|
||||
this.proprietaryPkgs = new HashSet<String>();
|
||||
}
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// for debugging
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0) {
|
||||
Profile[] profiles = new Profile[getProfileCount()];
|
||||
for (Profile p : map.values()) {
|
||||
// move the zeroth profile to the last
|
||||
int index = p.profile == 0 ? profiles.length-1 : p.profile-1;
|
||||
profiles[index] = p;
|
||||
}
|
||||
for (Profile p : profiles) {
|
||||
String profileName = p.name;
|
||||
SortedSet<String> set = new TreeSet<String>(p.packages);
|
||||
for (String s : set) {
|
||||
// filter out the inner classes that are not annotated with
|
||||
// the profile annotation
|
||||
if (map.get(s) == p) {
|
||||
System.out.format("%-10s %s%n", profileName, s);
|
||||
profileName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String pn : args) {
|
||||
System.out.format("%s in %s%n", pn, getProfileName(pn));
|
||||
}
|
||||
}
|
||||
}
|
@ -5,46 +5,63 @@ use -h, -? or --help for a list of possible options
|
||||
main.usage=\
|
||||
Usage: {0} <options> <classes...>\n\
|
||||
where <classes> can be a pathname to a .class file, a directory, a JAR file,\n\
|
||||
or a fully-qualified classname or wildcard "*". Possible options include:
|
||||
or a fully-qualified class name. Possible options include:
|
||||
|
||||
error.prefix=Error:
|
||||
warn.prefix=Warning:
|
||||
|
||||
main.opt.h=\
|
||||
\ -h -? --help Print this usage message
|
||||
\ -h -? -help Print this usage message
|
||||
|
||||
main.opt.version=\
|
||||
\ --version Version information
|
||||
|
||||
main.opt.V=\
|
||||
\ -V <level> --verbose-level=<level> Print package-level or class-level dependencies\n\
|
||||
\ Valid levels are: "package" and "class"
|
||||
\ -version Version information
|
||||
|
||||
main.opt.v=\
|
||||
\ -v --verbose Print additional information
|
||||
\ -v -verbose Print all class level dependencies\n\
|
||||
\ -verbose:package Print package-level dependencies excluding\n\
|
||||
\ dependencies within the same archive\n\
|
||||
\ -verbose:class Print class-level dependencies excluding\n\
|
||||
\ dependencies within the same archive
|
||||
|
||||
main.opt.s=\
|
||||
\ -s --summary Print dependency summary only
|
||||
\ -s -summary Print dependency summary only
|
||||
|
||||
main.opt.p=\
|
||||
\ -p <pkg name> --package=<pkg name> Restrict analysis to classes in this package\n\
|
||||
\ (may be given multiple times)
|
||||
\ -p <pkgname> -package <pkgname> Finds dependences in the given package\n\
|
||||
\ (may be given multiple times)
|
||||
|
||||
main.opt.e=\
|
||||
\ -e <regex> --regex=<regex> Restrict analysis to packages matching pattern\n\
|
||||
\ (-p and -e are exclusive)
|
||||
\ -e <regex> -regex <regex> Finds dependences in packages matching pattern\n\
|
||||
\ (-p and -e are exclusive)
|
||||
|
||||
main.opt.include=\
|
||||
\ -include <regex> Restrict analysis to classes matching pattern\n\
|
||||
\ This option filters the list of classes to\n\
|
||||
\ be analyzed. It can be used together with\n\
|
||||
\ -p and -e which apply pattern to the dependences
|
||||
|
||||
main.opt.P=\
|
||||
\ -P --profile Show profile or the file containing a package
|
||||
\ -P -profile Show profile or the file containing a package
|
||||
|
||||
main.opt.c=\
|
||||
\ -c <path> --classpath=<path> Specify where to find class files
|
||||
main.opt.cp=\
|
||||
\ -cp <path> -classpath <path> Specify where to find class files
|
||||
|
||||
main.opt.R=\
|
||||
\ -R --recursive Recursively traverse all dependencies
|
||||
\ -R -recursive Recursively traverse all dependencies
|
||||
|
||||
main.opt.d=\
|
||||
\ -d <depth> --depth=<depth> Specify the depth of the transitive dependency analysis
|
||||
main.opt.apionly=\
|
||||
\ -apionly Restrict analysis to APIs i.e. dependences\n\
|
||||
\ from the signature of public and protected\n\
|
||||
\ members of public classes including field\n\
|
||||
\ type, method parameter types, returned type,\n\
|
||||
\ checked exception types etc
|
||||
|
||||
main.opt.dotoutput=\
|
||||
\ -dotoutput <dir> Destination directory for DOT file output
|
||||
|
||||
main.opt.depth=\
|
||||
\ -depth=<depth> Specify the depth of the transitive\n\
|
||||
\ dependency analysis
|
||||
|
||||
err.unknown.option=unknown option: {0}
|
||||
err.missing.arg=no value given for {0}
|
||||
@ -53,6 +70,7 @@ err.invalid.arg.for.option=invalid argument for option: {0}
|
||||
err.option.after.class=option must be specified before classes: {0}
|
||||
err.option.unsupported={0} not supported: {1}
|
||||
err.profiles.msg=No profile information
|
||||
err.dot.output.path=invalid path: {0}
|
||||
warn.invalid.arg=Invalid classname or pathname not exist: {0}
|
||||
warn.split.package=package {0} defined in {1} {2}
|
||||
|
||||
|
191
langtools/test/tools/jdeps/APIDeps.java
Normal file
191
langtools/test/tools/jdeps/APIDeps.java
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8015912
|
||||
* @summary find API dependencies
|
||||
* @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G
|
||||
* @run main APIDeps
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class APIDeps {
|
||||
private static boolean symbolFileExist = initProfiles();
|
||||
private static boolean initProfiles() {
|
||||
// check if ct.sym exists; if not use the profiles.properties file
|
||||
Path home = Paths.get(System.getProperty("java.home"));
|
||||
if (home.endsWith("jre")) {
|
||||
home = home.getParent();
|
||||
}
|
||||
Path ctsym = home.resolve("lib").resolve("ct.sym");
|
||||
boolean symbolExists = ctsym.toFile().exists();
|
||||
if (!symbolExists) {
|
||||
Path testSrcProfiles =
|
||||
Paths.get(System.getProperty("test.src", "."), "profiles.properties");
|
||||
if (!testSrcProfiles.toFile().exists())
|
||||
throw new Error(testSrcProfiles + " does not exist");
|
||||
System.out.format("%s doesn't exist.%nUse %s to initialize profiles info%n",
|
||||
ctsym, testSrcProfiles);
|
||||
System.setProperty("jdeps.profiles", testSrcProfiles.toString());
|
||||
}
|
||||
return symbolExists;
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
int errors = 0;
|
||||
errors += new APIDeps().run();
|
||||
if (errors > 0)
|
||||
throw new Exception(errors + " errors found");
|
||||
}
|
||||
|
||||
int run() throws IOException {
|
||||
File testDir = new File(System.getProperty("test.classes", "."));
|
||||
String testDirBasename = testDir.toPath().getFileName().toString();
|
||||
File mDir = new File(testDir, "m");
|
||||
// all dependencies
|
||||
test(new File(mDir, "Bar.class"),
|
||||
new String[] {"java.lang.Object", "java.lang.String",
|
||||
"java.util.Set", "java.util.HashSet",
|
||||
"java.lang.management.ManagementFactory",
|
||||
"java.lang.management.RuntimeMXBean",
|
||||
"b.B", "c.C", "d.D", "f.F", "g.G"},
|
||||
new String[] {"compact1", "compact3", testDirBasename},
|
||||
new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
|
||||
test(new File(mDir, "Foo.class"),
|
||||
new String[] {"c.I", "e.E", "f.F", "m.Bar"},
|
||||
new String[] {testDirBasename},
|
||||
new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
|
||||
test(new File(mDir, "Gee.class"),
|
||||
new String[] {"g.G", "sun.misc.Lock"},
|
||||
new String[] {testDirBasename, "JDK internal API"},
|
||||
new String[] {"-classpath", testDir.getPath(), "-verbose"});
|
||||
// parse only APIs
|
||||
test(mDir,
|
||||
new String[] {"java.lang.Object", "java.lang.String",
|
||||
"java.util.Set",
|
||||
"c.C", "d.D", "c.I", "e.E", "m.Bar"},
|
||||
new String[] {"compact1", testDirBasename, mDir.getName()},
|
||||
new String[] {"-classpath", testDir.getPath(), "-verbose", "-P", "-apionly"});
|
||||
return errors;
|
||||
}
|
||||
|
||||
void test(File file, String[] expect, String[] profiles) {
|
||||
test(file, expect, profiles, new String[0]);
|
||||
}
|
||||
|
||||
void test(File file, String[] expect, String[] profiles, String[] options) {
|
||||
List<String> args = new ArrayList<>(Arrays.asList(options));
|
||||
if (file != null) {
|
||||
args.add(file.getPath());
|
||||
}
|
||||
checkResult("api-dependencies", expect, profiles,
|
||||
jdeps(args.toArray(new String[0])));
|
||||
}
|
||||
|
||||
Map<String,String> jdeps(String... args) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
System.err.println("jdeps " + Arrays.toString(args));
|
||||
int rc = com.sun.tools.jdeps.Main.run(args, pw);
|
||||
pw.close();
|
||||
String out = sw.toString();
|
||||
if (!out.isEmpty())
|
||||
System.err.println(out);
|
||||
if (rc != 0)
|
||||
throw new Error("jdeps failed: rc=" + rc);
|
||||
return findDeps(out);
|
||||
}
|
||||
|
||||
// Pattern used to parse lines
|
||||
private static Pattern linePattern = Pattern.compile(".*\r?\n");
|
||||
private static Pattern pattern = Pattern.compile("\\s+ -> (\\S+) +(.*)");
|
||||
|
||||
// Use the linePattern to break the given String into lines, applying
|
||||
// the pattern to each line to see if we have a match
|
||||
private static Map<String,String> findDeps(String out) {
|
||||
Map<String,String> result = new HashMap<>();
|
||||
Matcher lm = linePattern.matcher(out); // Line matcher
|
||||
Matcher pm = null; // Pattern matcher
|
||||
int lines = 0;
|
||||
while (lm.find()) {
|
||||
lines++;
|
||||
CharSequence cs = lm.group(); // The current line
|
||||
if (pm == null)
|
||||
pm = pattern.matcher(cs);
|
||||
else
|
||||
pm.reset(cs);
|
||||
if (pm.find())
|
||||
result.put(pm.group(1), pm.group(2).trim());
|
||||
if (lm.end() == out.length())
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void checkResult(String label, String[] expect, Collection<String> found) {
|
||||
List<String> list = Arrays.asList(expect);
|
||||
if (!isEqual(list, found))
|
||||
error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'");
|
||||
}
|
||||
|
||||
void checkResult(String label, String[] expect, String[] profiles, Map<String,String> result) {
|
||||
// check the dependencies
|
||||
checkResult(label, expect, result.keySet());
|
||||
// check profile information
|
||||
Set<String> values = new TreeSet<>();
|
||||
String internal = "JDK internal API";
|
||||
for (String s: result.values()) {
|
||||
if (s.startsWith(internal)){
|
||||
values.add(internal);
|
||||
} else {
|
||||
values.add(s);
|
||||
}
|
||||
}
|
||||
checkResult(label, profiles, values);
|
||||
}
|
||||
|
||||
boolean isEqual(List<String> expected, Collection<String> found) {
|
||||
if (expected.size() != found.size())
|
||||
return false;
|
||||
|
||||
List<String> list = new ArrayList<>(found);
|
||||
list.removeAll(expected);
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
void error(String msg) {
|
||||
System.err.println("Error: " + msg);
|
||||
errors++;
|
||||
}
|
||||
|
||||
int errors;
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8003562 8005428
|
||||
* @bug 8003562 8005428 8015912
|
||||
* @summary Basic tests for jdeps tool
|
||||
* @build Test p.Foo
|
||||
* @run main Basic
|
||||
@ -79,40 +79,33 @@ public class Basic {
|
||||
new String[] {"compact1", "compact1", "compact3"});
|
||||
// test class-level dependency output
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang.Object", "p.Foo"},
|
||||
new String[] {"compact1", "not found"},
|
||||
new String[] {"-V", "class"});
|
||||
new String[] {"java.lang.Object", "java.lang.String", "p.Foo"},
|
||||
new String[] {"compact1", "compact1", "not found"},
|
||||
new String[] {"-verbose:class"});
|
||||
// test -p option
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"p.Foo"},
|
||||
new String[] {"not found"},
|
||||
new String[] {"--verbose-level=class", "-p", "p"});
|
||||
new String[] {"-verbose:class", "-p", "p"});
|
||||
// test -e option
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"p.Foo"},
|
||||
new String[] {"not found"},
|
||||
new String[] {"-V", "class", "-e", "p\\..*"});
|
||||
new String[] {"-verbose:class", "-e", "p\\..*"});
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang"},
|
||||
new String[] {"compact1"},
|
||||
new String[] {"-V", "package", "-e", "java\\.lang\\..*"});
|
||||
// test -classpath and wildcard options
|
||||
new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
|
||||
// test -classpath and -include options
|
||||
test(null,
|
||||
new String[] {"com.sun.tools.jdeps", "java.lang", "java.util",
|
||||
"java.util.regex", "java.io", "java.nio.file",
|
||||
new String[] {"java.lang", "java.util",
|
||||
"java.lang.management"},
|
||||
new String[] {(symbolFileExist? "not found" : "JDK internal API (classes)"),
|
||||
"compact1", "compact1", "compact1",
|
||||
"compact1", "compact1", "compact3"},
|
||||
new String[] {"--classpath", testDir.getPath(), "*"});
|
||||
/* Temporary disable this test case. Test.class has a dependency
|
||||
* on java.lang.String on certain windows machine (8008479).
|
||||
// -v shows intra-dependency
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang.Object", "p.Foo"},
|
||||
new String[] {"compact1", testDir.getName()},
|
||||
new String[] {"-v", "--classpath", testDir.getPath(), "Test.class"});
|
||||
*/
|
||||
new String[] {"compact1", "compact1", "compact3"},
|
||||
new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"});
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang.Object", "java.lang.String", "p.Foo"},
|
||||
new String[] {"compact1", "compact1", testDir.getName()},
|
||||
new String[] {"-v", "-classpath", testDir.getPath(), "Test.class"});
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
@ -25,4 +25,7 @@ public class Test {
|
||||
public void test() {
|
||||
p.Foo f = new p.Foo();
|
||||
}
|
||||
private String name() {
|
||||
return "this test";
|
||||
}
|
||||
}
|
||||
|
32
langtools/test/tools/jdeps/b/B.java
Normal file
32
langtools/test/tools/jdeps/b/B.java
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 b;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({TYPE, METHOD, FIELD})
|
||||
public @interface B {
|
||||
}
|
27
langtools/test/tools/jdeps/c/C.java
Normal file
27
langtools/test/tools/jdeps/c/C.java
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 c;
|
||||
|
||||
public class C {
|
||||
}
|
28
langtools/test/tools/jdeps/c/I.java
Normal file
28
langtools/test/tools/jdeps/c/I.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 c;
|
||||
|
||||
public interface I {
|
||||
void run();
|
||||
}
|
27
langtools/test/tools/jdeps/d/D.java
Normal file
27
langtools/test/tools/jdeps/d/D.java
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 d;
|
||||
|
||||
public class D {
|
||||
}
|
28
langtools/test/tools/jdeps/e/E.java
Normal file
28
langtools/test/tools/jdeps/e/E.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 e;
|
||||
|
||||
// use compact2
|
||||
public class E extends java.rmi.RemoteException {
|
||||
}
|
31
langtools/test/tools/jdeps/f/F.java
Normal file
31
langtools/test/tools/jdeps/f/F.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 f;
|
||||
|
||||
public class F {
|
||||
public F() {
|
||||
// jdk internal API
|
||||
sun.misc.Unsafe.getUnsafe();
|
||||
}
|
||||
}
|
29
langtools/test/tools/jdeps/g/G.java
Normal file
29
langtools/test/tools/jdeps/g/G.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 g;
|
||||
|
||||
public class G {
|
||||
// Full JRE
|
||||
private static final boolean gui = java.beans.Beans.isGuiAvailable();
|
||||
}
|
50
langtools/test/tools/jdeps/m/Bar.java
Normal file
50
langtools/test/tools/jdeps/m/Bar.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 m;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@b.B
|
||||
public class Bar {
|
||||
public final Set<String> set = new HashSet<>();
|
||||
protected d.D d;
|
||||
private f.F f;
|
||||
|
||||
public Bar() {
|
||||
// compact3
|
||||
java.lang.management.ManagementFactory.getRuntimeMXBean();
|
||||
}
|
||||
|
||||
protected c.C c() {
|
||||
return new c.C();
|
||||
}
|
||||
|
||||
/* package private */ void setF(f.F o) {
|
||||
f = o;
|
||||
}
|
||||
|
||||
private g.G g() {
|
||||
return new g.G();
|
||||
}
|
||||
}
|
33
langtools/test/tools/jdeps/m/Foo.java
Normal file
33
langtools/test/tools/jdeps/m/Foo.java
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 m;
|
||||
|
||||
public class Foo extends Bar implements c.I {
|
||||
public void foo() throws e.E {
|
||||
}
|
||||
public void run() {
|
||||
setF(new f.F());
|
||||
}
|
||||
}
|
||||
|
30
langtools/test/tools/jdeps/m/Gee.java
Normal file
30
langtools/test/tools/jdeps/m/Gee.java
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 m;
|
||||
|
||||
|
||||
class Gee extends g.G {
|
||||
public sun.misc.Lock lock;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user