8003562: Provide a CLI tool to analyze class dependencies
Reviewed-by: jjg, alanb, ulfzibis, erikj
This commit is contained in:
parent
8a40619e4d
commit
e8ce882d43
langtools
make
makefiles
src/share/classes/com/sun/tools
classfile
jdeps
test
@ -153,6 +153,7 @@ javah.tests = \
|
||||
javap.includes = \
|
||||
com/sun/tools/classfile/ \
|
||||
com/sun/tools/javap/ \
|
||||
com/sun/tools/jdeps/ \
|
||||
sun/tools/javap/
|
||||
|
||||
javap.tests = \
|
||||
|
@ -75,6 +75,7 @@ $(LANGTOOLS_OUTPUTDIR)/gensrc/_the_props.d : $(PROPSOURCES) $(BUILD_TOOLS)
|
||||
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javah/resources/version.properties
|
||||
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javap/resources/version.properties
|
||||
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.properties
|
||||
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.properties
|
||||
echo Compiling $(words $(PROPSOURCES) v1 v2 v3) properties into resource bundles
|
||||
$(TOOL_COMPILEPROPS_CMD) $(PROPCMDLINE) \
|
||||
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javah/resources/version.properties \
|
||||
@ -85,6 +86,9 @@ $(LANGTOOLS_OUTPUTDIR)/gensrc/_the_props.d : $(PROPSOURCES) $(BUILD_TOOLS)
|
||||
java.util.ListResourceBundle \
|
||||
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.properties \
|
||||
$(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.java \
|
||||
java.util.ListResourceBundle \
|
||||
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.properties \
|
||||
$(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.java \
|
||||
java.util.ListResourceBundle
|
||||
echo PROPS_ARE_CREATED=yes > $@
|
||||
|
||||
|
@ -141,6 +141,15 @@ public class Dependencies {
|
||||
return new APIDependencyFinder(access);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a finder to do class dependency analysis.
|
||||
*
|
||||
* @return a Class dependency finder
|
||||
*/
|
||||
public static Finder getClassDependencyFinder() {
|
||||
return new ClassDependencyFinder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finder used to locate the dependencies for a class.
|
||||
* @return the finder
|
||||
@ -246,8 +255,6 @@ public class Dependencies {
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find the dependencies of a class, using the current
|
||||
* {@link Dependencies#getFinder finder} and
|
||||
@ -306,38 +313,44 @@ public class Dependencies {
|
||||
* A location identifying a class.
|
||||
*/
|
||||
static class SimpleLocation implements Location {
|
||||
public SimpleLocation(String className) {
|
||||
this.className = className;
|
||||
public SimpleLocation(String name) {
|
||||
this.name = name;
|
||||
this.className = name.replace('/', '.').replace('$', '.');
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the class being depended on. This name will be used to
|
||||
* locate the class file for transitive dependency analysis.
|
||||
* @return the name of the class being depended on
|
||||
*/
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
int i = name.lastIndexOf('/');
|
||||
return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other)
|
||||
return true;
|
||||
if (!(other instanceof SimpleLocation))
|
||||
return false;
|
||||
return (className.equals(((SimpleLocation) other).className));
|
||||
return (name.equals(((SimpleLocation) other).name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return className.hashCode();
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return className;
|
||||
return name;
|
||||
}
|
||||
|
||||
private String name;
|
||||
private String className;
|
||||
}
|
||||
|
||||
@ -431,9 +444,7 @@ public class Dependencies {
|
||||
}
|
||||
|
||||
public boolean accepts(Dependency dependency) {
|
||||
String cn = dependency.getTarget().getClassName();
|
||||
int lastSep = cn.lastIndexOf("/");
|
||||
String pn = (lastSep == -1 ? "" : cn.substring(0, lastSep));
|
||||
String pn = dependency.getTarget().getPackageName();
|
||||
if (packageNames.contains(pn))
|
||||
return true;
|
||||
|
||||
@ -451,8 +462,6 @@ public class Dependencies {
|
||||
private final boolean matchSubpackages;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class identifies class names directly or indirectly in the constant pool.
|
||||
*/
|
||||
@ -462,6 +471,26 @@ public class Dependencies {
|
||||
for (CPInfo cpInfo: classfile.constant_pool.entries()) {
|
||||
v.scan(cpInfo);
|
||||
}
|
||||
try {
|
||||
v.addClass(classfile.super_class);
|
||||
v.addClasses(classfile.interfaces);
|
||||
v.scan(classfile.attributes);
|
||||
|
||||
for (Field f : classfile.fields) {
|
||||
v.scan(f.descriptor, f.attributes);
|
||||
}
|
||||
for (Method m : classfile.methods) {
|
||||
v.scan(m.descriptor, m.attributes);
|
||||
Exceptions_attribute e =
|
||||
(Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
|
||||
if (e != null) {
|
||||
v.addClasses(e.exception_index_table);
|
||||
}
|
||||
}
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
|
||||
return v.deps;
|
||||
}
|
||||
}
|
||||
@ -558,9 +587,7 @@ public class Dependencies {
|
||||
void scan(Descriptor d, Attributes attrs) {
|
||||
try {
|
||||
scan(new Signature(d.index).getType(constant_pool));
|
||||
Signature_attribute sa = (Signature_attribute) attrs.get(Attribute.Signature);
|
||||
if (sa != null)
|
||||
scan(new Signature(sa.signature_index).getType(constant_pool));
|
||||
scan(attrs);
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
@ -574,6 +601,43 @@ public class Dependencies {
|
||||
t.accept(this, null);
|
||||
}
|
||||
|
||||
void scan(Attributes attrs) {
|
||||
try {
|
||||
Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
|
||||
if (sa != null)
|
||||
scan(sa.getParsedSignature().getType(constant_pool));
|
||||
|
||||
scan((RuntimeVisibleAnnotations_attribute)
|
||||
attrs.get(Attribute.RuntimeVisibleAnnotations));
|
||||
scan((RuntimeVisibleParameterAnnotations_attribute)
|
||||
attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
|
||||
if (attr == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < attr.annotations.length; i++) {
|
||||
int index = attr.annotations[i].type_index;
|
||||
scan(new Signature(index).getType(constant_pool));
|
||||
}
|
||||
}
|
||||
|
||||
private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
|
||||
if (attr == null) {
|
||||
return;
|
||||
}
|
||||
for (int param = 0; param < attr.parameter_annotations.length; param++) {
|
||||
for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
|
||||
int index = attr.parameter_annotations[param][i].type_index;
|
||||
scan(new Signature(index).getType(constant_pool));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addClass(int index) throws ConstantPoolException {
|
||||
if (index != 0) {
|
||||
String name = constant_pool.getClassInfo(index).getBaseName();
|
||||
@ -698,6 +762,7 @@ public class Dependencies {
|
||||
findDependencies(type.paramTypes);
|
||||
findDependencies(type.returnType);
|
||||
findDependencies(type.throwsTypes);
|
||||
findDependencies(type.typeParamTypes);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -709,7 +774,7 @@ public class Dependencies {
|
||||
|
||||
public Void visitClassType(ClassType type, Void p) {
|
||||
findDependencies(type.outerType);
|
||||
addDependency(type.name);
|
||||
addDependency(type.getBinaryName());
|
||||
findDependencies(type.typeArgs);
|
||||
return null;
|
||||
}
|
||||
|
@ -71,7 +71,19 @@ public interface Dependency {
|
||||
* dependency analysis.
|
||||
* @return the name of the class containing the location.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the fully-qualified name of the class containing the location.
|
||||
* @return the fully-qualified name of the class containing the location.
|
||||
*/
|
||||
String getClassName();
|
||||
|
||||
/**
|
||||
* Get the package name of the class containing the location.
|
||||
* @return the package name of the class containing the location.
|
||||
*/
|
||||
String getPackageName();
|
||||
}
|
||||
|
||||
|
||||
|
173
langtools/src/share/classes/com/sun/tools/jdeps/Archive.java
Normal file
173
langtools/src/share/classes/com/sun/tools/jdeps/Archive.java
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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. 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.Dependency;
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
import java.io.File;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Represents the source of the class files.
|
||||
*/
|
||||
public class Archive {
|
||||
private static Map<String,Archive> archiveForClass = new HashMap<String,Archive>();
|
||||
public static Archive find(Location loc) {
|
||||
return archiveForClass.get(loc.getName());
|
||||
}
|
||||
|
||||
private final File file;
|
||||
private final String filename;
|
||||
private final DependencyRecorder recorder;
|
||||
private final ClassFileReader reader;
|
||||
public Archive(String name) {
|
||||
this.file = null;
|
||||
this.filename = name;
|
||||
this.recorder = new DependencyRecorder();
|
||||
this.reader = null;
|
||||
}
|
||||
|
||||
public Archive(File f, ClassFileReader reader) {
|
||||
this.file = f;
|
||||
this.filename = f.getName();
|
||||
this.recorder = new DependencyRecorder();
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public ClassFileReader reader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public void addClass(String classFileName) {
|
||||
Archive a = archiveForClass.get(classFileName);
|
||||
assert(a == null || a == this); // ## issue warning?
|
||||
if (!archiveForClass.containsKey(classFileName)) {
|
||||
archiveForClass.put(classFileName, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void addDependency(Dependency d) {
|
||||
recorder.addDependency(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted map of a class to its dependencies.
|
||||
*/
|
||||
public SortedMap<Location, SortedSet<Location>> getDependencies() {
|
||||
DependencyRecorder.Filter filter = new DependencyRecorder.Filter() {
|
||||
public boolean accept(Location origin, Location target) {
|
||||
return (archiveForClass.get(origin.getName()) !=
|
||||
archiveForClass.get(target.getName()));
|
||||
}};
|
||||
|
||||
SortedMap<Location, SortedSet<Location>> result =
|
||||
new TreeMap<Location, SortedSet<Location>>(locationComparator);
|
||||
for (Map.Entry<Location, Set<Location>> e : recorder.dependencies().entrySet()) {
|
||||
Location o = e.getKey();
|
||||
for (Location t : e.getValue()) {
|
||||
if (filter.accept(o, t)) {
|
||||
SortedSet<Location> odeps = result.get(o);
|
||||
if (odeps == null) {
|
||||
odeps = new TreeSet<Location>(locationComparator);
|
||||
result.put(o, odeps);
|
||||
}
|
||||
odeps.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of archives this archive requires.
|
||||
*/
|
||||
public Set<Archive> getRequiredArchives() {
|
||||
SortedSet<Archive> deps = new TreeSet<Archive>(new Comparator<Archive>() {
|
||||
public int compare(Archive a1, Archive a2) {
|
||||
return a1.toString().compareTo(a2.toString());
|
||||
}
|
||||
});
|
||||
|
||||
for (Map.Entry<Location, Set<Location>> e : recorder.dependencies().entrySet()) {
|
||||
Location o = e.getKey();
|
||||
Archive origin = Archive.find(o);
|
||||
for (Location t : e.getValue()) {
|
||||
Archive target = Archive.find(t);
|
||||
assert(origin != null && target != null);
|
||||
if (origin != target) {
|
||||
if (!deps.contains(target)) {
|
||||
deps.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return file != null ? file.getPath() : filename;
|
||||
}
|
||||
|
||||
private static class DependencyRecorder {
|
||||
static interface Filter {
|
||||
boolean accept(Location origin, Location target);
|
||||
}
|
||||
|
||||
public void addDependency(Dependency d) {
|
||||
Set<Location> odeps = map.get(d.getOrigin());
|
||||
if (odeps == null) {
|
||||
odeps = new HashSet<Location>();
|
||||
map.put(d.getOrigin(), odeps);
|
||||
}
|
||||
odeps.add(d.getTarget());
|
||||
}
|
||||
|
||||
public Map<Location, Set<Location>> dependencies() {
|
||||
return map;
|
||||
}
|
||||
|
||||
private final Map<Location, Set<Location>> map =
|
||||
new HashMap<Location, Set<Location>>();
|
||||
}
|
||||
|
||||
private static Comparator<Location> locationComparator =
|
||||
new Comparator<Location>() {
|
||||
public int compare(Location o1, Location o2) {
|
||||
return o1.toString().compareTo(o2.toString());
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* 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. 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.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.Dependencies.ClassFileError;
|
||||
import java.io.*;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* ClassFileReader reads ClassFile(s) of a given path that can be
|
||||
* a .class file, a directory, or a JAR file.
|
||||
*/
|
||||
public class ClassFileReader {
|
||||
/**
|
||||
* Returns a ClassFileReader instance of a given path.
|
||||
*/
|
||||
public static ClassFileReader newInstance(File path) throws IOException {
|
||||
if (!path.exists()) {
|
||||
throw new FileNotFoundException(path.getAbsolutePath());
|
||||
}
|
||||
|
||||
if (path.isDirectory()) {
|
||||
return new DirectoryReader(path.toPath());
|
||||
} else if (path.getName().endsWith(".jar")) {
|
||||
return new JarFileReader(path.toPath());
|
||||
} else {
|
||||
return new ClassFileReader(path.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
protected final Path path;
|
||||
protected final String baseFileName;
|
||||
private ClassFileReader(Path path) {
|
||||
this.path = path;
|
||||
this.baseFileName = path.getFileName() != null
|
||||
? path.getFileName().toString()
|
||||
: path.toString();
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return baseFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ClassFile matching the given binary name
|
||||
* or a fully-qualified class name.
|
||||
*/
|
||||
public ClassFile getClassFile(String name) throws IOException {
|
||||
if (name.indexOf('.') > 0) {
|
||||
int i = name.lastIndexOf('.');
|
||||
String pathname = name.replace('.', File.separatorChar) + ".class";
|
||||
if (baseFileName.equals(pathname) ||
|
||||
baseFileName.equals(pathname.substring(0, i) + "$" +
|
||||
pathname.substring(i+1, pathname.length()))) {
|
||||
return readClassFile(path);
|
||||
}
|
||||
} else {
|
||||
if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
|
||||
return readClassFile(path);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Iterable<ClassFile> getClassFiles() throws IOException {
|
||||
return new Iterable<ClassFile>() {
|
||||
public Iterator<ClassFile> iterator() {
|
||||
return new FileIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ClassFile readClassFile(Path p) throws IOException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = Files.newInputStream(p);
|
||||
return ClassFile.read(is);
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new ClassFileError(e);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FileIterator implements Iterator<ClassFile> {
|
||||
int count;
|
||||
FileIterator() {
|
||||
this.count = 0;
|
||||
}
|
||||
public boolean hasNext() {
|
||||
return count == 0 && baseFileName.endsWith(".class");
|
||||
}
|
||||
|
||||
public ClassFile next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
try {
|
||||
ClassFile cf = readClassFile(path);
|
||||
count++;
|
||||
return cf;
|
||||
} catch (IOException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
private static class DirectoryReader extends ClassFileReader {
|
||||
DirectoryReader(Path path) throws IOException {
|
||||
super(path);
|
||||
}
|
||||
|
||||
public ClassFile getClassFile(String name) throws IOException {
|
||||
if (name.indexOf('.') > 0) {
|
||||
int i = name.lastIndexOf('.');
|
||||
String pathname = name.replace('.', File.separatorChar) + ".class";
|
||||
Path p = path.resolve(pathname);
|
||||
if (!p.toFile().exists()) {
|
||||
p = path.resolve(pathname.substring(0, i) + "$" +
|
||||
pathname.substring(i+1, pathname.length()));
|
||||
}
|
||||
if (p.toFile().exists()) {
|
||||
return readClassFile(p);
|
||||
}
|
||||
} else {
|
||||
Path p = path.resolve(name + ".class");
|
||||
if (p.toFile().exists()) {
|
||||
return readClassFile(p);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Iterable<ClassFile> getClassFiles() throws IOException {
|
||||
final Iterator<ClassFile> iter = new DirectoryIterator();
|
||||
return new Iterable<ClassFile>() {
|
||||
public Iterator<ClassFile> iterator() {
|
||||
return iter;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private List<Path> walkTree(Path dir) throws IOException {
|
||||
final List<Path> files = new ArrayList<Path>();
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
if (file.toFile().getName().endsWith(".class")) {
|
||||
files.add(file);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
class DirectoryIterator implements Iterator<ClassFile> {
|
||||
private List<Path> entries;
|
||||
private int index = 0;
|
||||
DirectoryIterator() throws IOException {
|
||||
entries = walkTree(path);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index != entries.size();
|
||||
}
|
||||
|
||||
public ClassFile next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
Path path = entries.get(index++);
|
||||
try {
|
||||
return readClassFile(path);
|
||||
} catch (IOException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class JarFileReader extends ClassFileReader {
|
||||
final JarFile jarfile;
|
||||
JarFileReader(Path path) throws IOException {
|
||||
super(path);
|
||||
this.jarfile = new JarFile(path.toFile());
|
||||
}
|
||||
|
||||
public ClassFile getClassFile(String name) throws IOException {
|
||||
if (name.indexOf('.') > 0) {
|
||||
int i = name.lastIndexOf('.');
|
||||
String entryName = name.replace('.', '/') + ".class";
|
||||
JarEntry e = jarfile.getJarEntry(entryName);
|
||||
if (e == null) {
|
||||
e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
|
||||
+ entryName.substring(i + 1, entryName.length()));
|
||||
}
|
||||
if (e != null) {
|
||||
return readClassFile(e);
|
||||
}
|
||||
} else {
|
||||
JarEntry e = jarfile.getJarEntry(name + ".class");
|
||||
if (e != null) {
|
||||
return readClassFile(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ClassFile readClassFile(JarEntry e) throws IOException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = jarfile.getInputStream(e);
|
||||
return ClassFile.read(is);
|
||||
} catch (ConstantPoolException ex) {
|
||||
throw new ClassFileError(ex);
|
||||
} finally {
|
||||
if (is != null)
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
public Iterable<ClassFile> getClassFiles() throws IOException {
|
||||
final Iterator<ClassFile> iter = new JarFileIterator();
|
||||
return new Iterable<ClassFile>() {
|
||||
public Iterator<ClassFile> iterator() {
|
||||
return iter;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class JarFileIterator implements Iterator<ClassFile> {
|
||||
private Enumeration<JarEntry> entries;
|
||||
private JarEntry nextEntry;
|
||||
JarFileIterator() {
|
||||
this.entries = jarfile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry e = entries.nextElement();
|
||||
String name = e.getName();
|
||||
if (name.endsWith(".class")) {
|
||||
this.nextEntry = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return nextEntry != null;
|
||||
}
|
||||
|
||||
public ClassFile next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
ClassFile cf;
|
||||
try {
|
||||
cf = readClassFile(nextEntry);
|
||||
} catch (IOException ex) {
|
||||
throw new ClassFileError(ex);
|
||||
}
|
||||
JarEntry entry = nextEntry;
|
||||
nextEntry = null;
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry e = entries.nextElement();
|
||||
String name = e.getName();
|
||||
if (name.endsWith(".class")) {
|
||||
nextEntry = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
650
langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
Normal file
650
langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
* 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. 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.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.Dependencies;
|
||||
import com.sun.tools.classfile.Dependencies.ClassFileError;
|
||||
import com.sun.tools.classfile.Dependency;
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
import java.io.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Implementation for the jdeps tool for static class dependency analysis.
|
||||
*/
|
||||
class JdepsTask {
|
||||
class BadArgs extends Exception {
|
||||
static final long serialVersionUID = 8765093759964640721L;
|
||||
BadArgs(String key, Object... args) {
|
||||
super(JdepsTask.this.getMessage(key, args));
|
||||
this.key = key;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
BadArgs showUsage(boolean b) {
|
||||
showUsage = b;
|
||||
return this;
|
||||
}
|
||||
final String key;
|
||||
final Object[] args;
|
||||
boolean showUsage;
|
||||
}
|
||||
|
||||
static abstract class Option {
|
||||
Option(boolean hasArg, String... aliases) {
|
||||
this.hasArg = hasArg;
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
||||
boolean isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean matches(String opt) {
|
||||
for (String a : aliases) {
|
||||
if (a.equals(opt)) {
|
||||
return true;
|
||||
} else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ignoreRest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract void process(JdepsTask task, String opt, String arg) throws BadArgs;
|
||||
final boolean hasArg;
|
||||
final String[] aliases;
|
||||
}
|
||||
|
||||
static abstract class HiddenOption extends Option {
|
||||
HiddenOption(boolean hasArg, String... aliases) {
|
||||
super(hasArg, aliases);
|
||||
}
|
||||
|
||||
boolean isHidden() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static Option[] recognizedOptions = {
|
||||
new Option(false, "-h", "-?", "--help") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.help = true;
|
||||
}
|
||||
},
|
||||
new Option(false, "-s", "--summary") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.showSummary = true;
|
||||
task.options.verbose = Options.Verbose.SUMMARY;
|
||||
}
|
||||
},
|
||||
new Option(false, "-v", "--verbose") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.verbose = Options.Verbose.VERBOSE;
|
||||
}
|
||||
},
|
||||
new Option(true, "-V", "--verbose-level") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
switch (arg) {
|
||||
case "package":
|
||||
task.options.verbose = Options.Verbose.PACKAGE;
|
||||
break;
|
||||
case "class":
|
||||
task.options.verbose = Options.Verbose.CLASS;
|
||||
break;
|
||||
default:
|
||||
throw task.new BadArgs("err.invalid.arg.for.option", opt);
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(true, "-c", "--classpath") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.classpath = arg;
|
||||
}
|
||||
},
|
||||
new Option(true, "-p", "--package") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.packageNames.add(arg);
|
||||
}
|
||||
},
|
||||
new Option(true, "-e", "--regex") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.regex = arg;
|
||||
}
|
||||
},
|
||||
new Option(false, "-P", "--profile") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.showProfile = true;
|
||||
}
|
||||
},
|
||||
new Option(false, "-R", "--recursive") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.depth = 0;
|
||||
}
|
||||
},
|
||||
new HiddenOption(true, "-d", "--depth") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
try {
|
||||
task.options.depth = Integer.parseInt(arg);
|
||||
} catch (NumberFormatException e) {
|
||||
throw task.new BadArgs("err.invalid.arg.for.option", opt);
|
||||
}
|
||||
}
|
||||
},
|
||||
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 final Options options = new Options();
|
||||
private final List<String> classes = new ArrayList<String>();
|
||||
|
||||
private PrintWriter log;
|
||||
void setLog(PrintWriter out) {
|
||||
log = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result codes.
|
||||
*/
|
||||
static final int EXIT_OK = 0, // Completed with no errors.
|
||||
EXIT_ERROR = 1, // Completed but reported errors.
|
||||
EXIT_CMDERR = 2, // Bad command-line arguments
|
||||
EXIT_SYSERR = 3, // System error or resource exhaustion.
|
||||
EXIT_ABNORMAL = 4;// terminated abnormally
|
||||
|
||||
int run(String[] args) {
|
||||
if (log == null) {
|
||||
log = new PrintWriter(System.out);
|
||||
}
|
||||
try {
|
||||
handleOptions(args);
|
||||
if (options.help) {
|
||||
showHelp();
|
||||
}
|
||||
if (options.version || options.fullVersion) {
|
||||
showVersion(options.fullVersion);
|
||||
}
|
||||
if (classes.isEmpty() && !options.wildcard) {
|
||||
if (options.help || options.version || options.fullVersion) {
|
||||
return EXIT_OK;
|
||||
} else {
|
||||
showHelp();
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
}
|
||||
if (options.regex != null && options.packageNames.size() > 0) {
|
||||
showHelp();
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
if (options.showSummary && options.verbose != Options.Verbose.SUMMARY) {
|
||||
showHelp();
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
boolean ok = run();
|
||||
return ok ? EXIT_OK : EXIT_ERROR;
|
||||
} catch (BadArgs e) {
|
||||
reportError(e.key, e.args);
|
||||
if (e.showUsage) {
|
||||
log.println(getMessage("main.usage.summary", PROGNAME));
|
||||
}
|
||||
return EXIT_CMDERR;
|
||||
} catch (IOException e) {
|
||||
return EXIT_ABNORMAL;
|
||||
} finally {
|
||||
log.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Archive> sourceLocations = new ArrayList<Archive>();
|
||||
private final Archive NOT_FOUND = new Archive(getMessage("artifact.not.found"));
|
||||
private boolean run() throws IOException {
|
||||
findDependencies();
|
||||
switch (options.verbose) {
|
||||
case VERBOSE:
|
||||
case CLASS:
|
||||
printClassDeps(log);
|
||||
break;
|
||||
case PACKAGE:
|
||||
printPackageDeps(log);
|
||||
break;
|
||||
case SUMMARY:
|
||||
for (Archive origin : sourceLocations) {
|
||||
for (Archive target : origin.getRequiredArchives()) {
|
||||
log.format("%-30s -> %s%n", origin, target);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InternalError("Should not reach here");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidClassName(String name) {
|
||||
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i=1; i < name.length(); i++) {
|
||||
char c = name.charAt(i);
|
||||
if (c != '.' && !Character.isJavaIdentifierPart(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void findDependencies() throws IOException {
|
||||
Dependency.Finder finder = Dependencies.getClassDependencyFinder();
|
||||
Dependency.Filter filter;
|
||||
if (options.regex != null) {
|
||||
filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
|
||||
} else if (options.packageNames.size() > 0) {
|
||||
filter = Dependencies.getPackageFilter(options.packageNames, false);
|
||||
} else {
|
||||
filter = new Dependency.Filter() {
|
||||
public boolean accepts(Dependency dependency) {
|
||||
return !dependency.getOrigin().equals(dependency.getTarget());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
List<Archive> archives = new ArrayList<Archive>();
|
||||
Deque<String> roots = new LinkedList<String>();
|
||||
for (String s : classes) {
|
||||
File f = new File(s);
|
||||
if (f.exists()) {
|
||||
archives.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
} else {
|
||||
if (isValidClassName(s)) {
|
||||
roots.add(s);
|
||||
} else {
|
||||
warning("warn.invalid.arg", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup
|
||||
if (options.wildcard) {
|
||||
// include all archives from classpath to the initial list
|
||||
archives.addAll(getClassPathArchives(options.classpath));
|
||||
} else {
|
||||
classpaths.addAll(getClassPathArchives(options.classpath));
|
||||
}
|
||||
classpaths.addAll(PlatformClassPath.getArchives());
|
||||
|
||||
// add all archives to the source locations for reporting
|
||||
sourceLocations.addAll(archives);
|
||||
sourceLocations.addAll(classpaths);
|
||||
|
||||
// Work queue of names of classfiles to be searched.
|
||||
// Entries will be unique, and for classes that do not yet have
|
||||
// dependencies in the results map.
|
||||
Deque<String> deque = new LinkedList<String>();
|
||||
Set<String> doneClasses = new HashSet<String>();
|
||||
|
||||
// get the immediate dependencies of the input files
|
||||
for (Archive a : archives) {
|
||||
for (ClassFile cf : a.reader().getClassFiles()) {
|
||||
String classFileName;
|
||||
try {
|
||||
classFileName = cf.getName();
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
a.addClass(classFileName);
|
||||
if (!doneClasses.contains(classFileName)) {
|
||||
doneClasses.add(classFileName);
|
||||
}
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d)) {
|
||||
String cn = d.getTarget().getName();
|
||||
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
|
||||
deque.add(cn);
|
||||
}
|
||||
a.addDependency(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add Archive for looking up classes from the classpath
|
||||
// for transitive dependency analysis
|
||||
Deque<String> unresolved = roots;
|
||||
int depth = options.depth > 0 ? options.depth : Integer.MAX_VALUE;
|
||||
do {
|
||||
String name;
|
||||
while ((name = unresolved.poll()) != null) {
|
||||
if (doneClasses.contains(name)) {
|
||||
continue;
|
||||
}
|
||||
ClassFile cf = null;
|
||||
for (Archive a : classpaths) {
|
||||
cf = a.reader().getClassFile(name);
|
||||
if (cf != null) {
|
||||
String classFileName;
|
||||
try {
|
||||
classFileName = cf.getName();
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new ClassFileError(e);
|
||||
}
|
||||
a.addClass(classFileName);
|
||||
if (!doneClasses.contains(classFileName)) {
|
||||
// if name is a fully-qualified class name specified
|
||||
// from command-line, this class might already be parsed
|
||||
doneClasses.add(classFileName);
|
||||
if (depth > 0) {
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d)) {
|
||||
String cn = d.getTarget().getName();
|
||||
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
|
||||
deque.add(cn);
|
||||
}
|
||||
a.addDependency(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cf == null) {
|
||||
NOT_FOUND.addClass(name);
|
||||
}
|
||||
}
|
||||
unresolved = deque;
|
||||
deque = new LinkedList<String>();
|
||||
} while (!unresolved.isEmpty() && depth-- > 0);
|
||||
}
|
||||
|
||||
private void printPackageDeps(PrintWriter out) {
|
||||
for (Archive source : sourceLocations) {
|
||||
SortedMap<Location, SortedSet<Location>> deps = source.getDependencies();
|
||||
if (deps.isEmpty())
|
||||
continue;
|
||||
|
||||
for (Archive target : source.getRequiredArchives()) {
|
||||
out.format("%s -> %s%n", source, target);
|
||||
}
|
||||
|
||||
Map<String, Archive> pkgs = new TreeMap<String, Archive>();
|
||||
SortedMap<String, Archive> targets = new TreeMap<String, Archive>();
|
||||
String pkg = "";
|
||||
for (Map.Entry<Location, SortedSet<Location>> e : deps.entrySet()) {
|
||||
String cn = e.getKey().getClassName();
|
||||
String p = packageOf(e.getKey());
|
||||
Archive origin = Archive.find(e.getKey());
|
||||
assert origin != null;
|
||||
if (!pkgs.containsKey(p)) {
|
||||
pkgs.put(p, origin);
|
||||
} else if (pkgs.get(p) != origin) {
|
||||
warning("warn.split.package", p, origin, pkgs.get(p));
|
||||
}
|
||||
|
||||
if (!p.equals(pkg)) {
|
||||
printTargets(out, targets);
|
||||
pkg = p;
|
||||
targets.clear();
|
||||
out.format(" %s (%s)%n", p, origin.getFileName());
|
||||
}
|
||||
|
||||
for (Location t : e.getValue()) {
|
||||
p = packageOf(t);
|
||||
Archive target = Archive.find(t);
|
||||
if (!targets.containsKey(p)) {
|
||||
targets.put(p, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
printTargets(out, targets);
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void printTargets(PrintWriter out, Map<String, Archive> targets) {
|
||||
for (Map.Entry<String, Archive> t : targets.entrySet()) {
|
||||
String pn = t.getKey();
|
||||
out.format(" -> %-40s %s%n", pn, getPackageInfo(pn, t.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private String getPackageInfo(String pn, Archive source) {
|
||||
if (PlatformClassPath.contains(source)) {
|
||||
String name = PlatformClassPath.getProfileName(pn);
|
||||
if (name.isEmpty()) {
|
||||
return "JDK internal API (" + source.getFileName() + ")";
|
||||
}
|
||||
return options.showProfile ? name : "";
|
||||
}
|
||||
return source.getFileName();
|
||||
}
|
||||
|
||||
private static String packageOf(Location loc) {
|
||||
String pkg = loc.getPackageName();
|
||||
return pkg.isEmpty() ? "<unnamed>" : pkg;
|
||||
}
|
||||
|
||||
private void printClassDeps(PrintWriter out) {
|
||||
for (Archive source : sourceLocations) {
|
||||
SortedMap<Location, SortedSet<Location>> deps = source.getDependencies();
|
||||
if (deps.isEmpty())
|
||||
continue;
|
||||
|
||||
for (Archive target : source.getRequiredArchives()) {
|
||||
out.format("%s -> %s%n", source, target);
|
||||
}
|
||||
out.format("%s%n", source);
|
||||
for (Map.Entry<Location, SortedSet<Location>> e : deps.entrySet()) {
|
||||
String cn = e.getKey().getClassName();
|
||||
Archive origin = Archive.find(e.getKey());
|
||||
out.format(" %s (%s)%n", cn, origin.getFileName());
|
||||
for (Location t : e.getValue()) {
|
||||
cn = t.getClassName();
|
||||
Archive target = Archive.find(t);
|
||||
out.format(" -> %-60s %s%n", cn, getPackageInfo(t.getPackageName(), target));
|
||||
}
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
public void handleOptions(String[] args) throws BadArgs {
|
||||
// process options
|
||||
for (int i=0; i < args.length; i++) {
|
||||
if (args[i].charAt(0) == '-') {
|
||||
String name = args[i];
|
||||
Option option = getOption(name);
|
||||
String param = null;
|
||||
if (option.hasArg) {
|
||||
if (name.startsWith("--") && name.indexOf('=') > 0) {
|
||||
param = name.substring(name.indexOf('=') + 1, name.length());
|
||||
} else if (i + 1 < args.length) {
|
||||
param = args[++i];
|
||||
}
|
||||
if (param == null || param.isEmpty() || param.charAt(0) == '-') {
|
||||
throw new BadArgs("err.missing.arg", name).showUsage(true);
|
||||
}
|
||||
}
|
||||
option.process(this, name, param);
|
||||
if (option.ignoreRest()) {
|
||||
i = args.length;
|
||||
}
|
||||
} else {
|
||||
// process rest of the input arguments
|
||||
for (; i < args.length; i++) {
|
||||
String name = args[i];
|
||||
if (name.charAt(0) == '-') {
|
||||
throw new BadArgs("err.option.after.class", name).showUsage(true);
|
||||
}
|
||||
if (name.equals("*") || name.equals("\"*\"")) {
|
||||
options.wildcard = true;
|
||||
} else {
|
||||
classes.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Option getOption(String name) throws BadArgs {
|
||||
for (Option o : recognizedOptions) {
|
||||
if (o.matches(name)) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
throw new BadArgs("err.unknown.option", name).showUsage(true);
|
||||
}
|
||||
|
||||
private void reportError(String key, Object... args) {
|
||||
log.println(getMessage("error.prefix") + " " + getMessage(key, args));
|
||||
}
|
||||
|
||||
private void warning(String key, Object... args) {
|
||||
log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
|
||||
}
|
||||
|
||||
private void showHelp() {
|
||||
log.println(getMessage("main.usage", PROGNAME));
|
||||
for (Option o : recognizedOptions) {
|
||||
String name = o.aliases[0].substring(1); // there must always be at least one name
|
||||
name = name.charAt(0) == '-' ? name.substring(1) : name;
|
||||
if (o.isHidden() || name.equals("h")) {
|
||||
continue;
|
||||
}
|
||||
log.println(getMessage("main.opt." + name));
|
||||
}
|
||||
}
|
||||
|
||||
private void showVersion(boolean full) {
|
||||
log.println(version(full ? "full" : "release"));
|
||||
}
|
||||
|
||||
private String version(String key) {
|
||||
// key=version: mm.nn.oo[-milestone]
|
||||
// key=full: mm.mm.oo[-milestone]-build
|
||||
if (ResourceBundleHelper.versionRB == null) {
|
||||
return System.getProperty("java.version");
|
||||
}
|
||||
try {
|
||||
return ResourceBundleHelper.versionRB.getString(key);
|
||||
} catch (MissingResourceException e) {
|
||||
return getMessage("version.unknown", System.getProperty("java.version"));
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage(String key, Object... args) {
|
||||
try {
|
||||
return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
|
||||
} catch (MissingResourceException e) {
|
||||
throw new InternalError("Missing message: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Options {
|
||||
enum Verbose {
|
||||
CLASS,
|
||||
PACKAGE,
|
||||
SUMMARY,
|
||||
VERBOSE
|
||||
};
|
||||
|
||||
boolean help;
|
||||
boolean version;
|
||||
boolean fullVersion;
|
||||
boolean showFlags;
|
||||
boolean showProfile;
|
||||
boolean showSummary;
|
||||
boolean wildcard;
|
||||
String regex;
|
||||
String classpath = "";
|
||||
int depth = 1;
|
||||
Verbose verbose = Verbose.PACKAGE;
|
||||
Set<String> packageNames = new HashSet<String>();
|
||||
}
|
||||
|
||||
private static class ResourceBundleHelper {
|
||||
static final ResourceBundle versionRB;
|
||||
static final ResourceBundle bundle;
|
||||
|
||||
static {
|
||||
Locale locale = Locale.getDefault();
|
||||
try {
|
||||
bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
|
||||
} catch (MissingResourceException e) {
|
||||
throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
|
||||
}
|
||||
try {
|
||||
versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
|
||||
} catch (MissingResourceException e) {
|
||||
throw new InternalError("version.resource.missing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Archive> getArchives(List<String> filenames) throws IOException {
|
||||
List<Archive> result = new ArrayList<Archive>();
|
||||
for (String s : filenames) {
|
||||
File f = new File(s);
|
||||
if (f.exists()) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
} else {
|
||||
warning("warn.file.not.exist", s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Archive> getClassPathArchives(String paths) throws IOException {
|
||||
List<Archive> result = new ArrayList<Archive>();
|
||||
if (paths.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (String p : paths.split(File.pathSeparator)) {
|
||||
if (p.length() > 0) {
|
||||
File f = new File(p);
|
||||
if (f.exists()) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
66
langtools/src/share/classes/com/sun/tools/jdeps/Main.java
Normal file
66
langtools/src/share/classes/com/sun/tools/jdeps/Main.java
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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. 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 java.io.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* Usage:
|
||||
* jdeps [options] files ...
|
||||
* where options include:
|
||||
* -p package-name restrict analysis to classes in this package
|
||||
* (may be given multiple times)
|
||||
* -e regex restrict analysis to packages matching pattern
|
||||
* (-p and -e are exclusive)
|
||||
* -v show class-level dependencies
|
||||
* default: package-level dependencies
|
||||
* -r --recursive transitive dependencies analysis
|
||||
* -classpath paths Classpath to locate class files
|
||||
* -all process all class files in the given classpath
|
||||
*/
|
||||
public class Main {
|
||||
public static void main(String... args) throws Exception {
|
||||
JdepsTask t = new JdepsTask();
|
||||
int rc = t.run(args);
|
||||
System.exit(rc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Entry point that does <i>not</i> call System.exit.
|
||||
*
|
||||
* @param args command line arguments
|
||||
* @param out output stream
|
||||
* @return an exit code. 0 means success, non-zero means an error occurred.
|
||||
*/
|
||||
public static int run(String[] args, PrintWriter out) {
|
||||
JdepsTask t = new JdepsTask();
|
||||
t.setLog(out);
|
||||
return t.run(args);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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. 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 java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* ClassPath for Java SE and JDK
|
||||
*/
|
||||
class PlatformClassPath {
|
||||
/*
|
||||
* Profiles for Java SE
|
||||
*
|
||||
* This is a temporary workaround until a common API is defined for langtools
|
||||
* to determine which profile a given classname belongs to. The list of
|
||||
* packages and profile names are hardcoded in jdk.properties and
|
||||
* split packages are not supported.
|
||||
*/
|
||||
static class Profile {
|
||||
final String name;
|
||||
final Set<String> packages;
|
||||
|
||||
Profile(String name) {
|
||||
this.name = name;
|
||||
this.packages = new HashSet<String>();
|
||||
}
|
||||
}
|
||||
|
||||
private final static String JAVAFX = "javafx";
|
||||
private final static Map<String,Profile> map = getProfilePackages();
|
||||
static String getProfileName(String packageName) {
|
||||
Profile profile = map.get(packageName);
|
||||
if (packageName.startsWith(JAVAFX + ".")) {
|
||||
profile = map.get(JAVAFX);
|
||||
}
|
||||
return profile != null ? profile.name : "";
|
||||
}
|
||||
|
||||
private final static List<Archive> javaHomeArchives = init();
|
||||
static List<Archive> getArchives() {
|
||||
return javaHomeArchives;
|
||||
}
|
||||
|
||||
static boolean contains(Archive archive) {
|
||||
return javaHomeArchives.contains(archive);
|
||||
}
|
||||
|
||||
private static List<Archive> init() {
|
||||
List<Archive> result = new ArrayList<Archive>();
|
||||
String javaHome = System.getProperty("java.home");
|
||||
List<File> files = new ArrayList<File>();
|
||||
File jre = new File(javaHome, "jre");
|
||||
File lib = new File(javaHome, "lib");
|
||||
|
||||
try {
|
||||
if (jre.exists() && jre.isDirectory()) {
|
||||
result.addAll(addJarFiles(new File(jre, "lib")));
|
||||
result.addAll(addJarFiles(lib));
|
||||
} else if (lib.exists() && lib.isDirectory()) {
|
||||
// either a JRE or a jdk build image
|
||||
File classes = new File(javaHome, "classes");
|
||||
if (classes.exists() && classes.isDirectory()) {
|
||||
// jdk build outputdir
|
||||
result.add(new Archive(classes, ClassFileReader.newInstance(classes)));
|
||||
}
|
||||
// add other JAR files
|
||||
result.addAll(addJarFiles(lib));
|
||||
} else {
|
||||
throw new RuntimeException("\"" + javaHome + "\" not a JDK home");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// add a JavaFX profile if there is jfxrt.jar
|
||||
for (Archive archive : result) {
|
||||
if (archive.getFileName().equals("jfxrt.jar")) {
|
||||
map.put(JAVAFX, new Profile("jfxrt.jar"));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Archive> addJarFiles(File f) throws IOException {
|
||||
final List<Archive> result = new ArrayList<Archive>();
|
||||
final Path root = f.toPath();
|
||||
final Path ext = root.resolve("ext");
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
if (dir.equals(root) || dir.equals(ext)) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
// skip other cobundled JAR files
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
File f = file.toFile();
|
||||
String fn = f.getName();
|
||||
if (fn.endsWith(".jar") && !fn.equals("alt-rt.jar")) {
|
||||
result.add(new Archive(f, ClassFileReader.newInstance(f)));
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String,Profile> getProfilePackages() {
|
||||
Map<String,Profile> map = new HashMap<String,Profile>();
|
||||
|
||||
// read the properties as a ResourceBundle as the build compiles
|
||||
// the properties file into Java class. Another alternative is
|
||||
// to load it as Properties and fix the build to exclude this file.
|
||||
ResourceBundle profileBundle =
|
||||
ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdk");
|
||||
|
||||
int i=1;
|
||||
String key;
|
||||
while (profileBundle.containsKey((key = "profile." + i + ".name"))) {
|
||||
Profile profile = new Profile(profileBundle.getString(key));
|
||||
String n = profileBundle.getString("profile." + i + ".packages");
|
||||
String[] pkgs = n.split("\\s+");
|
||||
for (String p : pkgs) {
|
||||
if (p.isEmpty()) continue;
|
||||
assert(map.containsKey(p) == false);
|
||||
profile.packages.add(p);
|
||||
map.put(p, profile);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
main.usage.summary=\
|
||||
Usage: {0} <options> <classes...>\n\
|
||||
use -h, -? or --help for a list of possible options
|
||||
|
||||
main.usage=\
|
||||
Usage: {0} <options> <classes...>\n\
|
||||
where <classes> can be a pathname to a .class file, a directory, a JAR file,\n\
|
||||
or a fully-qualified classname or wildcard "*". Possible options include:
|
||||
|
||||
error.prefix=Error:
|
||||
warn.prefix=Warning:
|
||||
|
||||
main.opt.h=\
|
||||
\ -h -? --help Print this usage message
|
||||
|
||||
main.opt.version=\
|
||||
\ --version Version information
|
||||
|
||||
main.opt.V=\
|
||||
\ -V <level> --verbose-level=<level> Print package-level or class-level dependencies\n\
|
||||
\ Valid levels are: "package" and "class"
|
||||
|
||||
main.opt.v=\
|
||||
\ -v --verbose Print additional information
|
||||
|
||||
main.opt.s=\
|
||||
\ -s --summary Print dependency summary only
|
||||
|
||||
main.opt.p=\
|
||||
\ -p <pkg name> --package=<pkg name> Restrict analysis to classes in this package\n\
|
||||
\ (may be given multiple times)
|
||||
|
||||
main.opt.e=\
|
||||
\ -e <regex> --regex=<regex> Restrict analysis to packages matching pattern\n\
|
||||
\ (-p and -e are exclusive)
|
||||
|
||||
main.opt.P=\
|
||||
\ -P --profile Show profile or the file containing a package
|
||||
|
||||
main.opt.c=\
|
||||
\ -c <path> --classpath=<path> Specify where to find class files
|
||||
|
||||
main.opt.R=\
|
||||
\ -R --recursive Recursively traverse all dependencies
|
||||
|
||||
main.opt.d=\
|
||||
\ -d <depth> --depth=<depth> Specify the depth of the transitive dependency analysis
|
||||
|
||||
err.unknown.option=unknown option: {0}
|
||||
err.missing.arg=no value given for {0}
|
||||
err.internal.error=internal error: {0} {1} {2}
|
||||
err.invalid.arg.for.option=invalid argument for option: {0}
|
||||
err.option.after.class=option must be specified before classes: {0}
|
||||
warn.invalid.arg=Invalid classname or pathname not exist: {0}
|
||||
warn.split.package=package {0} defined in {1} {2}
|
||||
|
||||
artifact.not.found=not found
|
@ -0,0 +1,262 @@
|
||||
# This properties file does not need localization.
|
||||
|
||||
profile.1.name = compact1
|
||||
profile.1.packages = \
|
||||
java.io \
|
||||
java.lang \
|
||||
java.lang.annotation \
|
||||
java.lang.invoke \
|
||||
java.lang.ref \
|
||||
java.lang.reflect \
|
||||
java.math \
|
||||
java.net \
|
||||
java.nio \
|
||||
java.nio.channels \
|
||||
java.nio.channels.spi \
|
||||
java.nio.charset \
|
||||
java.nio.charset.spi \
|
||||
java.nio.file \
|
||||
java.nio.file.attribute \
|
||||
java.nio.file.spi \
|
||||
java.security \
|
||||
java.security.cert \
|
||||
java.security.interfaces \
|
||||
java.security.spec \
|
||||
java.text \
|
||||
java.text.spi \
|
||||
java.util \
|
||||
java.util.concurrent \
|
||||
java.util.concurrent.atomic \
|
||||
java.util.concurrent.locks \
|
||||
java.util.jar \
|
||||
java.util.logging \
|
||||
java.util.regex \
|
||||
java.util.spi \
|
||||
java.util.zip \
|
||||
javax.crypto \
|
||||
javax.crypto.interfaces \
|
||||
javax.crypto.spec \
|
||||
javax.security.auth \
|
||||
javax.security.auth.callback \
|
||||
javax.security.auth.login \
|
||||
javax.security.auth.spi \
|
||||
javax.security.auth.x500 \
|
||||
javax.net \
|
||||
javax.net.ssl \
|
||||
javax.security.cert \
|
||||
\
|
||||
com.sun.net.ssl \
|
||||
com.sun.nio.file \
|
||||
com.sun.nio.sctp \
|
||||
com.sun.security.auth \
|
||||
com.sun.security.auth.login
|
||||
|
||||
profile.2.name = compact2
|
||||
profile.2.packages = \
|
||||
java.sql \
|
||||
javax.sql \
|
||||
javax.xml \
|
||||
javax.xml.datatype \
|
||||
javax.xml.namespace \
|
||||
javax.xml.parsers \
|
||||
javax.xml.stream \
|
||||
javax.xml.stream.events \
|
||||
javax.xml.stream.util \
|
||||
javax.xml.transform \
|
||||
javax.xml.transform.dom \
|
||||
javax.xml.transform.sax \
|
||||
javax.xml.transform.stax \
|
||||
javax.xml.transform.stream \
|
||||
javax.xml.validation \
|
||||
javax.xml.xpath \
|
||||
org.w3c.dom \
|
||||
org.w3c.dom.bootstrap \
|
||||
org.w3c.dom.events \
|
||||
org.w3c.dom.ls \
|
||||
org.xml.sax \
|
||||
org.xml.sax.ext \
|
||||
org.xml.sax.helpers \
|
||||
java.rmi \
|
||||
java.rmi.activation \
|
||||
java.rmi.dgc \
|
||||
java.rmi.registry \
|
||||
java.rmi.server \
|
||||
javax.rmi.ssl \
|
||||
javax.transaction \
|
||||
javax.transaction.xa \
|
||||
\
|
||||
com.sun.net.httpserver \
|
||||
com.sun.net.httpserver.spi
|
||||
|
||||
profile.3.name = compact3
|
||||
profile.3.packages = \
|
||||
java.lang.instrument \
|
||||
java.lang.management \
|
||||
java.security.acl \
|
||||
java.util.prefs \
|
||||
javax.management \
|
||||
javax.management.loading \
|
||||
javax.management.modelmbean \
|
||||
javax.management.monitor \
|
||||
javax.management.openmbean \
|
||||
javax.management.relation \
|
||||
javax.management.remote \
|
||||
javax.management.remote.rmi \
|
||||
javax.management.timer \
|
||||
javax.naming \
|
||||
javax.naming.directory \
|
||||
javax.naming.event \
|
||||
javax.naming.ldap \
|
||||
javax.naming.spi \
|
||||
javax.sql.rowset \
|
||||
javax.sql.rowset.serial \
|
||||
javax.sql.rowset.spi \
|
||||
javax.security.auth.kerberos \
|
||||
javax.security.sasl \
|
||||
javax.script \
|
||||
javax.smartcardio \
|
||||
javax.xml.crypto \
|
||||
javax.xml.crypto.dom \
|
||||
javax.xml.crypto.dsig \
|
||||
javax.xml.crypto.dsig.dom \
|
||||
javax.xml.crypto.dsig.keyinfo \
|
||||
javax.xml.crypto.dsig.spec \
|
||||
javax.annotation.processing \
|
||||
javax.lang.model \
|
||||
javax.lang.model.element \
|
||||
javax.lang.model.type \
|
||||
javax.lang.model.util \
|
||||
javax.tools \
|
||||
javax.tools.annotation \
|
||||
org.ietf.jgss \
|
||||
\
|
||||
com.sun.management \
|
||||
com.sun.security.auth.callback \
|
||||
com.sun.security.auth.module \
|
||||
com.sun.security.jgss
|
||||
|
||||
profile.4.name = Full JRE
|
||||
profile.4.packages = \
|
||||
java.applet \
|
||||
java.awt \
|
||||
java.awt.color \
|
||||
java.awt.datatransfer \
|
||||
java.awt.dnd \
|
||||
java.awt.dnd.peer \
|
||||
java.awt.event \
|
||||
java.awt.font \
|
||||
java.awt.geom \
|
||||
java.awt.im \
|
||||
java.awt.im.spi \
|
||||
java.awt.image \
|
||||
java.awt.image.renderable \
|
||||
java.awt.peer \
|
||||
java.awt.print \
|
||||
java.beans \
|
||||
java.beans.beancontext \
|
||||
javax.accessibility \
|
||||
javax.imageio \
|
||||
javax.imageio.event \
|
||||
javax.imageio.metadata \
|
||||
javax.imageio.plugins.bmp \
|
||||
javax.imageio.plugins.jpeg \
|
||||
javax.imageio.spi \
|
||||
javax.imageio.stream \
|
||||
javax.print \
|
||||
javax.print.attribute \
|
||||
javax.print.attribute.standard \
|
||||
javax.print.event \
|
||||
javax.sound.midi \
|
||||
javax.sound.midi.spi \
|
||||
javax.sound.sampled \
|
||||
javax.sound.sampled.spi \
|
||||
javax.swing \
|
||||
javax.swing.border \
|
||||
javax.swing.colorchooser \
|
||||
javax.swing.event \
|
||||
javax.swing.filechooser \
|
||||
javax.swing.plaf \
|
||||
javax.swing.plaf.basic \
|
||||
javax.swing.plaf.metal \
|
||||
javax.swing.plaf.multi \
|
||||
javax.swing.plaf.nimbus \
|
||||
javax.swing.plaf.synth \
|
||||
javax.swing.table \
|
||||
javax.swing.text \
|
||||
javax.swing.text.html \
|
||||
javax.swing.text.html.parser \
|
||||
javax.swing.text.rtf \
|
||||
javax.swing.tree \
|
||||
javax.swing.undo \
|
||||
javax.activation \
|
||||
javax.jws \
|
||||
javax.jws.soap \
|
||||
javax.rmi \
|
||||
javax.rmi.CORBA \
|
||||
javax.xml.bind \
|
||||
javax.xml.bind.annotation \
|
||||
javax.xml.bind.annotation.adapters \
|
||||
javax.xml.bind.attachment \
|
||||
javax.xml.bind.helpers \
|
||||
javax.xml.bind.util \
|
||||
javax.xml.soap \
|
||||
javax.xml.ws \
|
||||
javax.xml.ws.handler \
|
||||
javax.xml.ws.handler.soap \
|
||||
javax.xml.ws.http \
|
||||
javax.xml.ws.soap \
|
||||
javax.xml.ws.spi \
|
||||
javax.xml.ws.spi.http \
|
||||
javax.xml.ws.wsaddressing \
|
||||
javax.annotation \
|
||||
org.omg.CORBA \
|
||||
org.omg.CORBA.DynAnyPackage \
|
||||
org.omg.CORBA.ORBPackage \
|
||||
org.omg.CORBA.TypeCodePackage \
|
||||
org.omg.CORBA.portable \
|
||||
org.omg.CORBA_2_3 \
|
||||
org.omg.CORBA_2_3.portable \
|
||||
org.omg.CosNaming \
|
||||
org.omg.CosNaming.NamingContextExtPackage \
|
||||
org.omg.CosNaming.NamingContextPackage \
|
||||
org.omg.Dynamic \
|
||||
org.omg.DynamicAny \
|
||||
org.omg.DynamicAny.DynAnyFactoryPackage \
|
||||
org.omg.DynamicAny.DynAnyPackage \
|
||||
org.omg.IOP \
|
||||
org.omg.IOP.CodecFactoryPackage \
|
||||
org.omg.IOP.CodecPackage \
|
||||
org.omg.Messaging \
|
||||
org.omg.PortableInterceptor \
|
||||
org.omg.PortableInterceptor.ORBInitInfoPackage \
|
||||
org.omg.PortableServer \
|
||||
org.omg.PortableServer.CurrentPackage \
|
||||
org.omg.PortableServer.POAManagerPackage \
|
||||
org.omg.PortableServer.POAPackage \
|
||||
org.omg.PortableServer.ServantLocatorPackage \
|
||||
org.omg.PortableServer.portable \
|
||||
org.omg.SendingContext \
|
||||
org.omg.stub.java.rmi \
|
||||
org.omg.stub.javax.management.remote.rmi
|
||||
|
||||
# Remaining JDK supported API
|
||||
profile.5.name = JDK tools
|
||||
profile.5.packages = \
|
||||
com.sun.jdi \
|
||||
com.sun.jdi.connect \
|
||||
com.sun.jdi.connect.spi \
|
||||
com.sun.jdi.event \
|
||||
com.sun.jdi.request \
|
||||
com.sun.javadoc \
|
||||
com.sun.tools.doclets \
|
||||
com.sun.tools.doctree \
|
||||
com.sun.source.tree \
|
||||
com.sun.source.util \
|
||||
com.sun.tools.attach \
|
||||
com.sun.tools.attach.spi \
|
||||
com.sun.tools.jconsole \
|
||||
com.sun.tools.javac \
|
||||
com.sun.tools.javah \
|
||||
com.sun.tools.javap \
|
||||
com.sun.tools.javadoc \
|
||||
com.sun.servicetag
|
28
langtools/src/share/classes/com/sun/tools/jdeps/resources/version.properties-template
Normal file
28
langtools/src/share/classes/com/sun/tools/jdeps/resources/version.properties-template
Normal file
@ -0,0 +1,28 @@
|
||||
#
|
||||
# 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. 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.
|
||||
#
|
||||
|
||||
jdk=$(JDK_VERSION)
|
||||
full=$(FULL_VERSION)
|
||||
release=$(RELEASE)
|
@ -229,7 +229,7 @@ JCK_RUNTIME_OUTPUT_DIR = $(ABS_TEST_OUTPUT_DIR)/jck-runtime-Xcompile
|
||||
all: $(JPRT_CLEAN) jtreg-tests jck-compiler-tests jck-runtime-tests $(JPRT_ARCHIVE_BUNDLE) all-summary
|
||||
@echo "Testing completed successfully"
|
||||
|
||||
jtreg apt javac javadoc javah javap: $(JPRT_CLEAN) jtreg-tests $(JPRT_ARCHIVE_BUNDLE) jtreg-summary
|
||||
jtreg apt javac javadoc javah javap jdeps: $(JPRT_CLEAN) jtreg-tests $(JPRT_ARCHIVE_BUNDLE) jtreg-summary
|
||||
@echo "Testing completed successfully"
|
||||
|
||||
jck-compiler: $(JPRT_CLEAN) jck-compiler-tests $(JPRT_ARCHIVE_BUNDLE) jck-compiler-summary
|
||||
@ -246,6 +246,7 @@ javac: JTREG_TESTDIRS = tools/javac
|
||||
javadoc: JTREG_TESTDIRS = tools/javadoc com/sun/javadoc
|
||||
javah: JTREG_TESTDIRS = tools/javah
|
||||
javap: JTREG_TESTDIRS = tools/javap
|
||||
jdeps: JTREG_TESTDIRS = tools/jdeps
|
||||
|
||||
# Run jtreg tests
|
||||
#
|
||||
@ -426,7 +427,7 @@ FRC:
|
||||
|
||||
# Phony targets (e.g. these are not filenames)
|
||||
.PHONY: all clean \
|
||||
jtreg javac javadoc javah javap jtreg-tests jtreg-summary check-jtreg \
|
||||
jtreg javac javadoc javah javap jdeps jtreg-tests jtreg-summary check-jtreg \
|
||||
jck-compiler jck-compiler-tests jck-compiler-summary \
|
||||
jck-runtime jck-runtime-tests jck-runtime-summary check-jck
|
||||
|
||||
|
149
langtools/test/tools/jdeps/Basic.java
Normal file
149
langtools/test/tools/jdeps/Basic.java
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 8003562
|
||||
* @summary Basic tests for jdeps tool
|
||||
* @build Test p.Foo
|
||||
* @run main Basic
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class Basic {
|
||||
public static void main(String... args) throws Exception {
|
||||
int errors = 0;
|
||||
|
||||
errors += new Basic().run();
|
||||
if (errors > 0)
|
||||
throw new Exception(errors + " errors found");
|
||||
}
|
||||
|
||||
int run() throws IOException {
|
||||
File testDir = new File(System.getProperty("test.classes", "."));
|
||||
// test a .class file
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang", "p"});
|
||||
// test a directory
|
||||
test(new File(testDir, "p"),
|
||||
new String[] {"java.lang", "java.util"});
|
||||
// test class-level dependency output
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang.Object", "p.Foo"},
|
||||
new String[] {"-V", "class"});
|
||||
// test -p option
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"p.Foo"},
|
||||
new String[] {"--verbose-level=class", "-p", "p"});
|
||||
// test -e option
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"p.Foo"},
|
||||
new String[] {"-V", "class", "-e", "p\\..*"});
|
||||
test(new File(testDir, "Test.class"),
|
||||
new String[] {"java.lang"},
|
||||
new String[] {"-V", "package", "-e", "java\\.lang\\..*"});
|
||||
// test -classpath and -all options
|
||||
test(null,
|
||||
new String[] {"com.sun.tools.jdeps", "java.lang", "java.util",
|
||||
"java.util.regex", "java.io", "p"},
|
||||
new String[] {"--classpath", testDir.getPath(), "*"});
|
||||
return errors;
|
||||
}
|
||||
|
||||
void test(File file, String[] expect) {
|
||||
test(file, expect, new String[0]);
|
||||
}
|
||||
|
||||
void test(File file, String[] expect, String[] options) {
|
||||
String[] args;
|
||||
if (file != null) {
|
||||
args = Arrays.copyOf(options, options.length+1);
|
||||
args[options.length] = file.getPath();
|
||||
} else {
|
||||
args = options;
|
||||
}
|
||||
String[] deps = jdeps(args);
|
||||
checkEqual("dependencies", expect, deps);
|
||||
}
|
||||
|
||||
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 String[] findDeps(String out) {
|
||||
List<String> result = new ArrayList<>();
|
||||
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.add(pm.group(1));
|
||||
if (lm.end() == out.length())
|
||||
break;
|
||||
}
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
||||
void checkEqual(String label, String[] expect, String[] found) {
|
||||
Set<String> s1 = new HashSet<>(Arrays.asList(expect));
|
||||
Set<String> s2 = new HashSet<>(Arrays.asList(found));
|
||||
|
||||
if (!s1.equals(s2))
|
||||
error("Unexpected " + label + " found: '" + s2 + "', expected: '" + s1 + "'");
|
||||
}
|
||||
|
||||
void error(String msg) {
|
||||
System.err.println("Error: " + msg);
|
||||
errors++;
|
||||
}
|
||||
|
||||
int errors;
|
||||
}
|
28
langtools/test/tools/jdeps/Test.java
Normal file
28
langtools/test/tools/jdeps/Test.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
public class Test {
|
||||
public void test() {
|
||||
p.Foo f = new p.Foo();
|
||||
}
|
||||
}
|
35
langtools/test/tools/jdeps/p/Foo.java
Normal file
35
langtools/test/tools/jdeps/p/Foo.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package p;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
public class Foo {
|
||||
public static List foo() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user