mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 03:24:38 +02:00
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
21 changed files with 1176 additions and 521 deletions
|
@ -25,9 +25,8 @@
|
||||||
package com.sun.tools.jdeps;
|
package com.sun.tools.jdeps;
|
||||||
|
|
||||||
import com.sun.tools.classfile.Dependency.Location;
|
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.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -52,8 +51,8 @@ public class Analyzer {
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
private final List<ArchiveDeps> results = new ArrayList<ArchiveDeps>();
|
private final Map<Archive, ArchiveDeps> results = new HashMap<>();
|
||||||
private final Map<String, Archive> map = new HashMap<String, Archive>();
|
private final Map<String, Archive> map = new HashMap<>();
|
||||||
private final Archive NOT_FOUND
|
private final Archive NOT_FOUND
|
||||||
= new Archive(JdepsTask.getMessage("artifact.not.found"));
|
= new Archive(JdepsTask.getMessage("artifact.not.found"));
|
||||||
|
|
||||||
|
@ -78,27 +77,27 @@ public class Analyzer {
|
||||||
deps = new PackageVisitor(archive);
|
deps = new PackageVisitor(archive);
|
||||||
}
|
}
|
||||||
archive.visit(deps);
|
archive.visit(deps);
|
||||||
results.add(deps);
|
results.put(archive, deps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the required dependencies
|
// set the required dependencies
|
||||||
for (ArchiveDeps result: results) {
|
for (ArchiveDeps result: results.values()) {
|
||||||
for (Set<String> set : result.deps.values()) {
|
for (Set<String> set : result.deps.values()) {
|
||||||
for (String target : set) {
|
for (String target : set) {
|
||||||
Archive source = getArchive(target);
|
Archive source = getArchive(target);
|
||||||
if (result.archive != source) {
|
if (result.archive != source) {
|
||||||
if (!result.requiredArchives.contains(source)) {
|
String profile = "";
|
||||||
result.requiredArchives.add(source);
|
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)) {
|
if (!result.requireArchives.containsKey(source)) {
|
||||||
result.targetNames.add(tname);
|
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 {
|
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
|
* Visits a recorded dependency from origin to target which can be
|
||||||
* a fully-qualified classname, a package name, a profile or
|
* a fully-qualified classname, a package name, a profile or
|
||||||
* archive name depending on the Analyzer's type.
|
* archive name depending on the Analyzer's type.
|
||||||
*/
|
*/
|
||||||
void visit(String origin, String target, String profile);
|
void visitDependence(String origin, Archive source, String target, Archive archive, String profile);
|
||||||
/**
|
|
||||||
* Visits the source archive to its destination archive of
|
|
||||||
* a recorded dependency.
|
|
||||||
*/
|
|
||||||
void visit(Archive source, Archive dest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitSummary(Visitor v) {
|
public void visitArchiveDependences(Archive source, Visitor v) {
|
||||||
for (ArchiveDeps r : results) {
|
ArchiveDeps r = results.get(source);
|
||||||
for (Archive a : r.requiredArchives) {
|
for (Map.Entry<Archive,String> e : r.requireArchives.entrySet()) {
|
||||||
v.visit(r.archive, a);
|
v.visitArchiveDependence(r.archive, e.getKey(), e.getValue());
|
||||||
}
|
|
||||||
for (String name : r.targetNames) {
|
|
||||||
v.visit(r.archive.getFileName(), name, name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visit(Visitor v) {
|
public void visitDependences(Archive source, Visitor v) {
|
||||||
for (ArchiveDeps r: results) {
|
ArchiveDeps r = results.get(source);
|
||||||
for (Archive a : r.requiredArchives) {
|
|
||||||
v.visit(r.archive, a);
|
|
||||||
}
|
|
||||||
for (String origin : r.deps.keySet()) {
|
for (String origin : r.deps.keySet()) {
|
||||||
for (String target : r.deps.get(origin)) {
|
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
|
// filter intra-dependency unless in verbose mode
|
||||||
if (type == Type.VERBOSE || getArchive(origin) != getArchive(target)) {
|
if (type == Type.VERBOSE || archive != source) {
|
||||||
v.visit(origin, target, r.getTargetProfile(target));
|
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;
|
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 {
|
private abstract class ArchiveDeps implements Archive.Visitor {
|
||||||
final Archive archive;
|
final Archive archive;
|
||||||
final Set<Archive> requiredArchives;
|
final Map<Archive,String> requireArchives;
|
||||||
final SortedSet<String> targetNames;
|
|
||||||
final SortedMap<String, SortedSet<String>> deps;
|
final SortedMap<String, SortedSet<String>> deps;
|
||||||
|
Profile profile = null;
|
||||||
ArchiveDeps(Archive archive) {
|
ArchiveDeps(Archive archive) {
|
||||||
this.archive = archive;
|
this.archive = archive;
|
||||||
this.requiredArchives = new HashSet<Archive>();
|
this.requireArchives = new HashMap<>();
|
||||||
this.targetNames = new TreeSet<String>();
|
this.deps = new TreeMap<>();
|
||||||
this.deps = new TreeMap<String, SortedSet<String>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(String loc) {
|
void add(String loc) {
|
||||||
|
@ -188,17 +177,19 @@ public class Analyzer {
|
||||||
void add(String origin, String target) {
|
void add(String origin, String target) {
|
||||||
SortedSet<String> set = deps.get(origin);
|
SortedSet<String> set = deps.get(origin);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
set = new TreeSet<String>();
|
deps.put(origin, set = new TreeSet<>());
|
||||||
deps.put(origin, set);
|
|
||||||
}
|
}
|
||||||
if (!set.contains(target)) {
|
if (!set.contains(target)) {
|
||||||
set.add(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 void visit(Location o, Location t);
|
||||||
public abstract String getTargetProfile(String target);
|
public abstract Profile getTargetProfile(String target);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ClassVisitor extends ArchiveDeps {
|
private class ClassVisitor extends ArchiveDeps {
|
||||||
|
@ -211,9 +202,9 @@ public class Analyzer {
|
||||||
public void visit(Location o, Location t) {
|
public void visit(Location o, Location t) {
|
||||||
add(o.getClassName(), t.getClassName());
|
add(o.getClassName(), t.getClassName());
|
||||||
}
|
}
|
||||||
public String getTargetProfile(String target) {
|
public Profile getTargetProfile(String target) {
|
||||||
int i = target.lastIndexOf('.');
|
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();
|
String pkg = loc.getPackageName();
|
||||||
return pkg.isEmpty() ? "<unnamed>" : pkg;
|
return pkg.isEmpty() ? "<unnamed>" : pkg;
|
||||||
}
|
}
|
||||||
public String getTargetProfile(String target) {
|
public Profile getTargetProfile(String target) {
|
||||||
return Profiles.getProfileName(target);
|
return Profile.getProfile(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
package com.sun.tools.jdeps;
|
package com.sun.tools.jdeps;
|
||||||
|
|
||||||
import com.sun.tools.classfile.Dependency.Location;
|
import com.sun.tools.classfile.Dependency.Location;
|
||||||
import java.io.File;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -35,21 +35,20 @@ import java.util.Set;
|
||||||
* Represents the source of the class files.
|
* Represents the source of the class files.
|
||||||
*/
|
*/
|
||||||
public class Archive {
|
public class Archive {
|
||||||
private final File file;
|
private final Path path;
|
||||||
private final String filename;
|
private final String filename;
|
||||||
private final ClassFileReader reader;
|
private final ClassFileReader reader;
|
||||||
private final Map<Location, Set<Location>> deps
|
private final Map<Location, Set<Location>> deps = new HashMap<>();
|
||||||
= new HashMap<Location, Set<Location>>();
|
|
||||||
|
|
||||||
public Archive(String name) {
|
public Archive(String name) {
|
||||||
this.file = null;
|
this.path = null;
|
||||||
this.filename = name;
|
this.filename = name;
|
||||||
this.reader = null;
|
this.reader = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Archive(File f, ClassFileReader reader) {
|
public Archive(Path p, ClassFileReader reader) {
|
||||||
this.file = f;
|
this.path = p;
|
||||||
this.filename = f.getName();
|
this.filename = path.getFileName().toString();
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,14 +63,14 @@ public class Archive {
|
||||||
public void addClass(Location origin) {
|
public void addClass(Location origin) {
|
||||||
Set<Location> set = deps.get(origin);
|
Set<Location> set = deps.get(origin);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
set = new HashSet<Location>();
|
set = new HashSet<>();
|
||||||
deps.put(origin, set);
|
deps.put(origin, set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void addClass(Location origin, Location target) {
|
public void addClass(Location origin, Location target) {
|
||||||
Set<Location> set = deps.get(origin);
|
Set<Location> set = deps.get(origin);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
set = new HashSet<Location>();
|
set = new HashSet<>();
|
||||||
deps.put(origin, set);
|
deps.put(origin, set);
|
||||||
}
|
}
|
||||||
set.add(target);
|
set.add(target);
|
||||||
|
@ -87,7 +86,7 @@ public class Archive {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return file != null ? file.getPath() : filename;
|
return path != null ? path.toString() : filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Visitor {
|
interface Visitor {
|
||||||
|
|
|
@ -45,17 +45,17 @@ public class ClassFileReader {
|
||||||
/**
|
/**
|
||||||
* Returns a ClassFileReader instance of a given path.
|
* Returns a ClassFileReader instance of a given path.
|
||||||
*/
|
*/
|
||||||
public static ClassFileReader newInstance(File path) throws IOException {
|
public static ClassFileReader newInstance(Path path) throws IOException {
|
||||||
if (!path.exists()) {
|
if (!Files.exists(path)) {
|
||||||
throw new FileNotFoundException(path.getAbsolutePath());
|
throw new FileNotFoundException(path.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.isDirectory()) {
|
if (Files.isDirectory(path)) {
|
||||||
return new DirectoryReader(path.toPath());
|
return new DirectoryReader(path);
|
||||||
} else if (path.getName().endsWith(".jar")) {
|
} else if (path.getFileName().toString().endsWith(".jar")) {
|
||||||
return new JarFileReader(path.toPath());
|
return new JarFileReader(path);
|
||||||
} else {
|
} else {
|
||||||
return new ClassFileReader(path.toPath());
|
return new ClassFileReader(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,16 +163,16 @@ public class ClassFileReader {
|
||||||
int i = name.lastIndexOf('.');
|
int i = name.lastIndexOf('.');
|
||||||
String pathname = name.replace('.', File.separatorChar) + ".class";
|
String pathname = name.replace('.', File.separatorChar) + ".class";
|
||||||
Path p = path.resolve(pathname);
|
Path p = path.resolve(pathname);
|
||||||
if (!p.toFile().exists()) {
|
if (!Files.exists(p)) {
|
||||||
p = path.resolve(pathname.substring(0, i) + "$" +
|
p = path.resolve(pathname.substring(0, i) + "$" +
|
||||||
pathname.substring(i+1, pathname.length()));
|
pathname.substring(i+1, pathname.length()));
|
||||||
}
|
}
|
||||||
if (p.toFile().exists()) {
|
if (Files.exists(p)) {
|
||||||
return readClassFile(p);
|
return readClassFile(p);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Path p = path.resolve(name + ".class");
|
Path p = path.resolve(name + ".class");
|
||||||
if (p.toFile().exists()) {
|
if (Files.exists(p)) {
|
||||||
return readClassFile(p);
|
return readClassFile(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ public class ClassFileReader {
|
||||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (file.toFile().getName().endsWith(".class")) {
|
if (file.getFileName().toString().endsWith(".class")) {
|
||||||
files.add(file);
|
files.add(file);
|
||||||
}
|
}
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
|
|
|
@ -24,12 +24,18 @@
|
||||||
*/
|
*/
|
||||||
package com.sun.tools.jdeps;
|
package com.sun.tools.jdeps;
|
||||||
|
|
||||||
|
import com.sun.tools.classfile.AccessFlags;
|
||||||
import com.sun.tools.classfile.ClassFile;
|
import com.sun.tools.classfile.ClassFile;
|
||||||
import com.sun.tools.classfile.ConstantPoolException;
|
import com.sun.tools.classfile.ConstantPoolException;
|
||||||
import com.sun.tools.classfile.Dependencies;
|
import com.sun.tools.classfile.Dependencies;
|
||||||
import com.sun.tools.classfile.Dependencies.ClassFileError;
|
import com.sun.tools.classfile.Dependencies.ClassFileError;
|
||||||
import com.sun.tools.classfile.Dependency;
|
import com.sun.tools.classfile.Dependency;
|
||||||
|
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
|
||||||
import java.io.*;
|
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.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -67,12 +73,11 @@ class JdepsTask {
|
||||||
|
|
||||||
boolean matches(String opt) {
|
boolean matches(String opt) {
|
||||||
for (String a : aliases) {
|
for (String a : aliases) {
|
||||||
if (a.equals(opt)) {
|
if (a.equals(opt))
|
||||||
return true;
|
return true;
|
||||||
} else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
|
if (hasArg && opt.startsWith(a + "="))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,62 +101,96 @@ class JdepsTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Option[] recognizedOptions = {
|
static Option[] recognizedOptions = {
|
||||||
new Option(false, "-h", "-?", "--help") {
|
new Option(false, "-h", "-?", "-help") {
|
||||||
void process(JdepsTask task, String opt, String arg) {
|
void process(JdepsTask task, String opt, String arg) {
|
||||||
task.options.help = true;
|
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) {
|
void process(JdepsTask task, String opt, String arg) {
|
||||||
task.options.showSummary = true;
|
task.options.showSummary = true;
|
||||||
task.options.verbose = Analyzer.Type.SUMMARY;
|
task.options.verbose = Analyzer.Type.SUMMARY;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Option(false, "-v", "--verbose") {
|
new Option(false, "-v", "-verbose",
|
||||||
void process(JdepsTask task, String opt, String arg) {
|
"-verbose:package",
|
||||||
task.options.verbose = Analyzer.Type.VERBOSE;
|
"-verbose:class")
|
||||||
}
|
{
|
||||||
},
|
|
||||||
new Option(true, "-V", "--verbose-level") {
|
|
||||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||||
if ("package".equals(arg)) {
|
switch (opt) {
|
||||||
|
case "-v":
|
||||||
|
case "-verbose":
|
||||||
|
task.options.verbose = Analyzer.Type.VERBOSE;
|
||||||
|
break;
|
||||||
|
case "-verbose:package":
|
||||||
task.options.verbose = Analyzer.Type.PACKAGE;
|
task.options.verbose = Analyzer.Type.PACKAGE;
|
||||||
} else if ("class".equals(arg)) {
|
break;
|
||||||
|
case "-verbose:class":
|
||||||
task.options.verbose = Analyzer.Type.CLASS;
|
task.options.verbose = Analyzer.Type.CLASS;
|
||||||
} else {
|
break;
|
||||||
|
default:
|
||||||
throw new BadArgs("err.invalid.arg.for.option", opt);
|
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) {
|
void process(JdepsTask task, String opt, String arg) {
|
||||||
task.options.classpath = arg;
|
task.options.classpath = arg;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Option(true, "-p", "--package") {
|
new Option(true, "-p", "-package") {
|
||||||
void process(JdepsTask task, String opt, String arg) {
|
void process(JdepsTask task, String opt, String arg) {
|
||||||
task.options.packageNames.add(arg);
|
task.options.packageNames.add(arg);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Option(true, "-e", "--regex") {
|
new Option(true, "-e", "-regex") {
|
||||||
void process(JdepsTask task, String opt, String arg) {
|
void process(JdepsTask task, String opt, String arg) {
|
||||||
task.options.regex = 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 {
|
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||||
task.options.showProfile = true;
|
task.options.showProfile = true;
|
||||||
if (Profiles.getProfileCount() == 0) {
|
if (Profile.getProfileCount() == 0) {
|
||||||
throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg"));
|
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) {
|
void process(JdepsTask task, String opt, String arg) {
|
||||||
task.options.depth = 0;
|
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 {
|
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||||
try {
|
try {
|
||||||
task.options.depth = Integer.parseInt(arg);
|
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";
|
private static final String PROGNAME = "jdeps";
|
||||||
|
@ -202,7 +231,7 @@ class JdepsTask {
|
||||||
if (options.version || options.fullVersion) {
|
if (options.version || options.fullVersion) {
|
||||||
showVersion(options.fullVersion);
|
showVersion(options.fullVersion);
|
||||||
}
|
}
|
||||||
if (classes.isEmpty() && !options.wildcard) {
|
if (classes.isEmpty() && options.includePattern == null) {
|
||||||
if (options.help || options.version || options.fullVersion) {
|
if (options.help || options.version || options.fullVersion) {
|
||||||
return EXIT_OK;
|
return EXIT_OK;
|
||||||
} else {
|
} 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 {
|
private boolean run() throws IOException {
|
||||||
findDependencies();
|
findDependencies();
|
||||||
Analyzer analyzer = new Analyzer(options.verbose);
|
Analyzer analyzer = new Analyzer(options.verbose);
|
||||||
analyzer.run(sourceLocations);
|
analyzer.run(sourceLocations);
|
||||||
if (options.verbose == Analyzer.Type.SUMMARY) {
|
if (options.dotOutputDir != null) {
|
||||||
printSummary(log, analyzer);
|
Path dir = Paths.get(options.dotOutputDir);
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
generateDotFiles(dir, analyzer);
|
||||||
} else {
|
} else {
|
||||||
printDependencies(log, analyzer);
|
printRawOutput(log, analyzer);
|
||||||
}
|
}
|
||||||
return true;
|
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) {
|
private boolean isValidClassName(String name) {
|
||||||
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
|
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -259,27 +320,43 @@ class JdepsTask {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findDependencies() throws IOException {
|
private Dependency.Filter getDependencyFilter() {
|
||||||
Dependency.Finder finder = Dependencies.getClassDependencyFinder();
|
|
||||||
Dependency.Filter filter;
|
|
||||||
if (options.regex != null) {
|
if (options.regex != null) {
|
||||||
filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
|
return Dependencies.getRegexFilter(Pattern.compile(options.regex));
|
||||||
} else if (options.packageNames.size() > 0) {
|
} else if (options.packageNames.size() > 0) {
|
||||||
filter = Dependencies.getPackageFilter(options.packageNames, false);
|
return Dependencies.getPackageFilter(options.packageNames, false);
|
||||||
} else {
|
} else {
|
||||||
filter = new Dependency.Filter() {
|
return new Dependency.Filter() {
|
||||||
|
@Override
|
||||||
public boolean accepts(Dependency dependency) {
|
public boolean accepts(Dependency dependency) {
|
||||||
return !dependency.getOrigin().equals(dependency.getTarget());
|
return !dependency.getOrigin().equals(dependency.getTarget());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Archive> archives = new ArrayList<Archive>();
|
private boolean matches(String classname, AccessFlags flags) {
|
||||||
Deque<String> roots = new LinkedList<String>();
|
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) {
|
for (String s : classes) {
|
||||||
File f = new File(s);
|
Path p = Paths.get(s);
|
||||||
if (f.exists()) {
|
if (Files.exists(p)) {
|
||||||
archives.add(new Archive(f, ClassFileReader.newInstance(f)));
|
archives.add(new Archive(p, ClassFileReader.newInstance(p)));
|
||||||
} else {
|
} else {
|
||||||
if (isValidClassName(s)) {
|
if (isValidClassName(s)) {
|
||||||
roots.add(s);
|
roots.add(s);
|
||||||
|
@ -289,9 +366,8 @@ class JdepsTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup
|
List<Archive> classpaths = new ArrayList<>(); // for class file lookup
|
||||||
if (options.wildcard) {
|
if (options.includePattern != null) {
|
||||||
// include all archives from classpath to the initial list
|
|
||||||
archives.addAll(getClassPathArchives(options.classpath));
|
archives.addAll(getClassPathArchives(options.classpath));
|
||||||
} else {
|
} else {
|
||||||
classpaths.addAll(getClassPathArchives(options.classpath));
|
classpaths.addAll(getClassPathArchives(options.classpath));
|
||||||
|
@ -305,8 +381,8 @@ class JdepsTask {
|
||||||
// Work queue of names of classfiles to be searched.
|
// Work queue of names of classfiles to be searched.
|
||||||
// Entries will be unique, and for classes that do not yet have
|
// Entries will be unique, and for classes that do not yet have
|
||||||
// dependencies in the results map.
|
// dependencies in the results map.
|
||||||
Deque<String> deque = new LinkedList<String>();
|
Deque<String> deque = new LinkedList<>();
|
||||||
Set<String> doneClasses = new HashSet<String>();
|
Set<String> doneClasses = new HashSet<>();
|
||||||
|
|
||||||
// get the immediate dependencies of the input files
|
// get the immediate dependencies of the input files
|
||||||
for (Archive a : archives) {
|
for (Archive a : archives) {
|
||||||
|
@ -318,6 +394,7 @@ class JdepsTask {
|
||||||
throw new ClassFileError(e);
|
throw new ClassFileError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matches(classFileName, cf.access_flags)) {
|
||||||
if (!doneClasses.contains(classFileName)) {
|
if (!doneClasses.contains(classFileName)) {
|
||||||
doneClasses.add(classFileName);
|
doneClasses.add(classFileName);
|
||||||
}
|
}
|
||||||
|
@ -332,6 +409,7 @@ class JdepsTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// add Archive for looking up classes from the classpath
|
// add Archive for looking up classes from the classpath
|
||||||
// for transitive dependency analysis
|
// for transitive dependency analysis
|
||||||
|
@ -379,46 +457,10 @@ class JdepsTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unresolved = deque;
|
unresolved = deque;
|
||||||
deque = new LinkedList<String>();
|
deque = new LinkedList<>();
|
||||||
} while (!unresolved.isEmpty() && depth-- > 0);
|
} 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 {
|
public void handleOptions(String[] args) throws BadArgs {
|
||||||
// process options
|
// process options
|
||||||
for (int i=0; i < args.length; i++) {
|
for (int i=0; i < args.length; i++) {
|
||||||
|
@ -427,7 +469,7 @@ class JdepsTask {
|
||||||
Option option = getOption(name);
|
Option option = getOption(name);
|
||||||
String param = null;
|
String param = null;
|
||||||
if (option.hasArg) {
|
if (option.hasArg) {
|
||||||
if (name.startsWith("--") && name.indexOf('=') > 0) {
|
if (name.startsWith("-") && name.indexOf('=') > 0) {
|
||||||
param = name.substring(name.indexOf('=') + 1, name.length());
|
param = name.substring(name.indexOf('=') + 1, name.length());
|
||||||
} else if (i + 1 < args.length) {
|
} else if (i + 1 < args.length) {
|
||||||
param = args[++i];
|
param = args[++i];
|
||||||
|
@ -447,15 +489,11 @@ class JdepsTask {
|
||||||
if (name.charAt(0) == '-') {
|
if (name.charAt(0) == '-') {
|
||||||
throw new BadArgs("err.option.after.class", name).showUsage(true);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private Option getOption(String name) throws BadArgs {
|
private Option getOption(String name) throws BadArgs {
|
||||||
for (Option o : recognizedOptions) {
|
for (Option o : recognizedOptions) {
|
||||||
|
@ -518,13 +556,15 @@ class JdepsTask {
|
||||||
boolean showProfile;
|
boolean showProfile;
|
||||||
boolean showSummary;
|
boolean showSummary;
|
||||||
boolean wildcard;
|
boolean wildcard;
|
||||||
String regex;
|
boolean apiOnly;
|
||||||
|
String dotOutputDir;
|
||||||
String classpath = "";
|
String classpath = "";
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
Analyzer.Type verbose = Analyzer.Type.PACKAGE;
|
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 {
|
private static class ResourceBundleHelper {
|
||||||
static final ResourceBundle versionRB;
|
static final ResourceBundle versionRB;
|
||||||
static final ResourceBundle bundle;
|
static final ResourceBundle bundle;
|
||||||
|
@ -547,9 +587,9 @@ class JdepsTask {
|
||||||
private List<Archive> getArchives(List<String> filenames) throws IOException {
|
private List<Archive> getArchives(List<String> filenames) throws IOException {
|
||||||
List<Archive> result = new ArrayList<Archive>();
|
List<Archive> result = new ArrayList<Archive>();
|
||||||
for (String s : filenames) {
|
for (String s : filenames) {
|
||||||
File f = new File(s);
|
Path p = Paths.get(s);
|
||||||
if (f.exists()) {
|
if (Files.exists(p)) {
|
||||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
result.add(new Archive(p, ClassFileReader.newInstance(p)));
|
||||||
} else {
|
} else {
|
||||||
warning("warn.file.not.exist", s);
|
warning("warn.file.not.exist", s);
|
||||||
}
|
}
|
||||||
|
@ -558,18 +598,131 @@ class JdepsTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Archive> getClassPathArchives(String paths) throws IOException {
|
private List<Archive> getClassPathArchives(String paths) throws IOException {
|
||||||
List<Archive> result = new ArrayList<Archive>();
|
List<Archive> result = new ArrayList<>();
|
||||||
if (paths.isEmpty()) {
|
if (paths.isEmpty()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
for (String p : paths.split(File.pathSeparator)) {
|
for (String p : paths.split(File.pathSeparator)) {
|
||||||
if (p.length() > 0) {
|
if (p.length() > 0) {
|
||||||
File f = new File(p);
|
List<Path> files = new ArrayList<>();
|
||||||
if (f.exists()) {
|
// 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)));
|
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result;
|
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;
|
package com.sun.tools.jdeps;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.FileVisitResult;
|
import java.nio.file.FileVisitResult;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.SimpleFileVisitor;
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -38,45 +38,38 @@ import java.util.*;
|
||||||
*/
|
*/
|
||||||
class PlatformClassPath {
|
class PlatformClassPath {
|
||||||
private final static List<Archive> javaHomeArchives = init();
|
private final static List<Archive> javaHomeArchives = init();
|
||||||
|
|
||||||
static List<Archive> getArchives() {
|
static List<Archive> getArchives() {
|
||||||
return javaHomeArchives;
|
return javaHomeArchives;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean contains(Archive archive) {
|
|
||||||
return javaHomeArchives.contains(archive);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Archive> init() {
|
private static List<Archive> init() {
|
||||||
List<Archive> result = new ArrayList<Archive>();
|
List<Archive> result = new ArrayList<>();
|
||||||
String javaHome = System.getProperty("java.home");
|
Path home = Paths.get(System.getProperty("java.home"));
|
||||||
File jre = new File(javaHome, "jre");
|
|
||||||
File lib = new File(javaHome, "lib");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (jre.exists() && jre.isDirectory()) {
|
if (home.endsWith("jre")) {
|
||||||
result.addAll(addJarFiles(new File(jre, "lib")));
|
// jar files in <javahome>/jre/lib
|
||||||
result.addAll(addJarFiles(lib));
|
result.addAll(addJarFiles(home.resolve("lib")));
|
||||||
} else if (lib.exists() && lib.isDirectory()) {
|
} else if (Files.exists(home.resolve("lib"))) {
|
||||||
// either a JRE or a jdk build image
|
// either a JRE or a jdk build image
|
||||||
File classes = new File(javaHome, "classes");
|
Path classes = home.resolve("classes");
|
||||||
if (classes.exists() && classes.isDirectory()) {
|
if (Files.isDirectory(classes)) {
|
||||||
// jdk build outputdir
|
// jdk build outputdir
|
||||||
result.add(new Archive(classes, ClassFileReader.newInstance(classes)));
|
result.add(new JDKArchive(classes, ClassFileReader.newInstance(classes)));
|
||||||
}
|
}
|
||||||
// add other JAR files
|
// add other JAR files
|
||||||
result.addAll(addJarFiles(lib));
|
result.addAll(addJarFiles(home.resolve("lib")));
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("\"" + javaHome + "\" not a JDK home");
|
throw new RuntimeException("\"" + home + "\" not a JDK home");
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Archive> addJarFiles(File f) throws IOException {
|
private static List<Archive> addJarFiles(final Path root) throws IOException {
|
||||||
final List<Archive> result = new ArrayList<Archive>();
|
final List<Archive> result = new ArrayList<>();
|
||||||
final Path root = f.toPath();
|
|
||||||
final Path ext = root.resolve("ext");
|
final Path ext = root.resolve("ext");
|
||||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,17 +84,30 @@ class PlatformClassPath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
public FileVisitResult visitFile(Path p, BasicFileAttributes attrs)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
File f = file.toFile();
|
String fn = p.getFileName().toString();
|
||||||
String fn = f.getName();
|
if (fn.endsWith(".jar")) {
|
||||||
if (fn.endsWith(".jar") && !fn.equals("alt-rt.jar")) {
|
// JDK may cobundle with JavaFX that doesn't belong to any profile
|
||||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
// 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 FileVisitResult.CONTINUE;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return result;
|
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=\
|
main.usage=\
|
||||||
Usage: {0} <options> <classes...>\n\
|
Usage: {0} <options> <classes...>\n\
|
||||||
where <classes> can be a pathname to a .class file, a directory, a JAR file,\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:
|
error.prefix=Error:
|
||||||
warn.prefix=Warning:
|
warn.prefix=Warning:
|
||||||
|
|
||||||
main.opt.h=\
|
main.opt.h=\
|
||||||
\ -h -? --help Print this usage message
|
\ -h -? -help Print this usage message
|
||||||
|
|
||||||
main.opt.version=\
|
main.opt.version=\
|
||||||
\ --version Version information
|
\ -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"
|
|
||||||
|
|
||||||
main.opt.v=\
|
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=\
|
main.opt.s=\
|
||||||
\ -s --summary Print dependency summary only
|
\ -s -summary Print dependency summary only
|
||||||
|
|
||||||
main.opt.p=\
|
main.opt.p=\
|
||||||
\ -p <pkg name> --package=<pkg name> Restrict analysis to classes in this package\n\
|
\ -p <pkgname> -package <pkgname> Finds dependences in the given package\n\
|
||||||
\ (may be given multiple times)
|
\ (may be given multiple times)
|
||||||
|
|
||||||
main.opt.e=\
|
main.opt.e=\
|
||||||
\ -e <regex> --regex=<regex> Restrict analysis to packages matching pattern\n\
|
\ -e <regex> -regex <regex> Finds dependences in packages matching pattern\n\
|
||||||
\ (-p and -e are exclusive)
|
\ (-p and -e are exclusive)
|
||||||
|
|
||||||
main.opt.P=\
|
main.opt.include=\
|
||||||
\ -P --profile Show profile or the file containing a package
|
\ -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.c=\
|
main.opt.P=\
|
||||||
\ -c <path> --classpath=<path> Specify where to find class files
|
\ -P -profile Show profile or the file containing a package
|
||||||
|
|
||||||
|
main.opt.cp=\
|
||||||
|
\ -cp <path> -classpath <path> Specify where to find class files
|
||||||
|
|
||||||
main.opt.R=\
|
main.opt.R=\
|
||||||
\ -R --recursive Recursively traverse all dependencies
|
\ -R -recursive Recursively traverse all dependencies
|
||||||
|
|
||||||
main.opt.d=\
|
main.opt.apionly=\
|
||||||
\ -d <depth> --depth=<depth> Specify the depth of the transitive dependency analysis
|
\ -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.unknown.option=unknown option: {0}
|
||||||
err.missing.arg=no value given for {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.after.class=option must be specified before classes: {0}
|
||||||
err.option.unsupported={0} not supported: {1}
|
err.option.unsupported={0} not supported: {1}
|
||||||
err.profiles.msg=No profile information
|
err.profiles.msg=No profile information
|
||||||
|
err.dot.output.path=invalid path: {0}
|
||||||
warn.invalid.arg=Invalid classname or pathname not exist: {0}
|
warn.invalid.arg=Invalid classname or pathname not exist: {0}
|
||||||
warn.split.package=package {0} defined in {1} {2}
|
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
|
* @test
|
||||||
* @bug 8003562 8005428
|
* @bug 8003562 8005428 8015912
|
||||||
* @summary Basic tests for jdeps tool
|
* @summary Basic tests for jdeps tool
|
||||||
* @build Test p.Foo
|
* @build Test p.Foo
|
||||||
* @run main Basic
|
* @run main Basic
|
||||||
|
@ -79,40 +79,33 @@ public class Basic {
|
||||||
new String[] {"compact1", "compact1", "compact3"});
|
new String[] {"compact1", "compact1", "compact3"});
|
||||||
// test class-level dependency output
|
// test class-level dependency output
|
||||||
test(new File(testDir, "Test.class"),
|
test(new File(testDir, "Test.class"),
|
||||||
new String[] {"java.lang.Object", "p.Foo"},
|
new String[] {"java.lang.Object", "java.lang.String", "p.Foo"},
|
||||||
new String[] {"compact1", "not found"},
|
new String[] {"compact1", "compact1", "not found"},
|
||||||
new String[] {"-V", "class"});
|
new String[] {"-verbose:class"});
|
||||||
// test -p option
|
// test -p option
|
||||||
test(new File(testDir, "Test.class"),
|
test(new File(testDir, "Test.class"),
|
||||||
new String[] {"p.Foo"},
|
new String[] {"p.Foo"},
|
||||||
new String[] {"not found"},
|
new String[] {"not found"},
|
||||||
new String[] {"--verbose-level=class", "-p", "p"});
|
new String[] {"-verbose:class", "-p", "p"});
|
||||||
// test -e option
|
// test -e option
|
||||||
test(new File(testDir, "Test.class"),
|
test(new File(testDir, "Test.class"),
|
||||||
new String[] {"p.Foo"},
|
new String[] {"p.Foo"},
|
||||||
new String[] {"not found"},
|
new String[] {"not found"},
|
||||||
new String[] {"-V", "class", "-e", "p\\..*"});
|
new String[] {"-verbose:class", "-e", "p\\..*"});
|
||||||
test(new File(testDir, "Test.class"),
|
test(new File(testDir, "Test.class"),
|
||||||
new String[] {"java.lang"},
|
new String[] {"java.lang"},
|
||||||
new String[] {"compact1"},
|
new String[] {"compact1"},
|
||||||
new String[] {"-V", "package", "-e", "java\\.lang\\..*"});
|
new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
|
||||||
// test -classpath and wildcard options
|
// test -classpath and -include options
|
||||||
test(null,
|
test(null,
|
||||||
new String[] {"com.sun.tools.jdeps", "java.lang", "java.util",
|
new String[] {"java.lang", "java.util",
|
||||||
"java.util.regex", "java.io", "java.nio.file",
|
|
||||||
"java.lang.management"},
|
"java.lang.management"},
|
||||||
new String[] {(symbolFileExist? "not found" : "JDK internal API (classes)"),
|
new String[] {"compact1", "compact1", "compact3"},
|
||||||
"compact1", "compact1", "compact1",
|
new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"});
|
||||||
"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"),
|
test(new File(testDir, "Test.class"),
|
||||||
new String[] {"java.lang.Object", "p.Foo"},
|
new String[] {"java.lang.Object", "java.lang.String", "p.Foo"},
|
||||||
new String[] {"compact1", testDir.getName()},
|
new String[] {"compact1", "compact1", testDir.getName()},
|
||||||
new String[] {"-v", "--classpath", testDir.getPath(), "Test.class"});
|
new String[] {"-v", "-classpath", testDir.getPath(), "Test.class"});
|
||||||
*/
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,4 +25,7 @@ public class Test {
|
||||||
public void test() {
|
public void test() {
|
||||||
p.Foo f = new p.Foo();
|
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…
Add table
Add a link
Reference in a new issue