--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java Thu May 19 10:55:33 2016 -0700
@@ -25,23 +25,24 @@
package com.sun.tools.jdeps;
-import java.io.PrintStream;
-import java.util.ArrayList;
+import static com.sun.tools.jdeps.JdepsConfiguration.*;
+
+import com.sun.tools.classfile.Dependency.Location;
+import java.io.IOException;
+import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
+import java.util.MissingResourceException;
import java.util.Objects;
+import java.util.Optional;
+import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.sun.tools.classfile.Dependency.Location;
-
/**
* Dependency Analyzer.
*/
@@ -52,6 +53,7 @@
*/
public enum Type {
SUMMARY,
+ MODULE, // equivalent to summary in addition, print module descriptor
PACKAGE,
CLASS,
VERBOSE
@@ -62,9 +64,11 @@
* Only the accepted dependencies are recorded.
*/
interface Filter {
- boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
+ boolean accepts(Location origin, Archive originArchive,
+ Location target, Archive targetArchive);
}
+ protected final JdepsConfiguration configuration;
protected final Type type;
protected final Filter filter;
protected final Map<Archive, Dependences> results = new HashMap<>();
@@ -78,7 +82,8 @@
* @param type Type of the dependency analysis
* @param filter
*/
- public Analyzer(Type type, Filter filter) {
+ Analyzer(JdepsConfiguration config, Type type, Filter filter) {
+ this.configuration = config;
this.type = type;
this.filter = filter;
}
@@ -86,16 +91,10 @@
/**
* Performs the dependency analysis on the given archives.
*/
- public boolean run(Stream<? extends Archive> archives) {
- return run(archives.collect(Collectors.toList()));
- }
-
- /**
- * Performs the dependency analysis on the given archives.
- */
- public boolean run(Iterable<? extends Archive> archives) {
- // build a map from Location to Archive
- buildLocationArchiveMap(archives);
+ boolean run(Iterable<? extends Archive> archives,
+ Map<Location, Archive> locationMap)
+ {
+ this.locationToArchive.putAll(locationMap);
// traverse and analyze all dependencies
for (Archive archive : archives) {
@@ -106,40 +105,50 @@
return true;
}
- protected void buildLocationArchiveMap(Iterable<? extends Archive> archives) {
- // build a map from Location to Archive
- for (Archive archive: archives) {
- archive.getClasses()
- .forEach(l -> locationToArchive.putIfAbsent(l, archive));
- }
+ /**
+ * Returns the analyzed archives
+ */
+ Set<Archive> archives() {
+ return results.keySet();
}
- public boolean hasDependences(Archive archive) {
+ /**
+ * Returns true if the given archive has dependences.
+ */
+ boolean hasDependences(Archive archive) {
if (results.containsKey(archive)) {
return results.get(archive).dependencies().size() > 0;
}
return false;
}
- public Set<String> dependences(Archive source) {
+ /**
+ * Returns the dependences, either class name or package name
+ * as specified in the given verbose level, from the given source.
+ */
+ Set<String> dependences(Archive source) {
if (!results.containsKey(source)) {
return Collections.emptySet();
}
- Dependences result = results.get(source);
- return result.dependencies().stream()
- .map(Dep::target)
- .collect(Collectors.toSet());
+
+ return results.get(source).dependencies()
+ .stream()
+ .map(Dep::target)
+ .collect(Collectors.toSet());
}
- public Stream<Archive> requires(Archive source) {
+ /**
+ * Returns the direct dependences of the given source
+ */
+ Stream<Archive> requires(Archive source) {
if (!results.containsKey(source)) {
return Stream.empty();
}
- Dependences result = results.get(source);
- return result.requires().stream().filter(a -> !a.isEmpty());
+ return results.get(source).requires()
+ .stream();
}
- public interface Visitor {
+ interface Visitor {
/**
* Visits a recorded dependency from origin to target which can be
* a fully-qualified classname, a package name, a module or
@@ -153,7 +162,7 @@
* Visit the dependencies of the given source.
* If the requested level is SUMMARY, it will visit the required archives list.
*/
- public void visitDependences(Archive source, Visitor v, Type level) {
+ void visitDependences(Archive source, Visitor v, Type level) {
if (level == Type.SUMMARY) {
final Dependences result = results.get(source);
final Set<Archive> reqs = result.requires();
@@ -187,7 +196,7 @@
}
}
- public void visitDependences(Archive source, Visitor v) {
+ void visitDependences(Archive source, Visitor v) {
visitDependences(source, v, type);
}
@@ -224,14 +233,28 @@
}
}
+ /*
+ * Returns the archive that contains the given location.
+ */
Archive findArchive(Location t) {
+ // local in this archive
if (archive.getClasses().contains(t))
return archive;
- return locationToArchive.computeIfAbsent(t, _k -> NOT_FOUND);
+ Archive target;
+ if (locationToArchive.containsKey(t)) {
+ target = locationToArchive.get(t);
+ } else {
+ // special case JDK removed API
+ target = configuration.findClass(t)
+ .orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t)
+ ? REMOVED_JDK_INTERNALS
+ : NOT_FOUND);
+ }
+ return locationToArchive.computeIfAbsent(t, _k -> target);
}
- // return classname or package name depedning on the level
+ // return classname or package name depending on the level
private String getLocationName(Location o) {
if (level == Type.CLASS || level == Type.VERBOSE) {
return o.getClassName();
@@ -345,4 +368,66 @@
target, targetArchive.getName());
}
}
+
+ private static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
+
+ private static class JdkInternals extends Module {
+ private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals";
+
+ private final Set<String> jdkinternals;
+ private final Set<String> jdkUnsupportedClasses;
+ private JdkInternals() {
+ super("JDK removed internal API");
+
+ try {
+ ResourceBundle rb = ResourceBundle.getBundle(BUNDLE);
+ this.jdkinternals = rb.keySet();
+ } catch (MissingResourceException e) {
+ throw new InternalError("Cannot find jdkinternals resource bundle");
+ }
+
+ this.jdkUnsupportedClasses = getUnsupportedClasses();
+ }
+
+ public boolean contains(Location location) {
+ if (jdkUnsupportedClasses.contains(location.getName() + ".class")) {
+ return false;
+ }
+
+ String cn = location.getClassName();
+ int i = cn.lastIndexOf('.');
+ String pn = i > 0 ? cn.substring(0, i) : "";
+ return jdkinternals.contains(cn) || jdkinternals.contains(pn);
+ }
+
+ @Override
+ public String name() {
+ return getName();
+ }
+
+ @Override
+ public boolean isExported(String pn) {
+ return false;
+ }
+
+ private Set<String> getUnsupportedClasses() {
+ // jdk.unsupported may not be observable
+ Optional<Module> om = Profile.FULL_JRE.findModule(JDK_UNSUPPORTED);
+ if (om.isPresent()) {
+ return om.get().reader().entries();
+ }
+
+ // find from local run-time image
+ SystemModuleFinder system = new SystemModuleFinder();
+ if (system.find(JDK_UNSUPPORTED).isPresent()) {
+ try {
+ return system.getClassReader(JDK_UNSUPPORTED).entries();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ return Collections.emptySet();
+ }
+ }
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java Thu May 19 10:55:33 2016 -0700
@@ -38,6 +38,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
/**
* Represents the source of the class files.
@@ -86,6 +87,10 @@
return Module.UNNAMED_MODULE;
}
+ public boolean contains(String entry) {
+ return reader.entries().contains(entry);
+ }
+
public void addClass(Location origin) {
deps.computeIfAbsent(origin, _k -> new HashSet<>());
}
@@ -98,6 +103,15 @@
return deps.keySet();
}
+ public Stream<Location> getDependencies() {
+ return deps.values().stream()
+ .flatMap(Set::stream);
+ }
+
+ public boolean hasDependences() {
+ return getDependencies().count() > 0;
+ }
+
public void visitDependences(Visitor v) {
for (Map.Entry<Location,Set<Location>> e: deps.entrySet()) {
for (Location target : e.getValue()) {
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java Thu May 19 10:55:33 2016 -0700
@@ -173,7 +173,7 @@
static boolean isClass(Path file) {
String fn = file.getFileName().toString();
- return fn.endsWith(".class") && !fn.equals(MODULE_INFO);
+ return fn.endsWith(".class");
}
class FileIterator implements Iterator<ClassFile> {
@@ -306,7 +306,7 @@
protected Set<String> scan() {
try (JarFile jf = new JarFile(path.toFile())) {
return jf.stream().map(JarEntry::getName)
- .filter(n -> n.endsWith(".class") && !n.endsWith(MODULE_INFO))
+ .filter(n -> n.endsWith(".class"))
.collect(Collectors.toSet());
} catch (IOException e) {
throw new UncheckedIOException(e);
@@ -409,7 +409,7 @@
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
String name = e.getName();
- if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+ if (name.endsWith(".class")) {
return e;
}
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java Thu May 19 10:55:33 2016 -0700
@@ -24,360 +24,285 @@
*/
package com.sun.tools.jdeps;
+import static com.sun.tools.jdeps.Module.*;
+import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
+import static java.util.stream.Collectors.*;
+
import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Dependencies;
import com.sun.tools.classfile.Dependency;
+import com.sun.tools.classfile.Dependency.Location;
import java.io.IOException;
-import java.nio.file.Path;
-import java.util.ArrayList;
+import java.io.UncheckedIOException;
import java.util.Deque;
import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.HashSet;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.sun.tools.jdeps.Module.*;
-import static com.sun.tools.jdeps.ModulePaths.SystemModulePath.JAVA_BASE;
+/**
+ * Parses class files and finds dependences
+ */
+class DependencyFinder {
+ private static Finder API_FINDER = new Finder(true);
+ private static Finder CLASS_FINDER = new Finder(false);
+
+ private final JdepsConfiguration configuration;
+ private final JdepsFilter filter;
-public class DependencyFinder {
- private final List<Archive> roots = new ArrayList<>();
- private final List<Archive> classpaths = new ArrayList<>();
- private final List<Module> modulepaths = new ArrayList<>();
- private final List<String> classes = new ArrayList<>();
- private final boolean compileTimeView;
+ private final Map<Finder, Deque<Archive>> parsedArchives = new ConcurrentHashMap<>();
+ private final Map<Location, Archive> parsedClasses = new ConcurrentHashMap<>();
+
+ private final ExecutorService pool = Executors.newFixedThreadPool(2);
+ private final Deque<FutureTask<Set<Location>>> tasks = new ConcurrentLinkedDeque<>();
- DependencyFinder(boolean compileTimeView) {
- this.compileTimeView = compileTimeView;
+ DependencyFinder(JdepsConfiguration configuration,
+ JdepsFilter filter) {
+ this.configuration = configuration;
+ this.filter = filter;
+ this.parsedArchives.put(API_FINDER, new ConcurrentLinkedDeque<>());
+ this.parsedArchives.put(CLASS_FINDER, new ConcurrentLinkedDeque<>());
}
- /*
- * Adds a class name to the root set
- */
- void addClassName(String cn) {
- classes.add(cn);
+ Map<Location, Archive> locationToArchive() {
+ return parsedClasses;
}
- /*
- * Adds the archive of the given path to the root set
+ /**
+ * Returns the modules of all dependencies found
*/
- void addRoot(Path path) {
- addRoot(Archive.getInstance(path));
- }
-
- /*
- * Adds the given archive to the root set
- */
- void addRoot(Archive archive) {
- Objects.requireNonNull(archive);
- if (!roots.contains(archive))
- roots.add(archive);
+ Stream<Archive> getDependences(Archive source) {
+ return source.getDependencies()
+ .map(this::locationToArchive)
+ .filter(a -> a != source);
}
/**
- * Add an archive specified in the classpath.
+ * Returns the location to archive map; or NOT_FOUND.
+ *
+ * Location represents a parsed class.
*/
- void addClassPathArchive(Path path) {
- addClassPathArchive(Archive.getInstance(path));
- }
-
- /**
- * Add an archive specified in the classpath.
- */
- void addClassPathArchive(Archive archive) {
- Objects.requireNonNull(archive);
- classpaths.add(archive);
+ Archive locationToArchive(Location location) {
+ return parsedClasses.containsKey(location)
+ ? parsedClasses.get(location)
+ : configuration.findClass(location).orElse(NOT_FOUND);
}
/**
- * Add an archive specified in the modulepath.
+ * Returns a map from an archive to its required archives
*/
- void addModule(Module m) {
- Objects.requireNonNull(m);
- modulepaths.add(m);
+ Map<Archive, Set<Archive>> dependences() {
+ Map<Archive, Set<Archive>> map = new HashMap<>();
+ parsedArchives.values().stream()
+ .flatMap(Deque::stream)
+ .filter(a -> !a.isEmpty())
+ .forEach(source -> {
+ Set<Archive> deps = getDependences(source).collect(toSet());
+ if (!deps.isEmpty()) {
+ map.put(source, deps);
+ }
+ });
+ return map;
+ }
+
+ boolean isParsed(Location location) {
+ return parsedClasses.containsKey(location);
}
/**
- * Returns the root set.
+ * Parses all class files from the given archive stream and returns
+ * all target locations.
*/
- List<Archive> roots() {
- return roots;
+ public Set<Location> parse(Stream<? extends Archive> archiveStream) {
+ archiveStream.forEach(archive -> parse(archive, CLASS_FINDER));
+ return waitForTasksCompleted();
}
/**
- * Returns a stream of all archives including the root set, module paths,
- * and classpath.
- *
- * This only returns the archives with classes parsed.
+ * Parses the exported API class files from the given archive stream and
+ * returns all target locations.
*/
- Stream<Archive> archives() {
- Stream<Archive> archives = Stream.concat(roots.stream(), modulepaths.stream());
- archives = Stream.concat(archives, classpaths.stream());
- return archives.filter(a -> !a.isEmpty())
- .distinct();
+ public Set<Location> parseExportedAPIs(Stream<? extends Archive> archiveStream) {
+ archiveStream.forEach(archive -> parse(archive, API_FINDER));
+ return waitForTasksCompleted();
+ }
+
+ /**
+ * Parses the named class from the given archive and
+ * returns all target locations the named class references.
+ */
+ public Set<Location> parse(Archive archive, String name) {
+ try {
+ return parse(archive, CLASS_FINDER, name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
/**
- * Finds dependencies
- *
- * @param apiOnly API only
- * @param maxDepth depth of transitive dependency analysis; zero indicates
- * @throws IOException
+ * Parses the exported API of the named class from the given archive and
+ * returns all target locations the named class references.
*/
- void findDependencies(JdepsFilter filter, boolean apiOnly, int maxDepth)
- throws IOException
+ public Set<Location> parseExportedAPIs(Archive archive, String name)
{
- Dependency.Finder finder =
- apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
- : Dependencies.getClassDependencyFinder();
-
- // list of archives to be analyzed
- Set<Archive> roots = new LinkedHashSet<>(this.roots);
-
- // include java.base in root set
- roots.add(JAVA_BASE);
-
- // If -include pattern specified, classes may be in module path or class path.
- // To get compile time view analysis, all classes are analyzed.
- // add all modules except JDK modules to root set
- modulepaths.stream()
- .filter(filter::matches)
- .forEach(roots::add);
+ try {
+ return parse(archive, API_FINDER, name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
- // add classpath to the root set
- classpaths.stream()
- .filter(filter::matches)
- .forEach(roots::add);
-
- // transitive dependency
- int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
+ private Optional<FutureTask<Set<Location>>> parse(Archive archive, Finder finder) {
+ if (parsedArchives.get(finder).contains(archive))
+ return Optional.empty();
- // 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.
- ConcurrentLinkedDeque<String> deque = new ConcurrentLinkedDeque<>();
- ConcurrentSkipListSet<String> doneClasses = new ConcurrentSkipListSet<>();
-
- TaskExecutor executor = new TaskExecutor(finder, filter, apiOnly, deque, doneClasses);
- try {
- // get the immediate dependencies of the input files
- for (Archive source : roots) {
- executor.task(source, deque);
- }
- executor.waitForTasksCompleted();
-
- List<Archive> archives = Stream.concat(Stream.concat(roots.stream(),
- modulepaths.stream()),
- classpaths.stream())
- .collect(Collectors.toList());
+ parsedArchives.get(finder).add(archive);
- // Additional pass to find archive where dependences are identified
- // and also any specified classes, if any.
- // If -R is specified, perform transitive dependency analysis.
- Deque<String> unresolved = new LinkedList<>(classes);
- do {
- String name;
- while ((name = unresolved.poll()) != null) {
- if (doneClasses.contains(name)) {
- continue;
+ trace("parsing %s %s%n", archive.getName(), archive.path());
+ FutureTask<Set<Location>> task = new FutureTask<>(new Callable<>() {
+ public Set<Location> call() throws Exception {
+ Set<Location> targets = new HashSet<>();
+ for (ClassFile cf : archive.reader().getClassFiles()) {
+ String classFileName;
+ try {
+ classFileName = cf.getName();
+ } catch (ConstantPoolException e) {
+ throw new Dependencies.ClassFileError(e);
}
- if (compileTimeView) {
- final String cn = name + ".class";
- // parse all classes in the source archive
- Optional<Archive> source = archives.stream()
- .filter(a -> a.reader().entries().contains(cn))
- .findFirst();
- trace("%s compile time view %s%n", name, source.map(Archive::getName).orElse(" not found"));
- if (source.isPresent()) {
- executor.runTask(source.get(), deque);
- }
- }
- ClassFile cf = null;
- for (Archive archive : archives) {
- cf = archive.reader().getClassFile(name);
- if (cf != null) {
- String classFileName;
- try {
- classFileName = cf.getName();
- } catch (ConstantPoolException e) {
- throw new Dependencies.ClassFileError(e);
- }
- 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);
- for (Dependency d : finder.findDependencies(cf)) {
- if (depth == 0) {
- // ignore the dependency
- archive.addClass(d.getOrigin());
- break;
- } else if (filter.accepts(d) && filter.accept(archive)) {
- // continue analysis on non-JDK classes
- archive.addClass(d.getOrigin(), d.getTarget());
- String cn = d.getTarget().getName();
- if (!doneClasses.contains(cn) && !deque.contains(cn)) {
- deque.add(cn);
- }
- } else {
- // ensure that the parsed class is added the archive
- archive.addClass(d.getOrigin());
- }
- }
- }
- break;
+ // filter source class/archive
+ String cn = classFileName.replace('/', '.');
+ if (!finder.accept(archive, cn, cf.access_flags))
+ continue;
+
+ // tests if this class matches the -include
+ if (!filter.matches(cn))
+ continue;
+
+ for (Dependency d : finder.findDependencies(cf)) {
+ if (filter.accepts(d)) {
+ archive.addClass(d.getOrigin(), d.getTarget());
+ targets.add(d.getTarget());
+ } else {
+ // ensure that the parsed class is added the archive
+ archive.addClass(d.getOrigin());
}
- }
- if (cf == null) {
- doneClasses.add(name);
+ parsedClasses.putIfAbsent(d.getOrigin(), archive);
}
}
- unresolved = deque;
- deque = new ConcurrentLinkedDeque<>();
- } while (!unresolved.isEmpty() && depth-- > 0);
- } finally {
- executor.shutdown();
- }
- }
+
+ return targets;
+ }
+ });
+ tasks.add(task);
+ pool.submit(task);
+ return Optional.of(task);
+ }
- /**
- * TaskExecutor creates FutureTask to analyze all classes in a given archive
- */
- private class TaskExecutor {
- final ExecutorService pool;
- final Dependency.Finder finder;
- final JdepsFilter filter;
- final boolean apiOnly;
- final Set<String> doneClasses;
- final Map<Archive, FutureTask<Void>> tasks = new HashMap<>();
+ private Set<Location> parse(Archive archive, Finder finder, String name)
+ throws IOException
+ {
+ ClassFile cf = archive.reader().getClassFile(name);
+ if (cf == null) {
+ throw new IllegalArgumentException(archive.getName() + " does not contain " + name);
+ }
- TaskExecutor(Dependency.Finder finder,
- JdepsFilter filter,
- boolean apiOnly,
- ConcurrentLinkedDeque<String> deque,
- Set<String> doneClasses) {
- this.pool = Executors.newFixedThreadPool(2);
- this.finder = finder;
- this.filter = filter;
- this.apiOnly = apiOnly;
- this.doneClasses = doneClasses;
+ Set<Location> targets = new HashSet<>();
+ String cn;
+ try {
+ cn = cf.getName().replace('/', '.');
+ } catch (ConstantPoolException e) {
+ throw new Dependencies.ClassFileError(e);
}
- /**
- * Creates a new task to analyze class files in the given archive.
- * The dependences are added to the given deque for analysis.
- */
- FutureTask<Void> task(Archive archive, final ConcurrentLinkedDeque<String> deque) {
- trace("parsing %s %s%n", archive.getName(), archive.path());
- FutureTask<Void> task = new FutureTask<Void>(new Callable<Void>() {
- public Void call() throws Exception {
- for (ClassFile cf : archive.reader().getClassFiles()) {
- String classFileName;
- try {
- classFileName = cf.getName();
- } catch (ConstantPoolException e) {
- throw new Dependencies.ClassFileError(e);
- }
+ if (!finder.accept(archive, cn, cf.access_flags))
+ return targets;
+
+ // tests if this class matches the -include
+ if (!filter.matches(cn))
+ return targets;
- // tests if this class matches the -include
- String cn = classFileName.replace('/', '.');
- if (!filter.matches(cn))
- continue;
+ // skip checking filter.matches
+ for (Dependency d : finder.findDependencies(cf)) {
+ if (filter.accepts(d)) {
+ targets.add(d.getTarget());
+ archive.addClass(d.getOrigin(), d.getTarget());
+ } else {
+ // ensure that the parsed class is added the archive
+ archive.addClass(d.getOrigin());
+ }
+ parsedClasses.putIfAbsent(d.getOrigin(), archive);
+ }
+ return targets;
+ }
- // if -apionly is specified, analyze only exported and public types
- if (apiOnly && !(isExported(archive, cn) && cf.access_flags.is(AccessFlags.ACC_PUBLIC)))
- continue;
-
- if (!doneClasses.contains(classFileName)) {
- doneClasses.add(classFileName);
- }
+ /*
+ * Waits until all submitted tasks are completed.
+ */
+ private Set<Location> waitForTasksCompleted() {
+ try {
+ Set<Location> targets = new HashSet<>();
+ FutureTask<Set<Location>> task;
+ while ((task = tasks.poll()) != null) {
+ // wait for completion
+ if (!task.isDone())
+ targets.addAll(task.get());
+ }
+ return targets;
+ } catch (InterruptedException|ExecutionException e) {
+ throw new Error(e);
+ }
+ }
- for (Dependency d : finder.findDependencies(cf)) {
- if (filter.accepts(d) && filter.accept(archive)) {
- String name = d.getTarget().getName();
- if (!doneClasses.contains(name) && !deque.contains(name)) {
- deque.add(name);
- }
- archive.addClass(d.getOrigin(), d.getTarget());
- } else {
- // ensure that the parsed class is added the archive
- archive.addClass(d.getOrigin());
- }
- }
- }
- return null;
- }
- });
- tasks.put(archive, task);
- pool.submit(task);
- return task;
+ /*
+ * Shutdown the executor service.
+ */
+ void shutdown() {
+ pool.shutdown();
+ }
+
+ private interface SourceFilter {
+ boolean accept(Archive archive, String cn, AccessFlags accessFlags);
+ }
+
+ private static class Finder implements Dependency.Finder, SourceFilter {
+ private final Dependency.Finder finder;
+ private final boolean apiOnly;
+ Finder(boolean apiOnly) {
+ this.apiOnly = apiOnly;
+ this.finder = apiOnly
+ ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
+ : Dependencies.getClassDependencyFinder();
+
}
- /*
- * This task will parse all class files of the given archive, if it's a new task.
- * This method waits until the task is completed.
- */
- void runTask(Archive archive, final ConcurrentLinkedDeque<String> deque) {
- if (tasks.containsKey(archive))
- return;
+ @Override
+ public boolean accept(Archive archive, String cn, AccessFlags accessFlags) {
+ int i = cn.lastIndexOf('.');
+ String pn = i > 0 ? cn.substring(0, i) : "";
- FutureTask<Void> task = task(archive, deque);
- try {
- // wait for completion
- task.get();
- } catch (InterruptedException|ExecutionException e) {
- throw new Error(e);
- }
+ // if -apionly is specified, analyze only exported and public types
+ // All packages are exported in unnamed module.
+ return apiOnly ? archive.getModule().isExported(pn) &&
+ accessFlags.is(AccessFlags.ACC_PUBLIC)
+ : true;
}
- /*
- * Waits until all submitted tasks are completed.
- */
- void waitForTasksCompleted() {
- try {
- for (FutureTask<Void> t : tasks.values()) {
- if (t.isDone())
- continue;
-
- // wait for completion
- t.get();
- }
- } catch (InterruptedException|ExecutionException e) {
- throw new Error(e);
- }
- }
-
- /*
- * Shutdown the executor service.
- */
- void shutdown() {
- pool.shutdown();
- }
-
- /**
- * Tests if the given class name is exported by the given archive.
- *
- * All packages are exported in unnamed module.
- */
- private boolean isExported(Archive archive, String classname) {
- int i = classname.lastIndexOf('.');
- String pn = i > 0 ? classname.substring(0, i) : "";
- return archive.getModule().isExported(pn);
+ @Override
+ public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
+ return finder.findDependencies(classfile);
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.Location;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
+import static com.sun.tools.jdeps.Analyzer.Type.VERBOSE;
+import static com.sun.tools.jdeps.Module.trace;
+import static java.util.stream.Collectors.*;
+
+/**
+ * Dependency Analyzer.
+ *
+ * Type of filters:
+ * source filter: -include <pattern>
+ * target filter: -package, -regex, -requires
+ *
+ * The initial archive set for analysis includes
+ * 1. archives specified in the command line arguments
+ * 2. observable modules matching the source filter
+ * 3. classpath archives matching the source filter or target filter
+ * 4. -addmods and -m root modules
+ */
+public class DepsAnalyzer {
+ final JdepsConfiguration configuration;
+ final JdepsFilter filter;
+ final JdepsWriter writer;
+ final Analyzer.Type verbose;
+ final boolean apiOnly;
+
+ final DependencyFinder finder;
+ final Analyzer analyzer;
+ final List<Archive> rootArchives = new ArrayList<>();
+
+ // parsed archives
+ final Set<Archive> archives = new LinkedHashSet<>();
+
+ public DepsAnalyzer(JdepsConfiguration config,
+ JdepsFilter filter,
+ JdepsWriter writer,
+ Analyzer.Type verbose,
+ boolean apiOnly) {
+ this.configuration = config;
+ this.filter = filter;
+ this.writer = writer;
+ this.verbose = verbose;
+ this.apiOnly = apiOnly;
+
+ this.finder = new DependencyFinder(config, filter);
+ this.analyzer = new Analyzer(configuration, verbose, filter);
+
+ // determine initial archives to be analyzed
+ this.rootArchives.addAll(configuration.initialArchives());
+
+ // if -include pattern is specified, add the matching archives on
+ // classpath to the root archives
+ if (filter.hasIncludePattern() || filter.hasTargetFilter()) {
+ configuration.getModules().values().stream()
+ .filter(source -> filter.include(source) && filter.matches(source))
+ .forEach(this.rootArchives::add);
+ }
+
+ // class path archives
+ configuration.classPathArchives().stream()
+ .filter(filter::matches)
+ .forEach(this.rootArchives::add);
+
+ // Include the root modules for analysis
+ this.rootArchives.addAll(configuration.rootModules());
+
+ trace("analyze root archives: %s%n", this.rootArchives);
+ }
+
+ /*
+ * Perform runtime dependency analysis
+ */
+ public boolean run() throws IOException {
+ return run(false, 1);
+ }
+
+ /**
+ * Perform compile-time view or run-time view dependency analysis.
+ *
+ * @param compileTimeView
+ * @param maxDepth depth of recursive analysis. depth == 0 if -R is set
+ */
+ public boolean run(boolean compileTimeView, int maxDepth) throws IOException {
+ try {
+ // parse each packaged module or classpath archive
+ if (apiOnly) {
+ finder.parseExportedAPIs(rootArchives.stream());
+ } else {
+ finder.parse(rootArchives.stream());
+ }
+ archives.addAll(rootArchives);
+
+ int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
+
+ // transitive analysis
+ if (depth > 1) {
+ if (compileTimeView)
+ transitiveArchiveDeps(depth-1);
+ else
+ transitiveDeps(depth-1);
+ }
+
+ Set<Archive> archives = archives();
+
+ // analyze the dependencies collected
+ analyzer.run(archives, finder.locationToArchive());
+
+ writer.generateOutput(archives, analyzer);
+ } finally {
+ finder.shutdown();
+ }
+ return true;
+ }
+
+ /**
+ * Returns the archives for reporting that has matching dependences.
+ *
+ * If -requires is set, they should be excluded.
+ */
+ Set<Archive> archives() {
+ if (filter.requiresFilter().isEmpty()) {
+ return archives.stream()
+ .filter(filter::include)
+ .filter(Archive::hasDependences)
+ .collect(Collectors.toSet());
+ } else {
+ // use the archives that have dependences and not specified in -requires
+ return archives.stream()
+ .filter(filter::include)
+ .filter(source -> !filter.requiresFilter().contains(source))
+ .filter(source ->
+ source.getDependencies()
+ .map(finder::locationToArchive)
+ .anyMatch(a -> a != source))
+ .collect(Collectors.toSet());
+ }
+ }
+
+ /**
+ * Returns the dependences, either class name or package name
+ * as specified in the given verbose level.
+ */
+ Stream<String> dependences() {
+ return analyzer.archives().stream()
+ .map(analyzer::dependences)
+ .flatMap(Set::stream)
+ .distinct();
+ }
+
+ /**
+ * Returns the archives that contains the given locations and
+ * not parsed and analyzed.
+ */
+ private Set<Archive> unresolvedArchives(Stream<Location> locations) {
+ return locations.filter(l -> !finder.isParsed(l))
+ .distinct()
+ .map(configuration::findClass)
+ .flatMap(Optional::stream)
+ .filter(filter::include)
+ .collect(toSet());
+ }
+
+ /*
+ * Recursively analyzes entire module/archives.
+ */
+ private void transitiveArchiveDeps(int depth) throws IOException {
+ Stream<Location> deps = archives.stream()
+ .flatMap(Archive::getDependencies);
+
+ // start with the unresolved archives
+ Set<Archive> unresolved = unresolvedArchives(deps);
+ do {
+ // parse all unresolved archives
+ Set<Location> targets = apiOnly
+ ? finder.parseExportedAPIs(unresolved.stream())
+ : finder.parse(unresolved.stream());
+ archives.addAll(unresolved);
+
+ // Add dependencies to the next batch for analysis
+ unresolved = unresolvedArchives(targets.stream());
+ } while (!unresolved.isEmpty() && depth-- > 0);
+ }
+
+ /*
+ * Recursively analyze the class dependences
+ */
+ private void transitiveDeps(int depth) throws IOException {
+ Stream<Location> deps = archives.stream()
+ .flatMap(Archive::getDependencies);
+
+ Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new));
+ ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>();
+ do {
+ Location target;
+ while ((target = unresolved.poll()) != null) {
+ if (finder.isParsed(target))
+ continue;
+
+ Archive archive = configuration.findClass(target).orElse(null);
+ if (archive != null && filter.include(archive)) {
+ archives.add(archive);
+
+ String name = target.getName();
+ Set<Location> targets = apiOnly
+ ? finder.parseExportedAPIs(archive, name)
+ : finder.parse(archive, name);
+
+ // build unresolved dependencies
+ targets.stream()
+ .filter(t -> !finder.isParsed(t))
+ .forEach(deque::add);
+ }
+ }
+ unresolved = deque;
+ deque = new ConcurrentLinkedDeque<>();
+ } while (!unresolved.isEmpty() && depth-- > 0);
+ }
+
+ // ----- for testing purpose -----
+
+ public static enum Info {
+ REQUIRES,
+ REQUIRES_PUBLIC,
+ EXPORTED_API,
+ MODULE_PRIVATE,
+ QUALIFIED_EXPORTED_API,
+ INTERNAL_API,
+ JDK_INTERNAL_API
+ }
+
+ public static class Node {
+ public final String name;
+ public final String source;
+ public final Info info;
+ Node(String name, Info info) {
+ this(name, name, info);
+ }
+ Node(String name, String source, Info info) {
+ this.name = name;
+ this.source = source;
+ this.info = info;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (info != Info.REQUIRES && info != Info.REQUIRES_PUBLIC)
+ sb.append(source).append("/");
+
+ sb.append(name);
+ if (info == Info.QUALIFIED_EXPORTED_API)
+ sb.append(" (qualified)");
+ else if (info == Info.JDK_INTERNAL_API)
+ sb.append(" (JDK internal)");
+ else if (info == Info.INTERNAL_API)
+ sb.append(" (internal)");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Node))
+ return false;
+
+ Node other = (Node)o;
+ return this.name.equals(other.name) &&
+ this.source.equals(other.source) &&
+ this.info.equals(other.info);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + source.hashCode();
+ result = 31 * result + info.hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * Returns a graph of module dependences.
+ *
+ * Each Node represents a module and each edge is a dependence.
+ * No analysis on "requires public".
+ */
+ public Graph<Node> moduleGraph() {
+ Graph.Builder<Node> builder = new Graph.Builder<>();
+
+ archives().stream()
+ .forEach(m -> {
+ Node u = new Node(m.getName(), Info.REQUIRES);
+ builder.addNode(u);
+ analyzer.requires(m)
+ .map(req -> new Node(req.getName(), Info.REQUIRES))
+ .forEach(v -> builder.addEdge(u, v));
+ });
+ return builder.build();
+ }
+
+ /**
+ * Returns a graph of dependences.
+ *
+ * Each Node represents a class or package per the specified verbose level.
+ * Each edge indicates
+ */
+ public Graph<Node> dependenceGraph() {
+ Graph.Builder<Node> builder = new Graph.Builder<>();
+
+ archives().stream()
+ .map(analyzer.results::get)
+ .filter(deps -> !deps.dependencies().isEmpty())
+ .flatMap(deps -> deps.dependencies().stream())
+ .forEach(d -> addEdge(builder, d));
+ return builder.build();
+ }
+
+ private void addEdge(Graph.Builder<Node> builder, Analyzer.Dep dep) {
+ Archive source = dep.originArchive();
+ Archive target = dep.targetArchive();
+ String pn = dep.target();
+ if ((verbose == CLASS || verbose == VERBOSE)) {
+ int i = dep.target().lastIndexOf('.');
+ pn = i > 0 ? dep.target().substring(0, i) : "";
+ }
+ final Info info;
+ if (source == target) {
+ info = Info.MODULE_PRIVATE;
+ } else if (!target.getModule().isNamed()) {
+ info = Info.EXPORTED_API;
+ } else if (target.getModule().isExported(pn)) {
+ info = Info.EXPORTED_API;
+ } else {
+ Module module = target.getModule();
+
+ if (!source.getModule().isJDK() && module.isJDK())
+ info = Info.JDK_INTERNAL_API;
+ // qualified exports or inaccessible
+ else if (module.isExported(pn, source.getModule().name()))
+ info = Info.QUALIFIED_EXPORTED_API;
+ else
+ info = Info.INTERNAL_API;
+ }
+
+ Node u = new Node(dep.origin(), source.getName(), info);
+ Node v = new Node(dep.target(), target.getName(), info);
+ builder.addEdge(u, v);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.PrintWriter;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public final class Graph<T> {
+ private final Set<T> nodes;
+ private final Map<T, Set<T>> edges;
+
+ public Graph(Set<T> nodes, Map<T, Set<T>> edges) {
+ this.nodes = Collections.unmodifiableSet(nodes);
+ this.edges = Collections.unmodifiableMap(edges);
+ }
+
+ public Set<T> nodes() {
+ return nodes;
+ }
+
+ public Map<T, Set<T>> edges() {
+ return edges;
+ }
+
+ public Set<T> adjacentNodes(T u) {
+ return edges.get(u);
+ }
+
+ public boolean contains(T u) {
+ return nodes.contains(u);
+ }
+
+ public Set<Edge<T>> edgesFrom(T u) {
+ return edges.get(u).stream()
+ .map(v -> new Edge<T>(u, v))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Returns a new Graph after transitive reduction
+ */
+ public Graph<T> reduce() {
+ Builder<T> builder = new Builder<>();
+ nodes.stream()
+ .forEach(u -> {
+ builder.addNode(u);
+ edges.get(u).stream()
+ .filter(v -> !pathExists(u, v, false))
+ .forEach(v -> builder.addEdge(u, v));
+ });
+ return builder.build();
+ }
+
+ /**
+ * Returns a new Graph after transitive reduction. All edges in
+ * the given g takes precedence over this graph.
+ *
+ * @throw IllegalArgumentException g must be a subgraph this graph
+ */
+ public Graph<T> reduce(Graph<T> g) {
+ boolean subgraph = nodes.containsAll(g.nodes) &&
+ g.edges.keySet().stream()
+ .allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));
+ if (!subgraph) {
+ throw new IllegalArgumentException(g + " is not a subgraph of " + this);
+ }
+
+ Builder<T> builder = new Builder<>();
+ nodes.stream()
+ .forEach(u -> {
+ builder.addNode(u);
+ // filter the edge if there exists a path from u to v in the given g
+ // or there exists another path from u to v in this graph
+ edges.get(u).stream()
+ .filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))
+ .forEach(v -> builder.addEdge(u, v));
+ });
+
+ // add the overlapped edges from this graph and the given g
+ g.edges().keySet().stream()
+ .forEach(u -> g.adjacentNodes(u).stream()
+ .filter(v -> isAdjacent(u, v))
+ .forEach(v -> builder.addEdge(u, v)));
+ return builder.build();
+ }
+
+ /**
+ * Returns nodes sorted in topological order.
+ */
+ public Stream<T> orderedNodes() {
+ TopoSorter<T> sorter = new TopoSorter<>(this);
+ return sorter.result.stream();
+ }
+
+ /**
+ * Traverse this graph and performs the given action in topological order
+ */
+ public void ordered(Consumer<T> action) {
+ TopoSorter<T> sorter = new TopoSorter<>(this);
+ sorter.ordered(action);
+ }
+
+ /**
+ * Traverses this graph and performs the given action in reverse topological order
+ */
+ public void reverse(Consumer<T> action) {
+ TopoSorter<T> sorter = new TopoSorter<>(this);
+ sorter.reverse(action);
+ }
+
+ /**
+ * Returns a transposed graph from this graph
+ */
+ public Graph<T> transpose() {
+ Builder<T> builder = new Builder<>();
+ builder.addNodes(nodes);
+ // reverse edges
+ edges.keySet().forEach(u -> {
+ edges.get(u).stream()
+ .forEach(v -> builder.addEdge(v, u));
+ });
+ return builder.build();
+ }
+
+ /**
+ * Returns all nodes reachable from the given set of roots.
+ */
+ public Set<T> dfs(Set<T> roots) {
+ Deque<T> deque = new LinkedList<>(roots);
+ Set<T> visited = new HashSet<>();
+ while (!deque.isEmpty()) {
+ T u = deque.pop();
+ if (!visited.contains(u)) {
+ visited.add(u);
+ if (contains(u)) {
+ adjacentNodes(u).stream()
+ .filter(v -> !visited.contains(v))
+ .forEach(deque::push);
+ }
+ }
+ }
+ return visited;
+ }
+
+ private boolean isAdjacent(T u, T v) {
+ return edges.containsKey(u) && edges.get(u).contains(v);
+ }
+
+ private boolean pathExists(T u, T v) {
+ return pathExists(u, v, true);
+ }
+
+ /**
+ * Returns true if there exists a path from u to v in this graph.
+ * If includeAdjacent is false, it returns true if there exists
+ * another path from u to v of distance > 1
+ */
+ private boolean pathExists(T u, T v, boolean includeAdjacent) {
+ if (!nodes.contains(u) || !nodes.contains(v)) {
+ return false;
+ }
+ if (includeAdjacent && isAdjacent(u, v)) {
+ return true;
+ }
+ Deque<T> stack = new LinkedList<>();
+ Set<T> visited = new HashSet<>();
+ stack.push(u);
+ while (!stack.isEmpty()) {
+ T node = stack.pop();
+ if (node.equals(v)) {
+ return true;
+ }
+ if (!visited.contains(node)) {
+ visited.add(node);
+ edges.get(node).stream()
+ .filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))
+ .forEach(e -> stack.push(e));
+ }
+ }
+ assert !visited.contains(v);
+ return false;
+ }
+
+ public void printGraph(PrintWriter out) {
+ out.println("graph for " + nodes);
+ nodes.stream()
+ .forEach(u -> adjacentNodes(u).stream()
+ .forEach(v -> out.format(" %s -> %s%n", u, v)));
+ }
+
+ @Override
+ public String toString() {
+ return nodes.toString();
+ }
+
+ static class Edge<T> {
+ final T u;
+ final T v;
+ Edge(T u, T v) {
+ this.u = u;
+ this.v = v;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s -> %s", u, v);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof Edge))
+ return false;
+
+ @SuppressWarnings("unchecked")
+ Edge<T> edge = (Edge<T>) o;
+
+ return u.equals(edge.u) && v.equals(edge.v);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = u.hashCode();
+ result = 31 * result + v.hashCode();
+ return result;
+ }
+ }
+
+ static class Builder<T> {
+ final Set<T> nodes = new HashSet<>();
+ final Map<T, Set<T>> edges = new HashMap<>();
+
+ public void addNode(T node) {
+ if (nodes.contains(node)) {
+ return;
+ }
+ nodes.add(node);
+ edges.computeIfAbsent(node, _e -> new HashSet<>());
+ }
+
+ public void addNodes(Set<T> nodes) {
+ nodes.addAll(nodes);
+ }
+
+ public void addEdge(T u, T v) {
+ addNode(u);
+ addNode(v);
+ edges.get(u).add(v);
+ }
+
+ public Graph<T> build() {
+ return new Graph<T>(nodes, edges);
+ }
+ }
+
+ /**
+ * Topological sort
+ */
+ static class TopoSorter<T> {
+ final Deque<T> result = new LinkedList<>();
+ final Deque<T> nodes;
+ final Graph<T> graph;
+ TopoSorter(Graph<T> graph) {
+ this.graph = graph;
+ this.nodes = new LinkedList<>(graph.nodes);
+ sort();
+ }
+
+ public void ordered(Consumer<T> action) {
+ result.iterator().forEachRemaining(action);
+ }
+
+ public void reverse(Consumer<T> action) {
+ result.descendingIterator().forEachRemaining(action);
+ }
+
+ private void sort() {
+ Deque<T> visited = new LinkedList<>();
+ Deque<T> done = new LinkedList<>();
+ T node;
+ while ((node = nodes.poll()) != null) {
+ if (!visited.contains(node)) {
+ visit(node, visited, done);
+ }
+ }
+ }
+
+ private void visit(T node, Deque<T> visited, Deque<T> done) {
+ if (visited.contains(node)) {
+ if (!done.contains(node)) {
+ throw new IllegalArgumentException("Cyclic detected: " +
+ node + " " + graph.edges().get(node));
+ }
+ return;
+ }
+ visited.add(node);
+ graph.edges().get(node).stream()
+ .forEach(x -> visit(x, visited, done));
+ done.add(node);
+ result.addLast(node);
+ }
+ }
+
+ public static class DotGraph {
+ static final String ORANGE = "#e76f00";
+ static final String BLUE = "#437291";
+ static final String GRAY = "#dddddd";
+
+ static final String REEXPORTS = "";
+ static final String REQUIRES = "style=\"dashed\"";
+ static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
+
+ static final Set<String> javaModules = modules(name ->
+ (name.startsWith("java.") && !name.equals("java.smartcardio")));
+ static final Set<String> jdkModules = modules(name ->
+ (name.startsWith("java.") ||
+ name.startsWith("jdk.") ||
+ name.startsWith("javafx.")) && !javaModules.contains(name));
+
+ private static Set<String> modules(Predicate<String> predicate) {
+ return ModuleFinder.ofSystem().findAll()
+ .stream()
+ .map(ModuleReference::descriptor)
+ .map(ModuleDescriptor::name)
+ .filter(predicate)
+ .collect(Collectors.toSet());
+ }
+
+ static void printAttributes(PrintWriter out) {
+ out.format(" size=\"25,25\";%n");
+ out.format(" nodesep=.5;%n");
+ out.format(" ranksep=1.5;%n");
+ out.format(" pencolor=transparent;%n");
+ out.format(" node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
+ out.format(" edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
+ }
+
+ static void printNodes(PrintWriter out, Graph<String> graph) {
+ out.format(" subgraph se {%n");
+ graph.nodes().stream()
+ .filter(javaModules::contains)
+ .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
+ mn, ORANGE, "java"));
+ out.format(" }%n");
+ graph.nodes().stream()
+ .filter(jdkModules::contains)
+ .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
+ mn, BLUE, "jdk"));
+
+ graph.nodes().stream()
+ .filter(mn -> !javaModules.contains(mn) && !jdkModules.contains(mn))
+ .forEach(mn -> out.format(" \"%s\";%n", mn));
+ }
+
+ static void printEdges(PrintWriter out, Graph<String> graph,
+ String node, Set<String> requiresPublic) {
+ graph.adjacentNodes(node).forEach(dn -> {
+ String attr = dn.equals("java.base") ? REQUIRES_BASE
+ : (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
+ out.format(" \"%s\" -> \"%s\" [%s];%n", node, dn, attr);
+ });
+ }
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 static com.sun.tools.jdeps.Module.trace;
+import static java.util.stream.Collectors.*;
+
+import com.sun.tools.classfile.Dependency;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public class JdepsConfiguration {
+ private static final String MODULE_INFO = "module-info.class";
+
+ private final SystemModuleFinder system;
+ private final ModuleFinder finder;
+
+ private final Map<String, Module> nameToModule = new LinkedHashMap<>();
+ private final Map<String, Module> packageToModule = new HashMap<>();
+ private final Map<String, List<Archive>> packageToUnnamedModule = new HashMap<>();
+
+ private final List<Archive> classpathArchives = new ArrayList<>();
+ private final List<Archive> initialArchives = new ArrayList<>();
+ private final Set<Module> rootModules = new HashSet<>();
+ private final Configuration configuration;
+
+ private JdepsConfiguration(SystemModuleFinder systemModulePath,
+ ModuleFinder finder,
+ Set<String> roots,
+ List<Path> classpaths,
+ List<Archive> initialArchives)
+ throws IOException
+ {
+ trace("root: %s%n", roots);
+
+ this.system = systemModulePath;
+ this.finder = finder;
+
+ // build root set
+ Set<String> mods = new HashSet<>();
+
+ if (initialArchives.isEmpty() && classpaths.isEmpty() && !roots.isEmpty()) {
+ // named module as root. No initial unnamed module
+ mods.addAll(roots);
+ } else {
+ // unnamed module
+ mods.addAll(roots);
+ mods.addAll(systemModulePath.defaultSystemRoots());
+ }
+
+ this.configuration = Configuration.empty()
+ .resolveRequires(finder, ModuleFinder.of(), mods);
+
+ this.configuration.modules().stream()
+ .map(ResolvedModule::reference)
+ .forEach(this::addModuleReference);
+
+ // packages in unnamed module
+ initialArchives.forEach(archive -> {
+ addPackagesInUnnamedModule(archive);
+ this.initialArchives.add(archive);
+ });
+
+ // classpath archives
+ for (Path p : classpaths) {
+ if (Files.exists(p)) {
+ Archive archive = Archive.getInstance(p);
+ addPackagesInUnnamedModule(archive);
+ classpathArchives.add(archive);
+ }
+ }
+
+ // all roots specified in -addmods or -m are included
+ // as the initial set for analysis.
+ roots.stream()
+ .map(nameToModule::get)
+ .forEach(this.rootModules::add);
+
+ initProfiles();
+
+ trace("resolved modules: %s%n", nameToModule.keySet().stream()
+ .sorted().collect(joining("\n", "\n", "")));
+ }
+
+ private void initProfiles() {
+ // other system modules are not observed and not added in nameToModule map
+ Map<String, Module> systemModules =
+ system.moduleNames()
+ .collect(toMap(Function.identity(), (mn) -> {
+ Module m = nameToModule.get(mn);
+ if (m == null) {
+ ModuleReference mref = finder.find(mn).get();
+ m = toModule(mref);
+ }
+ return m;
+ }));
+ Profile.init(systemModules);
+ }
+
+ private void addModuleReference(ModuleReference mref) {
+ Module module = toModule(mref);
+ nameToModule.put(mref.descriptor().name(), module);
+ mref.descriptor().packages()
+ .forEach(pn -> packageToModule.putIfAbsent(pn, module));
+ }
+
+ private void addPackagesInUnnamedModule(Archive archive) {
+ archive.reader().entries().stream()
+ .filter(e -> e.endsWith(".class") && !e.equals(MODULE_INFO))
+ .map(this::toPackageName)
+ .distinct()
+ .forEach(pn -> packageToUnnamedModule
+ .computeIfAbsent(pn, _n -> new ArrayList<>()).add(archive));
+ }
+
+ private String toPackageName(String name) {
+ int i = name.lastIndexOf('/');
+ return i > 0 ? name.replace('/', '.').substring(0, i) : "";
+ }
+
+ public Optional<Module> findModule(String name) {
+ Objects.requireNonNull(name);
+ Module m = nameToModule.get(name);
+ return m!= null ? Optional.of(m) : Optional.empty();
+
+ }
+
+ public Optional<ModuleDescriptor> findModuleDescriptor(String name) {
+ Objects.requireNonNull(name);
+ Module m = nameToModule.get(name);
+ return m!= null ? Optional.of(m.descriptor()) : Optional.empty();
+ }
+
+ boolean isSystem(Module m) {
+ return system.find(m.name()).isPresent();
+ }
+
+ /**
+ * Returns the modules that the given module can read
+ */
+ public Stream<Module> reads(Module module) {
+ return configuration.findModule(module.name()).get()
+ .reads().stream()
+ .map(ResolvedModule::name)
+ .map(nameToModule::get);
+ }
+
+ /**
+ * Returns the list of packages that split between resolved module and
+ * unnamed module
+ */
+ public Map<String, Set<String>> splitPackages() {
+ Set<String> splitPkgs = packageToModule.keySet().stream()
+ .filter(packageToUnnamedModule::containsKey)
+ .collect(toSet());
+ if (splitPkgs.isEmpty())
+ return Collections.emptyMap();
+
+ return splitPkgs.stream().collect(toMap(Function.identity(), (pn) -> {
+ Set<String> sources = new LinkedHashSet<>();
+ sources.add(packageToModule.get(pn).getModule().location().toString());
+ packageToUnnamedModule.get(pn).stream()
+ .map(Archive::getPathName)
+ .forEach(sources::add);
+ return sources;
+ }));
+ }
+
+ /**
+ * Returns an optional archive containing the given Location
+ */
+ public Optional<Archive> findClass(Dependency.Location location) {
+ String name = location.getName();
+ int i = name.lastIndexOf('/');
+ String pn = i > 0 ? name.substring(0, i).replace('/', '.') : "";
+ Archive archive = packageToModule.get(pn);
+ if (archive != null) {
+ return archive.contains(name + ".class")
+ ? Optional.of(archive)
+ : Optional.empty();
+ }
+
+ if (packageToUnnamedModule.containsKey(pn)) {
+ return packageToUnnamedModule.get(pn).stream()
+ .filter(a -> a.contains(name + ".class"))
+ .findFirst();
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the list of Modules that can be found in the specified
+ * module paths.
+ */
+ public Map<String, Module> getModules() {
+ return nameToModule;
+ }
+
+ public Stream<Module> resolve(Set<String> roots) {
+ if (roots.isEmpty()) {
+ return nameToModule.values().stream();
+ } else {
+ return Configuration.empty()
+ .resolveRequires(finder, ModuleFinder.of(), roots)
+ .modules().stream()
+ .map(ResolvedModule::name)
+ .map(nameToModule::get);
+ }
+ }
+
+ public List<Archive> classPathArchives() {
+ return classpathArchives;
+ }
+
+ public List<Archive> initialArchives() {
+ return initialArchives;
+ }
+
+ public Set<Module> rootModules() {
+ return rootModules;
+ }
+
+ public Module toModule(ModuleReference mref) {
+ try {
+ String mn = mref.descriptor().name();
+ URI location = mref.location().orElseThrow(FileNotFoundException::new);
+ ModuleDescriptor md = mref.descriptor();
+ Module.Builder builder = new Module.Builder(md, system.find(mn).isPresent());
+
+ final ClassFileReader reader;
+ if (location.getScheme().equals("jrt")) {
+ reader = system.getClassReader(mn);
+ } else {
+ reader = ClassFileReader.newInstance(Paths.get(location));
+ }
+
+ builder.classes(reader);
+ builder.location(location);
+
+ return builder.build();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ static class SystemModuleFinder implements ModuleFinder {
+ private static final String JAVA_SE = "java.se";
+
+ private final FileSystem fileSystem;
+ private final Path root;
+ private final Map<String, ModuleReference> systemModules;
+
+ SystemModuleFinder() {
+ this(System.getProperty("java.home"));
+ }
+ SystemModuleFinder(String javaHome) {
+ final FileSystem fs;
+ final Path root;
+ final Map<String, ModuleReference> systemModules;
+ if (javaHome != null) {
+ if (Files.isRegularFile(Paths.get(javaHome, "lib", "modules"))) {
+ try {
+ // jrt file system
+ fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+ root = fs.getPath("/modules");
+ systemModules = Files.walk(root, 1)
+ .filter(path -> !path.equals(root))
+ .map(this::toModuleReference)
+ .collect(toMap(mref -> mref.descriptor().name(),
+ Function.identity()));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ } else {
+ // exploded image
+ fs = FileSystems.getDefault();
+ root = Paths.get(javaHome, "modules");
+ systemModules = ModuleFinder.ofSystem().findAll().stream()
+ .collect(toMap(mref -> mref.descriptor().name(), Function.identity()));
+ }
+ } else {
+ fs = null;
+ root = null;
+ systemModules = Collections.emptyMap();
+ }
+ this.fileSystem = fs;
+ this.root = root;
+ this.systemModules = systemModules;
+ }
+
+ private ModuleReference toModuleReference(Path path) {
+ Path minfo = path.resolve(MODULE_INFO);
+ try (InputStream in = Files.newInputStream(minfo);
+ BufferedInputStream bin = new BufferedInputStream(in)) {
+
+ ModuleDescriptor descriptor = dropHashes(ModuleDescriptor.read(bin));
+ String mn = descriptor.name();
+ URI uri = URI.create("jrt:/" + path.getFileName().toString());
+ Supplier<ModuleReader> readerSupplier = new Supplier<>() {
+ @Override
+ public ModuleReader get() {
+ return new ModuleReader() {
+ @Override
+ public Optional<URI> find(String name) throws IOException {
+ return name.equals(mn)
+ ? Optional.of(uri) : Optional.empty();
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ };
+ }
+ };
+
+ return new ModuleReference(descriptor, uri, readerSupplier);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private ModuleDescriptor dropHashes(ModuleDescriptor md) {
+ ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(md.name());
+ md.requires().forEach(builder::requires);
+ md.exports().forEach(builder::exports);
+ md.provides().values().stream().forEach(builder::provides);
+ md.uses().stream().forEach(builder::uses);
+ builder.conceals(md.conceals());
+ return builder.build();
+ }
+
+ @Override
+ public Set<ModuleReference> findAll() {
+ return systemModules.values().stream().collect(toSet());
+ }
+
+ @Override
+ public Optional<ModuleReference> find(String mn) {
+ return systemModules.containsKey(mn)
+ ? Optional.of(systemModules.get(mn)) : Optional.empty();
+ }
+
+ public Stream<String> moduleNames() {
+ return systemModules.values().stream()
+ .map(mref -> mref.descriptor().name());
+ }
+
+ public ClassFileReader getClassReader(String modulename) throws IOException {
+ Path mp = root.resolve(modulename);
+ if (Files.exists(mp) && Files.isDirectory(mp)) {
+ return ClassFileReader.newInstance(fileSystem, mp);
+ } else {
+ throw new FileNotFoundException(mp.toString());
+ }
+ }
+
+ public Set<String> defaultSystemRoots() {
+ Set<String> roots = new HashSet<>();
+ boolean hasJava = false;
+ if (systemModules.containsKey(JAVA_SE)) {
+ // java.se is a system module
+ hasJava = true;
+ roots.add(JAVA_SE);
+ }
+
+ for (ModuleReference mref : systemModules.values()) {
+ String mn = mref.descriptor().name();
+ if (hasJava && mn.startsWith("java."))
+ continue;
+
+ // add as root if observable and exports at least one package
+ ModuleDescriptor descriptor = mref.descriptor();
+ for (ModuleDescriptor.Exports e : descriptor.exports()) {
+ if (!e.isQualified()) {
+ roots.add(mn);
+ break;
+ }
+ }
+ }
+ return roots;
+ }
+ }
+
+ public static class Builder {
+ // the token for "all modules on the module path"
+ private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
+
+ final SystemModuleFinder systemModulePath;
+ final Set<String> rootModules = new HashSet<>();
+ final List<Archive> initialArchives = new ArrayList<>();
+ final List<Path> paths = new ArrayList<>();
+ final List<Path> classPaths = new ArrayList<>();
+
+ ModuleFinder upgradeModulePath;
+ ModuleFinder appModulePath;
+ boolean addAllApplicationModules;
+
+ public Builder() {
+ this(System.getProperty("java.home"));
+ }
+
+ public Builder(String systemModulePath) {
+ this.systemModulePath = new SystemModuleFinder(systemModulePath);;
+ }
+
+ public Builder upgradeModulePath(String upgradeModulePath) {
+ this.upgradeModulePath = createModulePathFinder(upgradeModulePath);
+ return this;
+ }
+
+ public Builder appModulePath(String modulePath) {
+ this.appModulePath = createModulePathFinder(modulePath);
+ return this;
+ }
+
+ public Builder addmods(Set<String> addmods) {
+ for (String mn : addmods) {
+ switch (mn) {
+ case ALL_MODULE_PATH:
+ this.addAllApplicationModules = true;
+ break;
+ default:
+ this.rootModules.add(mn);
+ }
+ }
+ return this;
+ }
+
+ /*
+ * This method is for -check option to find all target modules specified
+ * in qualified exports.
+ *
+ * Include all system modules and modules found on modulepath
+ */
+ public Builder allModules() {
+ systemModulePath.moduleNames()
+ .forEach(this.rootModules::add);
+ this.addAllApplicationModules = true;
+ return this;
+ }
+
+ public Builder addRoot(Path path) {
+ Archive archive = Archive.getInstance(path);
+ if (archive.contains(MODULE_INFO)) {
+ paths.add(path);
+ } else {
+ initialArchives.add(archive);
+ }
+ return this;
+ }
+
+ public Builder addClassPath(String classPath) {
+ this.classPaths.addAll(getClassPaths(classPath));
+ return this;
+ }
+
+ public JdepsConfiguration build() throws IOException {
+ ModuleFinder finder = systemModulePath;
+ if (upgradeModulePath != null) {
+ finder = ModuleFinder.compose(upgradeModulePath, systemModulePath);
+ }
+ if (appModulePath != null) {
+ finder = ModuleFinder.compose(finder, appModulePath);
+ }
+ if (!paths.isEmpty()) {
+ ModuleFinder otherModulePath = ModuleFinder.of(paths.toArray(new Path[0]));
+
+ finder = ModuleFinder.compose(finder, otherModulePath);
+ // add modules specified on command-line (convenience) as root set
+ otherModulePath.findAll().stream()
+ .map(mref -> mref.descriptor().name())
+ .forEach(rootModules::add);
+ }
+ if (addAllApplicationModules && appModulePath != null) {
+ appModulePath.findAll().stream()
+ .map(mref -> mref.descriptor().name())
+ .forEach(rootModules::add);
+ }
+ return new JdepsConfiguration(systemModulePath,
+ finder,
+ rootModules,
+ classPaths,
+ initialArchives);
+ }
+
+ private static ModuleFinder createModulePathFinder(String mpaths) {
+ if (mpaths == null) {
+ return null;
+ } else {
+ String[] dirs = mpaths.split(File.pathSeparator);
+ Path[] paths = new Path[dirs.length];
+ int i = 0;
+ for (String dir : dirs) {
+ paths[i++] = Paths.get(dir);
+ }
+ return ModuleFinder.of(paths);
+ }
+ }
+
+ /*
+ * Returns the list of Archive specified in cpaths and not included
+ * initialArchives
+ */
+ private List<Path> getClassPaths(String cpaths) {
+ if (cpaths.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<Path> paths = new ArrayList<>();
+ for (String p : cpaths.split(File.pathSeparator)) {
+ if (p.length() > 0) {
+ // 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) {
+ paths.add(entry);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ } else {
+ paths.add(Paths.get(p));
+ }
+ }
+ }
+ return paths;
+ }
+ }
+
+}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java Thu May 19 10:55:33 2016 -0700
@@ -41,22 +41,27 @@
* 2. -filter:package to filter out same-package dependencies
* This filter is applied when jdeps parses the class files
* and filtered dependencies are not stored in the Analyzer.
- * 3. -module specifies to match target dependencies from the given module
+ * 3. -requires specifies to match target dependence from the given module
* This gets expanded into package lists to be filtered.
* 4. -filter:archive to filter out same-archive dependencies
* This filter is applied later in the Analyzer as the
* containing archive of a target class may not be known until
* the entire archive
*/
-class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
+public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
+
+ public static final JdepsFilter DEFAULT_FILTER =
+ new JdepsFilter.Builder().filter(true, true).build();
+
private final Dependency.Filter filter;
private final Pattern filterPattern;
private final boolean filterSamePackage;
private final boolean filterSameArchive;
private final boolean findJDKInternals;
private final Pattern includePattern;
- private final Set<String> includePackages;
- private final Set<String> excludeModules;
+ private final Pattern includeSystemModules;
+
+ private final Set<String> requires;
private JdepsFilter(Dependency.Filter filter,
Pattern filterPattern,
@@ -64,16 +69,16 @@
boolean filterSameArchive,
boolean findJDKInternals,
Pattern includePattern,
- Set<String> includePackages,
- Set<String> excludeModules) {
+ Pattern includeSystemModules,
+ Set<String> requires) {
this.filter = filter;
this.filterPattern = filterPattern;
this.filterSamePackage = filterSamePackage;
this.filterSameArchive = filterSameArchive;
this.findJDKInternals = findJDKInternals;
this.includePattern = includePattern;
- this.includePackages = includePackages;
- this.excludeModules = excludeModules;
+ this.includeSystemModules = includeSystemModules;
+ this.requires = requires;
}
/**
@@ -82,12 +87,7 @@
* @param cn fully-qualified name
*/
public boolean matches(String cn) {
- if (includePackages.isEmpty() && includePattern == null)
- return true;
-
- int i = cn.lastIndexOf('.');
- String pn = i > 0 ? cn.substring(0, i) : "";
- if (includePackages.contains(pn))
+ if (includePattern == null)
return true;
if (includePattern != null)
@@ -97,29 +97,39 @@
}
/**
- * Tests if the given source includes classes specified in includePattern
- * or includePackages filters.
+ * Tests if the given source includes classes specified in -include option
*
* This method can be used to determine if the given source should eagerly
* be processed.
*/
public boolean matches(Archive source) {
- if (!includePackages.isEmpty() && source.getModule().isNamed()) {
- boolean found = source.getModule().packages()
- .stream()
- .filter(pn -> includePackages.contains(pn))
- .findAny().isPresent();
- if (found)
- return true;
+ if (includePattern != null) {
+ return source.reader().entries().stream()
+ .map(name -> name.replace('/', '.'))
+ .filter(name -> !name.equals("module-info.class"))
+ .anyMatch(this::matches);
}
- if (!includePackages.isEmpty() || includePattern != null) {
- return source.reader().entries()
- .stream()
- .map(name -> name.replace('/', '.'))
- .filter(this::matches)
- .findAny().isPresent();
- }
- return false;
+ return hasTargetFilter();
+ }
+
+ public boolean include(Archive source) {
+ Module module = source.getModule();
+ // skip system module by default; or if includeSystemModules is set
+ // only include the ones matching the pattern
+ return !module.isSystem() || (includeSystemModules != null &&
+ includeSystemModules.matcher(module.name()).matches());
+ }
+
+ public boolean hasIncludePattern() {
+ return includePattern != null || includeSystemModules != null;
+ }
+
+ public boolean hasTargetFilter() {
+ return filter != null;
+ }
+
+ public Set<String> requiresFilter() {
+ return requires;
}
// ----- Dependency.Filter -----
@@ -164,42 +174,35 @@
return true;
}
- /**
- * Returns true if dependency should be recorded for the given source.
- */
- public boolean accept(Archive source) {
- return !excludeModules.contains(source.getName());
- }
-
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("exclude modules: ")
- .append(excludeModules.stream().sorted().collect(Collectors.joining(",")))
- .append("\n");
+ sb.append("include pattern: ").append(includePattern).append("\n");
sb.append("filter same archive: ").append(filterSameArchive).append("\n");
sb.append("filter same package: ").append(filterSamePackage).append("\n");
+ sb.append("requires: ").append(requires).append("\n");
return sb.toString();
}
- static class Builder {
- Dependency.Filter filter;
+ public static class Builder {
+ static Pattern SYSTEM_MODULE_PATTERN = Pattern.compile("java\\..*|jdk\\..*|javafx\\..*");
Pattern filterPattern;
+ Pattern regex;
boolean filterSamePackage;
boolean filterSameArchive;
boolean findJDKInterals;
// source filters
Pattern includePattern;
- Set<String> includePackages = new HashSet<>();
- Set<String> includeModules = new HashSet<>();
- Set<String> excludeModules = new HashSet<>();
+ Pattern includeSystemModules;
+ Set<String> requires = new HashSet<>();
+ Set<String> targetPackages = new HashSet<>();
public Builder packages(Set<String> packageNames) {
- this.filter = Dependencies.getPackageFilter(packageNames, false);
+ this.targetPackages.addAll(packageNames);
return this;
}
public Builder regex(Pattern regex) {
- this.filter = Dependencies.getRegexFilter(regex);
+ this.regex = regex;
return this;
}
public Builder filter(Pattern regex) {
@@ -211,6 +214,13 @@
this.filterSameArchive = sameArchive;
return this;
}
+ public Builder requires(String name, Set<String> packageNames) {
+ this.requires.add(name);
+ this.targetPackages.addAll(packageNames);
+
+ includeIfSystemModule(name);
+ return this;
+ }
public Builder findJDKInternals(boolean value) {
this.findJDKInterals = value;
return this;
@@ -219,30 +229,33 @@
this.includePattern = regex;
return this;
}
- public Builder includePackage(String pn) {
- this.includePackages.add(pn);
+ public Builder includeSystemModules(Pattern regex) {
+ this.includeSystemModules = regex;
return this;
}
- public Builder includeModules(Set<String> includes) {
- this.includeModules.addAll(includes);
- return this;
- }
- public Builder excludeModules(Set<String> excludes) {
- this.excludeModules.addAll(excludes);
+ public Builder includeIfSystemModule(String name) {
+ if (includeSystemModules == null &&
+ SYSTEM_MODULE_PATTERN.matcher(name).matches()) {
+ this.includeSystemModules = SYSTEM_MODULE_PATTERN;
+ }
return this;
}
- JdepsFilter build() {
+ public JdepsFilter build() {
+ Dependency.Filter filter = null;
+ if (regex != null)
+ filter = Dependencies.getRegexFilter(regex);
+ else if (!targetPackages.isEmpty()) {
+ filter = Dependencies.getPackageFilter(targetPackages, false);
+ }
return new JdepsFilter(filter,
filterPattern,
filterSamePackage,
filterSameArchive,
findJDKInterals,
includePattern,
- includePackages,
- excludeModules.stream()
- .filter(mn -> !includeModules.contains(mn))
- .collect(Collectors.toSet()));
+ includeSystemModules,
+ requires);
}
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java Thu May 19 10:55:33 2016 -0700
@@ -25,31 +25,26 @@
package com.sun.tools.jdeps;
+import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
import static com.sun.tools.jdeps.Analyzer.Type.*;
import static com.sun.tools.jdeps.JdepsWriter.*;
-import static com.sun.tools.jdeps.ModuleAnalyzer.Graph;
-import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.module.ResolutionException;
-import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
-import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
-import java.util.TreeMap;
+import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -58,7 +53,12 @@
* Implementation for the jdeps tool for static class dependency analysis.
*/
class JdepsTask {
- static class BadArgs extends Exception {
+ static interface BadArguments {
+ String getKey();
+ Object[] getArgs();
+ boolean showUsage();
+ }
+ static class BadArgs extends Exception implements BadArguments {
static final long serialVersionUID = 8765093759964640721L;
BadArgs(String key, Object... args) {
super(JdepsTask.getMessage(key, args));
@@ -73,6 +73,44 @@
final String key;
final Object[] args;
boolean showUsage;
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public Object[] getArgs() {
+ return args;
+ }
+
+ @Override
+ public boolean showUsage() {
+ return showUsage;
+ }
+ }
+
+ static class UncheckedBadArgs extends RuntimeException implements BadArguments {
+ static final long serialVersionUID = -1L;
+ final BadArgs cause;
+ UncheckedBadArgs(BadArgs cause) {
+ super(cause);
+ this.cause = cause;
+ }
+ @Override
+ public String getKey() {
+ return cause.key;
+ }
+
+ @Override
+ public Object[] getArgs() {
+ return cause.args;
+ }
+
+ @Override
+ public boolean showUsage() {
+ return cause.showUsage;
+ }
}
static abstract class Option {
@@ -126,7 +164,7 @@
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
throw new BadArgs("err.invalid.path", arg);
}
- task.options.dotOutputDir = arg;
+ task.options.dotOutputDir = Paths.get(arg);;
}
},
new Option(false, "-s", "-summary") {
@@ -136,8 +174,9 @@
}
},
new Option(false, "-v", "-verbose",
- "-verbose:package",
- "-verbose:class") {
+ "-verbose:module",
+ "-verbose:package",
+ "-verbose:class") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
switch (opt) {
case "-v":
@@ -146,6 +185,9 @@
task.options.filterSameArchive = false;
task.options.filterSamePackage = false;
break;
+ case "-verbose:module":
+ task.options.verbose = MODULE;
+ break;
case "-verbose:package":
task.options.verbose = PACKAGE;
break;
@@ -157,6 +199,61 @@
}
}
},
+ new Option(false, "-apionly") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.apiOnly = true;
+ }
+ },
+ new Option(true, "-check") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ Set<String> mods = Set.of(arg.split(","));
+ task.options.checkModuleDeps = mods;
+ task.options.addmods.addAll(mods);
+ }
+ },
+ new Option(true, "-genmoduleinfo") {
+ 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.invalid.path", arg);
+ }
+ task.options.genModuleInfo = Paths.get(arg);
+ }
+ },
+ new Option(false, "-jdkinternals") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.findJDKInternals = true;
+ task.options.verbose = CLASS;
+ if (task.options.includePattern == null) {
+ task.options.includePattern = Pattern.compile(".*");
+ }
+ }
+ },
+
+ // ---- paths option ----
+ new Option(true, "-cp", "-classpath") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.classpath = arg;
+ }
+ },
+ new Option(true, "-mp", "-modulepath") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ task.options.modulePath = arg;
+ }
+ },
+ new Option(true, "-upgrademodulepath") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ task.options.upgradeModulePath = arg;
+ }
+ },
+ new Option(true, "-m") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ task.options.rootModule = arg;
+ task.options.addmods.add(arg);
+ }
+ },
+
+ // ---- Target filtering options ----
new Option(true, "-p", "-package") {
void process(JdepsTask task, String opt, String arg) {
task.options.packageNames.add(arg);
@@ -198,26 +295,24 @@
}
}
},
+
+ // ---- Source filtering options ----
new Option(true, "-include") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
task.options.includePattern = Pattern.compile(arg);
}
},
+
+ // Another alternative to list modules in -addmods option
+ new HiddenOption(true, "-include-system-modules") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ task.options.includeSystemModulePattern = Pattern.compile(arg);
+ }
+ },
+
new Option(false, "-P", "-profile") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
task.options.showProfile = true;
- task.options.showModule = false;
- }
- },
- new Option(false, "-M") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- task.options.showModule = true;
- task.options.showProfile = false;
- }
- },
- new Option(false, "-apionly") {
- void process(JdepsTask task, String opt, String arg) {
- task.options.apiOnly = true;
}
},
new Option(false, "-R", "-recursive") {
@@ -228,15 +323,6 @@
task.options.filterSamePackage = false;
}
},
- new Option(true, "-genmoduleinfo") {
- 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.invalid.path", arg);
- }
- task.options.genModuleInfo = arg;
- }
- },
new Option(false, "-ct", "-compile-time") {
void process(JdepsTask task, String opt, String arg) {
task.options.compileTimeView = true;
@@ -245,62 +331,11 @@
task.options.depth = 0;
}
},
- new Option(false, "-jdkinternals") {
- void process(JdepsTask task, String opt, String arg) {
- task.options.findJDKInternals = true;
- task.options.verbose = CLASS;
- if (task.options.includePattern == null) {
- task.options.includePattern = Pattern.compile(".*");
- }
- }
- },
- new Option(true, "-cp", "-classpath") {
- void process(JdepsTask task, String opt, String arg) {
- task.options.classpath = arg;
- }
- },
- new Option(true, "-mp", "-modulepath") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- task.options.modulePath = arg;
- task.options.showModule = true;
- }
- },
- new Option(true, "-upgrademodulepath") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- task.options.upgradeModulePath = arg;
- task.options.showModule = true;
- }
- },
- new Option(true, "-m") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- task.options.rootModule = arg;
- task.options.includes.add(arg);
- task.options.showModule = true;
- }
- },
- new Option(false, "-check") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- task.options.checkModuleDeps = true;
- }
- },
- new HiddenOption(true, "-include-modules") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- Arrays.stream(arg.split(","))
- .forEach(task.options.includes::add);
- task.options.showModule = true;
- }
- },
- new HiddenOption(true, "-exclude-modules") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- Arrays.stream(arg.split(","))
- .forEach(task.options.excludes::add);
- task.options.showModule = true;
- }
- },
+
new Option(false, "-q", "-quiet") {
void process(JdepsTask task, String opt, String arg) {
- task.options.nowarning = true;
- }
+ task.options.nowarning = true;
+ }
},
new Option(false, "-version") {
@@ -318,7 +353,11 @@
task.options.showLabel = true;
}
},
-
+ new HiddenOption(false, "-hide-module") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.showModule = false;
+ }
+ },
new HiddenOption(true, "-depth") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
try {
@@ -332,7 +371,7 @@
private static final String PROGNAME = "jdeps";
private final Options options = new Options();
- private final List<String> classes = new ArrayList<>();
+ private final List<String> inputArgs = new ArrayList<>();
private PrintWriter log;
void setLog(PrintWriter out) {
@@ -342,13 +381,13 @@
/**
* 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
+ 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) {
+ int run(String... args) {
if (log == null) {
log = new PrintWriter(System.out);
}
@@ -360,15 +399,11 @@
if (options.version || options.fullVersion) {
showVersion(options.fullVersion);
}
- if (options.rootModule != null && !classes.isEmpty()) {
- reportError("err.invalid.module.option", options.rootModule, classes);
- return EXIT_CMDERR;
+ if (!inputArgs.isEmpty() && options.rootModule != null) {
+ reportError("err.invalid.arg.for.option", "-m");
}
- if (options.checkModuleDeps && options.rootModule == null) {
- reportError("err.root.module.not.set");
- return EXIT_CMDERR;
- }
- if (classes.isEmpty() && options.rootModule == null && options.includePattern == null) {
+ if (inputArgs.isEmpty() && options.addmods.isEmpty() && options.includePattern == null
+ && options.includeSystemModulePattern == null && options.checkModuleDeps == null) {
if (options.help || options.version || options.fullVersion) {
return EXIT_OK;
} else {
@@ -377,19 +412,10 @@
}
}
if (options.genModuleInfo != null) {
- if (options.dotOutputDir != null || !options.classpath.isEmpty() || options.hasFilter()) {
+ if (options.dotOutputDir != null || options.classpath != null || options.hasFilter()) {
showHelp();
return EXIT_CMDERR;
}
- // default to compile time view analysis
- options.compileTimeView = true;
- for (String fn : classes) {
- Path p = Paths.get(fn);
- if (!Files.exists(p) || !fn.endsWith(".jar")) {
- reportError("err.genmoduleinfo.not.jarfile", fn);
- return EXIT_CMDERR;
- }
- }
}
if (options.numFilters() > 1) {
@@ -405,12 +431,15 @@
showHelp();
return EXIT_CMDERR;
}
+ if (options.checkModuleDeps != null && !inputArgs.isEmpty()) {
+ reportError("err.invalid.module.option", inputArgs, "-check");
+ }
boolean ok = run();
return ok ? EXIT_OK : EXIT_ERROR;
- } catch (BadArgs e) {
- reportError(e.key, e.args);
- if (e.showUsage) {
+ } catch (BadArgs|UncheckedBadArgs e) {
+ reportError(e.getKey(), e.getArgs());
+ if (e.showUsage()) {
log.println(getMessage("main.usage.summary", PROGNAME));
}
return EXIT_CMDERR;
@@ -419,176 +448,178 @@
return EXIT_CMDERR;
} catch (IOException e) {
e.printStackTrace();
- return EXIT_ABNORMAL;
+ return EXIT_CMDERR;
} finally {
log.flush();
}
}
- private ModulePaths modulePaths;
- private boolean run() throws BadArgs, IOException {
- DependencyFinder dependencyFinder =
- new DependencyFinder(options.compileTimeView);
+ boolean run() throws IOException {
+ JdepsConfiguration config = buildConfig();
- buildArchive(dependencyFinder);
+ // detect split packages
+ config.splitPackages().entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(e -> System.out.format("split package: %s %s%n", e.getKey(),
+ e.getValue().toString()));
- if (options.rootModule != null &&
- (options.checkModuleDeps || (options.dotOutputDir != null &&
- options.verbose == SUMMARY))) {
- // -dotfile -s prints the configuration of the given root
- // -checkModuleDeps prints the suggested module-info.java
- return analyzeModules(dependencyFinder);
+ // check if any module specified in -module is missing
+ Stream.concat(options.addmods.stream(), options.requires.stream())
+ .forEach(mn -> config.findModule(mn).orElseThrow(() ->
+ new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
+
+ // -genmoduleinfo
+ if (options.genModuleInfo != null) {
+ return genModuleInfo(config);
}
- // otherwise analyze the dependencies
- if (options.genModuleInfo != null) {
- return genModuleInfo(dependencyFinder);
- } else {
- return analyzeDeps(dependencyFinder);
+ // -check
+ if (options.checkModuleDeps != null) {
+ return new ModuleAnalyzer(config, log, options.checkModuleDeps).run();
}
+
+ if (options.dotOutputDir != null &&
+ (options.verbose == SUMMARY || options.verbose == MODULE) &&
+ !options.addmods.isEmpty() && inputArgs.isEmpty()) {
+ return new ModuleAnalyzer(config, log).genDotFiles(options.dotOutputDir);
+ }
+
+ return analyzeDeps(config);
}
- private void buildArchive(DependencyFinder dependencyFinder)
- throws BadArgs, IOException
- {
- // If -genmoduleinfo is specified, the input arguments must be JAR files
- // Treat them as automatic modules for analysis
- List<Path> jarfiles = options.genModuleInfo != null
- ? classes.stream().map(Paths::get)
- .collect(Collectors.toList())
- : Collections.emptyList();
- // Set module paths
- this.modulePaths = new ModulePaths(options.upgradeModulePath, options.modulePath, jarfiles);
+ private JdepsConfiguration buildConfig() throws IOException {
+ JdepsConfiguration.Builder builder =
+ new JdepsConfiguration.Builder(options.systemModulePath);
- // add modules to dependency finder for analysis
- Map<String, Module> modules = modulePaths.getModules();
- modules.values().stream()
- .forEach(dependencyFinder::addModule);
+ builder.upgradeModulePath(options.upgradeModulePath)
+ .appModulePath(options.modulePath)
+ .addmods(options.addmods);
- // If -m option is set, add the specified module and its transitive dependences
- // to the root set
- if (options.rootModule != null) {
- modulePaths.dependences(options.rootModule)
- .forEach(dependencyFinder::addRoot);
+ if (options.checkModuleDeps != null) {
+ // check all system modules in the image
+ builder.allModules();
}
- // check if any module specified in -requires is missing
- Optional<String> req = options.requires.stream()
- .filter(mn -> !modules.containsKey(mn))
- .findFirst();
- if (req.isPresent()) {
- throw new BadArgs("err.module.not.found", req.get());
- }
+ if (options.classpath != null)
+ builder.addClassPath(options.classpath);
- // classpath
- for (Path p : getClassPaths(options.classpath)) {
+ // build the root set of archives to be analyzed
+ for (String s : inputArgs) {
+ Path p = Paths.get(s);
if (Files.exists(p)) {
- dependencyFinder.addClassPathArchive(p);
+ builder.addRoot(p);
}
}
- // if -genmoduleinfo is not set, the input arguments are considered as
- // unnamed module. Add them to the root set
- if (options.genModuleInfo == null) {
- // add root set
- for (String s : classes) {
- Path p = Paths.get(s);
- if (Files.exists(p)) {
- // add to the initial root set
- dependencyFinder.addRoot(p);
- } else {
- if (isValidClassName(s)) {
- dependencyFinder.addClassName(s);
- } else {
- warning("warn.invalid.arg", s);
- }
- }
- }
- }
+ return builder.build();
}
- private boolean analyzeDeps(DependencyFinder dependencyFinder) throws IOException {
- JdepsFilter filter = dependencyFilter();
-
- // parse classfiles and find all dependencies
- findDependencies(dependencyFinder, filter, options.apiOnly);
-
- // analyze the dependencies collected
- Analyzer analyzer = new Analyzer(options.verbose, filter);
- analyzer.run(dependencyFinder.archives());
-
+ private boolean analyzeDeps(JdepsConfiguration config) throws IOException {
// output result
final JdepsWriter writer;
if (options.dotOutputDir != null) {
- Path dir = Paths.get(options.dotOutputDir);
- Files.createDirectories(dir);
- writer = new DotFileWriter(dir, options.verbose,
+ writer = new DotFileWriter(options.dotOutputDir,
+ options.verbose,
options.showProfile,
options.showModule,
options.showLabel);
} else {
- writer = new SimpleWriter(log, options.verbose,
+ writer = new SimpleWriter(log,
+ options.verbose,
options.showProfile,
options.showModule);
}
- // Targets for reporting - include the root sets and other analyzed archives
- final List<Archive> targets;
- if (options.rootModule == null) {
- // no module as the root set
- targets = dependencyFinder.archives()
- .filter(filter::accept)
- .filter(a -> !a.getModule().isNamed())
- .collect(Collectors.toList());
- } else {
- // named modules in topological order
- Stream<Module> modules = dependencyFinder.archives()
- .filter(a -> a.getModule().isNamed())
- .map(Archive::getModule);
- Graph<Module> graph = ModuleAnalyzer.graph(modulePaths, modules.toArray(Module[]::new));
- // then add unnamed module
- targets = graph.orderedNodes()
- .filter(filter::accept)
- .collect(Collectors.toList());
+ // analyze the dependencies
+ DepsAnalyzer analyzer = new DepsAnalyzer(config,
+ dependencyFilter(config),
+ writer,
+ options.verbose,
+ options.apiOnly);
+
+ boolean ok = analyzer.run(options.compileTimeView, options.depth);
+
+ // print skipped entries, if any
+ analyzer.analyzer.archives()
+ .forEach(archive -> archive.reader()
+ .skippedEntries().stream()
+ .forEach(name -> warning("warn.skipped.entry",
+ name, archive.getPathName())));
- // in case any reference not found
- dependencyFinder.archives()
- .filter(a -> !a.getModule().isNamed())
- .forEach(targets::add);
- }
+ if (options.findJDKInternals && !options.nowarning) {
+ Map<String, String> jdkInternals = analyzer.dependences()
+ .collect(Collectors.toMap(Function.identity(), this::replacementFor));
+
+ if (!jdkInternals.isEmpty()) {
+ log.println();
+ warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
- writer.generateOutput(targets, analyzer);
- if (options.findJDKInternals && !options.nowarning) {
- showReplacements(targets, analyzer);
+ if (jdkInternals.values().stream().anyMatch(repl -> repl != null)) {
+ log.println();
+ log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
+ log.format("%-40s %s%n", "----------------", "---------------------");
+ jdkInternals.entrySet().stream()
+ .filter(e -> e.getValue() != null)
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(e -> log.format("%-40s %s%n", e.getKey(), e.getValue()));
+ }
+ }
+
}
- return true;
+ return ok;
}
- private JdepsFilter dependencyFilter() {
+ private boolean genModuleInfo(JdepsConfiguration config) throws IOException {
+ ModuleInfoBuilder builder
+ = new ModuleInfoBuilder(config, inputArgs, options.genModuleInfo);
+ boolean ok = builder.run();
+
+ builder.modules().forEach(module -> {
+ if (module.packages().contains("")) {
+ reportError("ERROR: %s contains unnamed package. " +
+ "module-info.java not generated%n", module.getPathName());
+ }
+ });
+
+ if (!ok && !options.nowarning) {
+ log.println("Missing dependencies");
+ builder.visitMissingDeps(
+ new Analyzer.Visitor() {
+ @Override
+ public void visitDependence(String origin, Archive originArchive,
+ String target, Archive targetArchive) {
+ if (targetArchive == NOT_FOUND)
+ log.format(" %-50s -> %-50s %s%n",
+ origin, target, targetArchive.getName());
+ }
+ });
+
+ log.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
+ }
+ return ok;
+ }
+
+ /**
+ * Returns a filter used during dependency analysis
+ */
+ private JdepsFilter dependencyFilter(JdepsConfiguration config) {
// Filter specified by -filter, -package, -regex, and -module options
JdepsFilter.Builder builder = new JdepsFilter.Builder();
- // Exclude JDK modules from analysis and reporting if -m specified.
- modulePaths.getModules().values().stream()
- .filter(m -> m.isJDK())
- .map(Module::name)
- .forEach(options.excludes::add);
-
// source filters
builder.includePattern(options.includePattern);
- builder.includeModules(options.includes);
- builder.excludeModules(options.excludes);
+ builder.includeSystemModules(options.includeSystemModulePattern);
builder.filter(options.filterSamePackage, options.filterSameArchive);
builder.findJDKInternals(options.findJDKInternals);
// -module
if (!options.requires.isEmpty()) {
- Map<String, Module> modules = modulePaths.getModules();
- builder.packages(options.requires.stream()
- .map(modules::get)
- .flatMap(m -> m.packages().stream())
- .collect(Collectors.toSet()));
+ options.requires.stream()
+ .forEach(mn -> {
+ Module m = config.findModule(mn).get();
+ builder.requires(mn, m.packages());
+ });
}
// -regex
if (options.regex != null)
@@ -600,60 +631,12 @@
if (options.filterRegex != null)
builder.filter(options.filterRegex);
- return builder.build();
- }
-
- private void findDependencies(DependencyFinder dependencyFinder,
- JdepsFilter filter,
- boolean apiOnly)
- throws IOException
- {
- dependencyFinder.findDependencies(filter, apiOnly, options.depth);
-
- // print skipped entries, if any
- for (Archive a : dependencyFinder.roots()) {
- for (String name : a.reader().skippedEntries()) {
- warning("warn.skipped.entry", name, a.getPathName());
- }
- }
- }
-
- private boolean genModuleInfo(DependencyFinder dependencyFinder) throws IOException {
- ModuleInfoBuilder builder = new ModuleInfoBuilder(modulePaths, dependencyFinder);
- boolean result = builder.run(options.verbose, options.nowarning);
- builder.build(Paths.get(options.genModuleInfo));
- return result;
- }
+ // check if system module is set
+ config.rootModules().stream()
+ .map(Module::name)
+ .forEach(builder::includeIfSystemModule);
- private boolean analyzeModules(DependencyFinder dependencyFinder)
- throws IOException
- {
- ModuleAnalyzer analyzer = new ModuleAnalyzer(modulePaths,
- dependencyFinder,
- options.rootModule);
- if (options.checkModuleDeps) {
- return analyzer.run();
- }
- if (options.dotOutputDir != null && options.verbose == SUMMARY) {
- Path dir = Paths.get(options.dotOutputDir);
- Files.createDirectories(dir);
- analyzer.genDotFile(dir);
- return true;
- }
- return false;
- }
-
- 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;
+ return builder.build();
}
public void handleOptions(String[] args) throws BadArgs {
@@ -684,7 +667,7 @@
if (name.charAt(0) == '-') {
throw new BadArgs("err.option.after.class", name).showUsage(true);
}
- classes.add(name);
+ inputArgs.add(name);
}
}
}
@@ -703,7 +686,7 @@
log.println(getMessage("error.prefix") + " " + getMessage(key, args));
}
- private void warning(String key, Object... args) {
+ void warning(String key, Object... args) {
log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
}
@@ -749,7 +732,7 @@
boolean version;
boolean fullVersion;
boolean showProfile;
- boolean showModule;
+ boolean showModule = true;
boolean showSummary;
boolean apiOnly;
boolean showLabel;
@@ -761,22 +744,22 @@
boolean filterSamePackage = true;
boolean filterSameArchive = false;
Pattern filterRegex;
- String dotOutputDir;
- String genModuleInfo;
- String classpath = "";
+ Path dotOutputDir;
+ Path genModuleInfo;
+ String classpath;
int depth = 1;
Set<String> requires = new HashSet<>();
Set<String> packageNames = new HashSet<>();
Pattern regex; // apply to the dependences
- Pattern includePattern; // apply to classes
+ Pattern includePattern;
+ Pattern includeSystemModulePattern;
boolean compileTimeView = false;
- boolean checkModuleDeps = false;
+ Set<String> checkModuleDeps;
+ String systemModulePath = System.getProperty("java.home");
String upgradeModulePath;
String modulePath;
String rootModule;
- // modules to be included or excluded
- Set<String> includes = new HashSet<>();
- Set<String> excludes = new HashSet<>();
+ Set<String> addmods = new HashSet<>();
boolean hasFilter() {
return numFilters() > 0;
@@ -789,11 +772,8 @@
if (packageNames.size() > 0) count++;
return count;
}
+ }
- boolean isRootModule() {
- return rootModule != null;
- }
- }
private static class ResourceBundleHelper {
static final ResourceBundle versionRB;
static final ResourceBundle bundle;
@@ -819,35 +799,6 @@
}
}
- /*
- * Returns the list of Archive specified in cpaths and not included
- * initialArchives
- */
- private List<Path> getClassPaths(String cpaths) throws IOException
- {
- if (cpaths.isEmpty()) {
- return Collections.emptyList();
- }
- List<Path> paths = new ArrayList<>();
- for (String p : cpaths.split(File.pathSeparator)) {
- if (p.length() > 0) {
- // 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) {
- paths.add(entry);
- }
- }
- } else {
- paths.add(Paths.get(p));
- }
- }
- }
- return paths;
- }
-
/**
* Returns the recommended replacement API for the given classname;
* or return null if replacement API is not known.
@@ -865,32 +816,5 @@
}
}
return value;
- };
-
- private void showReplacements(List<Archive> archives, Analyzer analyzer) {
- Map<String,String> jdkinternals = new TreeMap<>();
- boolean useInternals = false;
- for (Archive source : archives) {
- useInternals = useInternals || analyzer.hasDependences(source);
- for (String cn : analyzer.dependences(source)) {
- String repl = replacementFor(cn);
- if (repl != null) {
- jdkinternals.putIfAbsent(cn, repl);
- }
- }
- }
- if (useInternals) {
- log.println();
- warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
- }
- if (!jdkinternals.isEmpty()) {
- log.println();
- log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
- log.format("%-40s %s%n", "----------------", "---------------------");
- for (Map.Entry<String,String> e : jdkinternals.entrySet()) {
- log.format("%-40s %s%n", e.getKey(), e.getValue());
- }
- }
}
-
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsWriter.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsWriter.java Thu May 19 10:55:33 2016 -0700
@@ -24,23 +24,33 @@
*/
package com.sun.tools.jdeps;
+import static com.sun.tools.jdeps.Analyzer.Type.*;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor.Requires;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
-import static com.sun.tools.jdeps.Analyzer.Type.*;
+public abstract class JdepsWriter {
+ public static JdepsWriter newDotWriter(Path outputdir, Analyzer.Type type) {
+ return new DotFileWriter(outputdir, type, false, true, false);
+ }
-public abstract class JdepsWriter {
+ public static JdepsWriter newSimpleWriter(PrintWriter writer, Analyzer.Type type) {
+ return new SimpleWriter(writer, type, false, true);
+ }
+
final Analyzer.Type type;
final boolean showProfile;
final boolean showModule;
- JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
+ private JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
this.type = type;
this.showProfile = showProfile;
this.showModule = showModule;
@@ -48,7 +58,7 @@
abstract void generateOutput(Collection<Archive> archives, Analyzer analyzer) throws IOException;
- public static class DotFileWriter extends JdepsWriter {
+ static class DotFileWriter extends JdepsWriter {
final boolean showLabel;
final Path outputDir;
DotFileWriter(Path dir, Analyzer.Type type,
@@ -62,8 +72,10 @@
void generateOutput(Collection<Archive> archives, Analyzer analyzer)
throws IOException
{
+ Files.createDirectories(outputDir);
+
// output individual .dot file for each archive
- if (type != SUMMARY) {
+ if (type != SUMMARY && type != MODULE) {
archives.stream()
.filter(analyzer::hasDependences)
.forEach(archive -> {
@@ -85,13 +97,13 @@
{
// If verbose mode (-v or -verbose option),
// the summary.dot file shows package-level dependencies.
- Analyzer.Type summaryType =
- (type == PACKAGE || type == SUMMARY) ? SUMMARY : PACKAGE;
+ boolean isSummary = type == PACKAGE || type == SUMMARY || type == MODULE;
+ Analyzer.Type summaryType = isSummary ? SUMMARY : PACKAGE;
Path summary = outputDir.resolve("summary.dot");
try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) {
for (Archive archive : archives) {
- if (type == PACKAGE || type == SUMMARY) {
+ if (isSummary) {
if (showLabel) {
// build labels listing package-level dependencies
analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE);
@@ -208,19 +220,22 @@
void generateOutput(Collection<Archive> archives, Analyzer analyzer) {
RawOutputFormatter depFormatter = new RawOutputFormatter(writer);
RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer);
- for (Archive archive : archives) {
- // print summary
- if (showModule && archive.getModule().isNamed()) {
- summaryFormatter.showModuleRequires(archive.getModule());
- } else {
+ archives.stream()
+ .filter(analyzer::hasDependences)
+ .sorted(Comparator.comparing(Archive::getName))
+ .forEach(archive -> {
+ if (showModule && archive.getModule().isNamed() && type != SUMMARY) {
+ // print module-info except -summary
+ summaryFormatter.printModuleDescriptor(archive.getModule());
+ }
+ // print summary
analyzer.visitDependences(archive, summaryFormatter, SUMMARY);
- }
- if (analyzer.hasDependences(archive) && type != SUMMARY) {
- // print the class-level or package-level dependences
- analyzer.visitDependences(archive, depFormatter);
- }
- }
+ if (analyzer.hasDependences(archive) && type != SUMMARY) {
+ // print the class-level or package-level dependences
+ analyzer.visitDependences(archive, depFormatter);
+ }
+ });
}
class RawOutputFormatter implements Analyzer.Visitor {
@@ -269,20 +284,16 @@
writer.format("%n");
}
- public void showModuleRequires(Module module) {
+ public void printModuleDescriptor(Module module) {
if (!module.isNamed())
return;
- writer.format("module %s", module.name());
- if (module.isAutomatic())
- writer.format(" (automatic)");
- writer.println();
- module.requires().keySet()
+ writer.format("%s%s%n", module.name(), module.isAutomatic() ? " automatic" : "");
+ writer.format(" [%s]%n", module.location());
+ module.descriptor().requires()
.stream()
- .sorted()
- .forEach(req -> writer.format(" requires %s%s%n",
- module.requires.get(req) ? "public " : "",
- req));
+ .sorted(Comparator.comparing(Requires::name))
+ .forEach(req -> writer.format(" requires %s%n", req));
}
}
}
@@ -307,7 +318,7 @@
}
// exported API
- boolean jdkunsupported = Module.isJDKUnsupported(module, pn);
+ boolean jdkunsupported = Module.JDK_UNSUPPORTED.equals(module.name());
if (module.isExported(pn) && !jdkunsupported) {
return showProfileOrModule(module);
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Module.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Module.java Thu May 19 10:55:33 2016 -0700
@@ -25,67 +25,56 @@
package com.sun.tools.jdeps;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UncheckedIOException;
import java.lang.module.ModuleDescriptor;
import java.net.URI;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Collectors;
/**
- * JDeps internal representation of module for dependency analysis.
+ * Jdeps internal representation of module for dependency analysis.
*/
class Module extends Archive {
- static final boolean traceOn = Boolean.getBoolean("jdeps.debug");
+ static final Module UNNAMED_MODULE = new UnnamedModule();
+ static final String JDK_UNSUPPORTED = "jdk.unsupported";
+
+ static final boolean DEBUG = Boolean.getBoolean("jdeps.debug");
static void trace(String fmt, Object... args) {
+ trace(DEBUG, fmt, args);
+ }
+
+ static void trace(boolean traceOn, String fmt, Object... args) {
if (traceOn) {
System.err.format(fmt, args);
}
}
- /*
- * Returns true if the given package name is JDK critical internal API
- * in jdk.unsupported module
- */
- static boolean isJDKUnsupported(Module m, String pn) {
- return JDK_UNSUPPORTED.equals(m.name()) || unsupported.contains(pn);
- };
+ private final ModuleDescriptor descriptor;
+ private final Map<String, Set<String>> exports;
+ private final boolean isSystem;
+ private final URI location;
- protected final ModuleDescriptor descriptor;
- protected final Map<String, Boolean> requires;
- protected final Map<String, Set<String>> exports;
- protected final Set<String> packages;
- protected final boolean isJDK;
- protected final URI location;
+ protected Module(String name) {
+ super(name);
+ this.descriptor = null;
+ this.location = null;
+ this.exports = Collections.emptyMap();
+ this.isSystem = true;
+ }
private Module(String name,
URI location,
ModuleDescriptor descriptor,
- Map<String, Boolean> requires,
Map<String, Set<String>> exports,
- Set<String> packages,
- boolean isJDK,
+ boolean isSystem,
ClassFileReader reader) {
super(name, location, reader);
this.descriptor = descriptor;
this.location = location;
- this.requires = Collections.unmodifiableMap(requires);
this.exports = Collections.unmodifiableMap(exports);
- this.packages = Collections.unmodifiableSet(packages);
- this.isJDK = isJDK;
+ this.isSystem = isSystem;
}
/**
@@ -111,31 +100,35 @@
return descriptor;
}
- public boolean isJDK() {
- return isJDK;
+ public URI location() {
+ return location;
}
- public Map<String, Boolean> requires() {
- return requires;
+ public boolean isJDK() {
+ String mn = name();
+ return isSystem &&
+ (mn.startsWith("java.") || mn.startsWith("jdk.") || mn.startsWith("javafx."));
+ }
+
+ public boolean isSystem() {
+ return isSystem;
}
public Map<String, Set<String>> exports() {
return exports;
}
- public Map<String, Set<String>> provides() {
- return descriptor.provides().entrySet().stream()
- .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().providers()));
- }
-
public Set<String> packages() {
- return packages;
+ return descriptor.packages();
}
/**
* Tests if the package of the given name is exported.
*/
public boolean isExported(String pn) {
+ if (JDK_UNSUPPORTED.equals(this.name())) {
+ return false;
+ }
return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false;
}
@@ -159,11 +152,6 @@
return isExported(pn) || exports.containsKey(pn) && exports.get(pn).contains(target);
}
- private final static String JDK_UNSUPPORTED = "jdk.unsupported";
-
- // temporary until jdk.unsupported module
- private final static List<String> unsupported = Arrays.asList("sun.misc", "sun.reflect");
-
@Override
public String toString() {
return name();
@@ -171,21 +159,19 @@
public final static class Builder {
final String name;
- final Map<String, Boolean> requires = new HashMap<>();
- final Map<String, Set<String>> exports = new HashMap<>();
- final Set<String> packages = new HashSet<>();
- final boolean isJDK;
+ final ModuleDescriptor descriptor;
+ final boolean isSystem;
ClassFileReader reader;
- ModuleDescriptor descriptor;
URI location;
- public Builder(String name) {
- this(name, false);
+ public Builder(ModuleDescriptor md) {
+ this(md, false);
}
- public Builder(String name, boolean isJDK) {
- this.name = name;
- this.isJDK = isJDK;
+ public Builder(ModuleDescriptor md, boolean isSystem) {
+ this.name = md.name();
+ this.descriptor = md;
+ this.isSystem = isSystem;
}
public Builder location(URI location) {
@@ -193,48 +179,30 @@
return this;
}
- public Builder descriptor(ModuleDescriptor md) {
- this.descriptor = md;
- return this;
- }
-
- public Builder require(String d, boolean reexport) {
- requires.put(d, reexport);
- return this;
- }
-
- public Builder packages(Set<String> pkgs) {
- packages.addAll(pkgs);
- return this;
- }
-
- public Builder export(String p, Set<String> ms) {
- Objects.requireNonNull(p);
- Objects.requireNonNull(ms);
- exports.put(p, new HashSet<>(ms));
- return this;
- }
public Builder classes(ClassFileReader reader) {
this.reader = reader;
return this;
}
public Module build() {
- if (descriptor.isAutomatic() && isJDK) {
+ if (descriptor.isAutomatic() && isSystem) {
throw new InternalError("JDK module: " + name + " can't be automatic module");
}
- return new Module(name, location, descriptor, requires, exports, packages, isJDK, reader);
+ Map<String, Set<String>> exports = new HashMap<>();
+
+ descriptor.exports().stream()
+ .forEach(exp -> exports.computeIfAbsent(exp.source(), _k -> new HashSet<>())
+ .addAll(exp.targets()));
+
+ return new Module(name, location, descriptor, exports, isSystem, reader);
}
}
- final static Module UNNAMED_MODULE = new UnnamedModule();
private static class UnnamedModule extends Module {
private UnnamedModule() {
super("unnamed", null, null,
Collections.emptyMap(),
- Collections.emptyMap(),
- Collections.emptySet(),
false, null);
}
@@ -260,10 +228,7 @@
}
private static class StrictModule extends Module {
- private static final String SERVICES_PREFIX = "META-INF/services/";
- private final Map<String, Set<String>> provides;
- private final Module module;
- private final JarFile jarfile;
+ private final ModuleDescriptor md;
/**
* Converts the given automatic module to a strict module.
@@ -272,114 +237,26 @@
* declare service providers, if specified in META-INF/services configuration file
*/
private StrictModule(Module m, Map<String, Boolean> requires) {
- super(m.name(), m.location, m.descriptor, requires, m.exports, m.packages, m.isJDK, m.reader());
- this.module = m;
- try {
- this.jarfile = new JarFile(m.path().toFile(), false);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- this.provides = providers(jarfile);
+ super(m.name(), m.location, m.descriptor, m.exports, m.isSystem, m.reader());
+
+ ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(m.name());
+ requires.keySet().forEach(mn -> {
+ if (requires.get(mn).equals(Boolean.TRUE)) {
+ builder.requires(ModuleDescriptor.Requires.Modifier.PUBLIC, mn);
+ } else {
+ builder.requires(mn);
+ }
+ });
+ m.descriptor.exports().forEach(e -> builder.exports(e));
+ m.descriptor.uses().forEach(s -> builder.uses(s));
+ m.descriptor.provides().values().forEach(p -> builder.provides(p));
+ builder.conceals(m.descriptor.conceals());
+ this.md = builder.build();
}
@Override
- public Map<String, Set<String>> provides() {
- return provides;
- }
-
- private Map<String, Set<String>> providers(JarFile jf) {
- Map<String, Set<String>> provides = new HashMap<>();
- // map names of service configuration files to service names
- Set<String> serviceNames = jf.stream()
- .map(e -> e.getName())
- .filter(e -> e.startsWith(SERVICES_PREFIX))
- .distinct()
- .map(this::toServiceName)
- .filter(Optional::isPresent)
- .map(Optional::get)
- .collect(Collectors.toSet());
-
- // parse each service configuration file
- for (String sn : serviceNames) {
- JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
- Set<String> providerClasses = new HashSet<>();
- try (InputStream in = jf.getInputStream(entry)) {
- BufferedReader reader
- = new BufferedReader(new InputStreamReader(in, "UTF-8"));
- String cn;
- while ((cn = nextLine(reader)) != null) {
- if (isJavaIdentifier(cn)) {
- providerClasses.add(cn);
- }
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- if (!providerClasses.isEmpty())
- provides.put(sn, providerClasses);
- }
-
- return provides;
- }
-
- /**
- * Returns a container with the service type corresponding to the name of
- * a services configuration file.
- *
- * For example, if called with "META-INF/services/p.S" then this method
- * returns a container with the value "p.S".
- */
- private Optional<String> toServiceName(String cf) {
- assert cf.startsWith(SERVICES_PREFIX);
- int index = cf.lastIndexOf("/") + 1;
- if (index < cf.length()) {
- String prefix = cf.substring(0, index);
- if (prefix.equals(SERVICES_PREFIX)) {
- String sn = cf.substring(index);
- if (isJavaIdentifier(sn))
- return Optional.of(sn);
- }
- }
- return Optional.empty();
- }
-
- /**
- * Reads the next line from the given reader and trims it of comments and
- * leading/trailing white space.
- *
- * Returns null if the reader is at EOF.
- */
- private String nextLine(BufferedReader reader) throws IOException {
- String ln = reader.readLine();
- if (ln != null) {
- int ci = ln.indexOf('#');
- if (ci >= 0)
- ln = ln.substring(0, ci);
- ln = ln.trim();
- }
- return ln;
- }
-
- /**
- * Returns {@code true} if the given identifier is a legal Java identifier.
- */
- private static boolean isJavaIdentifier(String id) {
- int n = id.length();
- if (n == 0)
- return false;
- if (!Character.isJavaIdentifierStart(id.codePointAt(0)))
- return false;
- int cp = id.codePointAt(0);
- int i = Character.charCount(cp);
- for (; i < n; i += Character.charCount(cp)) {
- cp = id.codePointAt(i);
- if (!Character.isJavaIdentifierPart(cp) && id.charAt(i) != '.')
- return false;
- }
- if (cp == '.')
- return false;
-
- return true;
+ public ModuleDescriptor descriptor() {
+ return md;
}
}
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java Thu May 19 10:55:33 2016 -0700
@@ -24,277 +24,364 @@
*/
package com.sun.tools.jdeps;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.UncheckedIOException;
-
+import static com.sun.tools.jdeps.Graph.*;
+import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
+import static com.sun.tools.jdeps.Module.*;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import static java.util.stream.Collectors.*;
-import java.lang.module.Configuration;
+import com.sun.tools.classfile.Dependency;
+import com.sun.tools.jdeps.JdepsTask.BadArgs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReference;
-import java.lang.module.ResolvedModule;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
-import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.sun.tools.jdeps.Module.*;
-import static com.sun.tools.jdeps.ModulePaths.SystemModulePath.JAVA_BASE;
-
/**
* Analyze module dependences and compare with module descriptor.
* Also identify any qualified exports not used by the target module.
*/
-class ModuleAnalyzer {
- private final ModulePaths modulePaths;
+public class ModuleAnalyzer {
+ private static final String JAVA_BASE = "java.base";
+
+ private final JdepsConfiguration configuration;
+ private final PrintWriter log;
+
private final DependencyFinder dependencyFinder;
- private final Module root;
- private final Set<Module> modules;
- private final Set<String> requiresPublic = new HashSet<>();
- private final Set<String> requires = new HashSet<>();
- private final Set<Module> exportTargets = new HashSet<>();
- private final JdepsFilter filter;
- private Graph<Module> graph;
- ModuleAnalyzer(ModulePaths modulePaths, DependencyFinder finder,
- String moduleName) {
- this.modulePaths = modulePaths;
- this.dependencyFinder = finder;
- this.root = modulePaths.getModules().get(moduleName);
- this.modules = modulePaths.dependences(moduleName);
+ private final Map<Module, ModuleDeps> modules;
+
+ public ModuleAnalyzer(JdepsConfiguration config,
+ PrintWriter log) {
+ this(config, log, Collections.emptySet());
+ }
+ public ModuleAnalyzer(JdepsConfiguration config,
+ PrintWriter log,
+ Set<String> names) {
- root.exports().values().stream()
- .flatMap(Set::stream)
- .map(target -> modulePaths.getModules().get(target))
- .forEach(this.exportTargets::add);
+ if (!config.initialArchives().isEmpty()) {
+ String list = config.initialArchives().stream()
+ .map(Archive::getPathName).collect(joining(" "));
+ throw new JdepsTask.UncheckedBadArgs(new BadArgs("err.invalid.module.option",
+ list, "-check"));
+ }
+
+ this.configuration = config;
+ this.log = log;
- this.filter = new JdepsFilter.Builder().filter(true, true).build();
+ this.dependencyFinder = new DependencyFinder(config, DEFAULT_FILTER);
+ if (names.isEmpty()) {
+ this.modules = configuration.rootModules().stream()
+ .collect(toMap(Function.identity(), ModuleDeps::new));
+ } else {
+ this.modules = names.stream()
+ .map(configuration::findModule)
+ .flatMap(Optional::stream)
+ .collect(toMap(Function.identity(), ModuleDeps::new));
+ }
}
- /**
- * Returns a graph of transitive closure of the given modules.
- *
- * This method does not add the implicit read edges and is intended to
- * get all transitive closures in (reverse) topological sort.
- */
- public static Graph<Module> graph(ModulePaths modulePaths, Module... modules) {
- Graph.Builder<Module> gb = new Graph.Builder<>();
- for (Module module : modules) {
- module.descriptor().requires().stream()
- .map(ModuleDescriptor.Requires::name)
- .map(mn -> modulePaths.getModules().get(mn))
- .forEach(m -> {
- gb.addNode(m);
- gb.addEdge(module, m);
- });
- }
- return gb.build();
- }
+ public boolean run() throws IOException {
+ try {
+ // compute "requires public" dependences
+ modules.values().forEach(ModuleDeps::computeRequiresPublic);
- /**
- * Do the analysis
- */
- public boolean run() {
- try {
- computeRequiresPublic();
- computeRequires();
- // apply transitive reduction and reports recommended requires.
- analyzeDeps();
- // detect any qualiifed exports not used by the target module
- checkQualifiedExports();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
+ modules.values().forEach(md -> {
+ // compute "requires" dependences
+ md.computeRequires();
+ // apply transitive reduction and reports recommended requires.
+ md.analyzeDeps();
+ });
+ } finally {
+ dependencyFinder.shutdown();
}
return true;
}
- /**
- * Compute 'requires public' dependences by analyzing API dependencies
- */
- private void computeRequiresPublic() throws IOException {
- JdepsFilter.Builder builder = new JdepsFilter.Builder();
- // only analyze exported API
- root.descriptor.exports().stream()
- .filter(exp -> !exp.isQualified())
- .map(ModuleDescriptor.Exports::source)
- .forEach(builder::includePackage);
+ class ModuleDeps {
+ final Module root;
+ Set<Module> requiresPublic;
+ Set<Module> requires;
+ Map<String, Set<String>> unusedQualifiedExports;
+
+ ModuleDeps(Module root) {
+ this.root = root;
+ }
+
+ /**
+ * Compute 'requires public' dependences by analyzing API dependencies
+ */
+ private void computeRequiresPublic() {
+ // record requires public
+ this.requiresPublic = computeRequires(true)
+ .filter(m -> !m.name().equals(JAVA_BASE))
+ .collect(toSet());
+
+ trace("requires public: %s%n", requiresPublic);
+ }
+
+ private void computeRequires() {
+ this.requires = computeRequires(false).collect(toSet());
+ trace("requires: %s%n", requires);
+ }
+
+ private Stream<Module> computeRequires(boolean apionly) {
+ // analyze all classes
+
+ if (apionly) {
+ dependencyFinder.parseExportedAPIs(Stream.of(root));
+ } else {
+ dependencyFinder.parse(Stream.of(root));
+ }
+
+ // find the modules of all the dependencies found
+ return dependencyFinder.getDependences(root)
+ .map(Archive::getModule);
+ }
+
+ ModuleDescriptor descriptor() {
+ return descriptor(requiresPublic, requires);
+ }
+
+ private ModuleDescriptor descriptor(Set<Module> requiresPublic,
+ Set<Module> requires) {
+
+ ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(root.name());
+
+ if (!root.name().equals(JAVA_BASE))
+ builder.requires(MANDATED, JAVA_BASE);
+
+ requiresPublic.stream()
+ .filter(m -> !m.name().equals(JAVA_BASE))
+ .map(Module::name)
+ .forEach(mn -> builder.requires(PUBLIC, mn));
- JdepsFilter filter = builder.filter(true, true).build();
+ requires.stream()
+ .filter(m -> !requiresPublic.contains(m))
+ .filter(m -> !m.name().equals(JAVA_BASE))
+ .map(Module::name)
+ .forEach(mn -> builder.requires(mn));
+
+ return builder.build();
+ }
+
+ ModuleDescriptor reduced() {
+ Graph.Builder<Module> bd = new Graph.Builder<>();
+ requiresPublic.stream()
+ .forEach(m -> {
+ bd.addNode(m);
+ bd.addEdge(root, m);
+ });
+
+ // requires public graph
+ Graph<Module> rbg = bd.build().reduce();
+
+ // transitive reduction
+ Graph<Module> newGraph = buildGraph(requires).reduce(rbg);
+ if (DEBUG) {
+ System.err.println("after transitive reduction: ");
+ newGraph.printGraph(log);
+ }
+
+ return descriptor(requiresPublic, newGraph.adjacentNodes(root));
+ }
+
- // analyze dependences for exported packages
- dependencyFinder.findDependencies(filter, true /* api only */, 1);
- Analyzer analyzer = new Analyzer(Analyzer.Type.CLASS, filter);
- analyzer.run(modules);
+ /**
+ * Apply transitive reduction on the resulting graph and reports
+ * recommended requires.
+ */
+ private void analyzeDeps() {
+ Graph.Builder<Module> builder = new Graph.Builder<>();
+ requiresPublic.stream()
+ .forEach(m -> {
+ builder.addNode(m);
+ builder.addEdge(root, m);
+ });
+
+ // requires public graph
+ Graph<Module> rbg = buildGraph(requiresPublic).reduce();
+
+ // transitive reduction
+ Graph<Module> newGraph = buildGraph(requires).reduce(builder.build().reduce());
+ if (DEBUG) {
+ System.err.println("after transitive reduction: ");
+ newGraph.printGraph(log);
+ }
+
+ printModuleDescriptor(log, root);
+
+ ModuleDescriptor analyzedDescriptor = descriptor();
+ if (!matches(root.descriptor(), analyzedDescriptor)) {
+ log.format(" [Suggested module descriptor for %s]%n", root.name());
+ analyzedDescriptor.requires()
+ .stream()
+ .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
+ .forEach(req -> log.format(" requires %s;%n", req));
+ }
- // record requires public
- analyzer.requires(root)
- .filter(m -> m != JAVA_BASE && m != root)
- .map(Archive::getName)
- .forEach(requiresPublic::add);
- trace("requires public: %s%n", requiresPublic);
- }
+ ModuleDescriptor reduced = reduced();
+ if (!matches(root.descriptor(), reduced)) {
+ log.format(" [Transitive reduced graph for %s]%n", root.name());
+ reduced.requires()
+ .stream()
+ .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
+ .forEach(req -> log.format(" requires %s;%n", req));
+ }
+
+ checkQualifiedExports();
+ log.println();
+ }
+
+ private void checkQualifiedExports() {
+ // detect any qualified exports not used by the target module
+ unusedQualifiedExports = unusedQualifiedExports();
+ if (!unusedQualifiedExports.isEmpty())
+ log.format(" [Unused qualified exports in %s]%n", root.name());
+
+ unusedQualifiedExports.keySet().stream()
+ .sorted()
+ .forEach(pn -> log.format(" exports %s to %s%n", pn,
+ unusedQualifiedExports.get(pn).stream()
+ .sorted()
+ .collect(joining(","))));
+ }
- private void computeRequires() throws IOException {
- // add the exportTargets of the qualified exports to the root set
- exportTargets.stream()
- .peek(target -> trace("add root: %s%n", target))
- .forEach(dependencyFinder::addRoot);
+ private void printModuleDescriptor(PrintWriter out, Module module) {
+ ModuleDescriptor descriptor = module.descriptor();
+ out.format("%s (%s)%n", descriptor.name(), module.location());
+
+ if (descriptor.name().equals(JAVA_BASE))
+ return;
+
+ out.println(" [Module descriptor]");
+ descriptor.requires()
+ .stream()
+ .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
+ .forEach(req -> out.format(" requires %s;%n", req));
+ }
+
+
+ /**
+ * Returns a graph of modules required by the specified module.
+ *
+ * Requires public edges of the dependences are added to the graph.
+ */
+ private Graph<Module> buildGraph(Set<Module> deps) {
+ Graph.Builder<Module> builder = new Graph.Builder<>();
+ builder.addNode(root);
+ Set<Module> visited = new HashSet<>();
+ visited.add(root);
+ Deque<Module> deque = new LinkedList<>();
+ deps.stream()
+ .forEach(m -> {
+ deque.add(m);
+ builder.addEdge(root, m);
+ });
- // analyze all classes
- dependencyFinder.findDependencies(filter, false /* all classes */, 1);
- Analyzer analyzer = new Analyzer(Analyzer.Type.CLASS, filter);
- analyzer.run(modules);
+ // read requires public from ModuleDescription
+ Module source;
+ while ((source = deque.poll()) != null) {
+ if (visited.contains(source))
+ continue;
+
+ visited.add(source);
+ builder.addNode(source);
+ Module from = source;
+ source.descriptor().requires().stream()
+ .filter(req -> req.modifiers().contains(PUBLIC))
+ .map(ModuleDescriptor.Requires::name)
+ .map(configuration::findModule)
+ .flatMap(Optional::stream)
+ .forEach(m -> {
+ deque.add(m);
+ builder.addEdge(from, m);
+ });
+ }
+ return builder.build();
+ }
+
+ /**
+ * Detects any qualified exports not used by the target module.
+ */
+ private Map<String, Set<String>> unusedQualifiedExports() {
+ Map<String, Set<String>> unused = new HashMap<>();
- // record requires
- analyzer.requires(root)
- .filter(m -> m != JAVA_BASE && m != root)
- .map(Archive::getName)
- .forEach(requires::add);
+ // build the qualified exports map
+ Map<String, Set<String>> qualifiedExports =
+ root.exports().entrySet().stream()
+ .filter(e -> !e.getValue().isEmpty())
+ .map(Map.Entry::getKey)
+ .collect(toMap(Function.identity(), _k -> new HashSet<>()));
+
+ Set<Module> mods = new HashSet<>();
+ root.exports().values()
+ .stream()
+ .flatMap(Set::stream)
+ .forEach(target -> configuration.findModule(target)
+ .ifPresentOrElse(mods::add,
+ () -> log.format("Warning: %s not found%n", target))
+ );
+
+ // parse all target modules
+ dependencyFinder.parse(mods.stream());
- this.graph = buildGraph(analyzer, root);
- if (traceOn) {
- trace("dependences: %s%n", graph.nodes());
- graph.printGraph(System.out);
+ // adds to the qualified exports map if a module references it
+ mods.stream().forEach(m ->
+ m.getDependencies()
+ .map(Dependency.Location::getPackageName)
+ .filter(qualifiedExports::containsKey)
+ .forEach(pn -> qualifiedExports.get(pn).add(m.name())));
+
+ // compare with the exports from ModuleDescriptor
+ Set<String> staleQualifiedExports =
+ qualifiedExports.keySet().stream()
+ .filter(pn -> !qualifiedExports.get(pn).equals(root.exports().get(pn)))
+ .collect(toSet());
+
+ if (!staleQualifiedExports.isEmpty()) {
+ for (String pn : staleQualifiedExports) {
+ Set<String> targets = new HashSet<>(root.exports().get(pn));
+ targets.removeAll(qualifiedExports.get(pn));
+ unused.put(pn, targets);
+ }
+ }
+ return unused;
}
}
- /**
- * Apply transitive reduction on the resulting graph and reports
- * recommended requires.
- */
- private void analyzeDeps() {
- String moduleName = root.name();
-
- Graph.Builder<String> builder = new Graph.Builder<>();
- requiresPublic.stream()
- .forEach(mn -> {
- builder.addNode(mn);
- builder.addEdge(moduleName, mn);
- });
- // requires public graph
- Graph<String> rbg = builder.build().reduce();
-
- // convert the dependence graph from Module to name
- Set<String> nodes = this.graph.nodes().stream()
- .map(Module::name)
- .collect(Collectors.toSet());
- Map<String, Set<String>> edges = new HashMap<>();
- this.graph.edges().keySet().stream()
- .forEach(m -> {
- String mn = m.name();
- Set<String> es = edges.computeIfAbsent(mn, _k -> new HashSet<String>());
- this.graph.edges().get(m).stream()
- .map(Module::name)
- .forEach(es::add);
- });
-
- // transitive reduction
- Graph<String> newGraph = new Graph<>(nodes, edges).reduce(rbg);
- if (traceOn) {
- System.out.println("after transitive reduction");
- newGraph.printGraph(System.out);
- };
+ private boolean matches(ModuleDescriptor md, ModuleDescriptor other) {
+ // build requires public from ModuleDescriptor
+ Set<ModuleDescriptor.Requires> reqPublic = md.requires().stream()
+ .filter(req -> req.modifiers().contains(PUBLIC))
+ .collect(toSet());
+ Set<ModuleDescriptor.Requires> otherReqPublic = other.requires().stream()
+ .filter(req -> req.modifiers().contains(PUBLIC))
+ .collect(toSet());
- Set<String> reducedRequires = newGraph.adjacentNodes(moduleName);
- if (matches(root.descriptor(), requires, requiresPublic) &&
- matches(root.descriptor(), reducedRequires, requiresPublic)) {
- System.out.println("--- Analysis result: no change for " + root.name());
- } else {
- System.out.println("--- Analysis result: suggested requires for " + root.name());
- System.out.format("module %s%n", root.name());
- requires.stream()
- .sorted()
- .forEach(dn -> System.out.format(" requires %s%s;%n",
- requiresPublic.contains(dn) ? "public " : "", dn));
- if (!requires.equals(reducedRequires) && !reducedRequires.isEmpty()) {
- System.out.format("%nmodule %s (reduced)%n", root.name());
- newGraph.adjacentNodes(moduleName)
- .stream()
- .sorted()
- .forEach(dn -> System.out.format(" requires %s%s;%n",
- requiresPublic.contains(dn) ? "public " : "", dn));
- }
- System.out.println("\n--- Module descriptor");
- Graph<Module> mdGraph = graph(modulePaths, root);
- mdGraph.reverse(m -> printModuleDescriptor(System.out, m.descriptor()));
- }
- }
-
- /**
- * Detects any qualified exports not used by the target module.
- */
- private void checkQualifiedExports() throws IOException {
- Analyzer analyzer = new Analyzer(Analyzer.Type.CLASS, filter);
- analyzer.run(dependencyFinder.roots());
-
- // build the qualified exports map
- Map<String, Set<String>> qualifiedExports =
- root.exports().entrySet().stream()
- .filter(e -> !e.getValue().isEmpty())
- .map(Map.Entry::getKey)
- .collect(Collectors.toMap(Function.identity(), _k -> new HashSet<>()));
-
- // adds to the qualified exports map if a module references it
- for (Module m : exportTargets) {
- analyzer.dependences(m).stream()
- .map(this::toPackageName)
- .filter(qualifiedExports::containsKey)
- .forEach(pn -> qualifiedExports.get(pn).add(m.name()));
- }
-
- // compare with the exports from ModuleDescriptor
- Set<String> staleQualifiedExports =
- qualifiedExports.keySet().stream()
- .filter(pn -> !qualifiedExports.get(pn).equals(root.exports().get(pn)))
- .collect(Collectors.toSet());
-
- if (!staleQualifiedExports.isEmpty()) {
- System.out.println("--- Unused qualified exports in " + root.name());
- for (String pn : staleQualifiedExports) {
- Set<String> unused = new HashSet<>(root.exports().get(pn));
- unused.removeAll(qualifiedExports.get(pn));
- System.out.format(" exports %s to %s%n", pn,
- unused.stream().collect(Collectors.joining(",")));
- }
- }
- }
-
- private String toPackageName(String cn) {
- int i = cn.lastIndexOf('.');
- return i > 0 ? cn.substring(0, i) : "";
- }
-
- private boolean matches(ModuleDescriptor md, Set<String> requires, Set<String> requiresPublic) {
- Set<String> reqPublic = md.requires().stream()
- .filter(req -> req.modifiers().contains(PUBLIC))
- .map(ModuleDescriptor.Requires::name)
- .collect(Collectors.toSet());
- if (!requiresPublic.equals(reqPublic)) {
+ if (!reqPublic.equals(otherReqPublic)) {
trace("mismatch requires public: %s%n", reqPublic);
return false;
}
- // java.base is not in requires
- int javaBase = md.name().equals(JAVA_BASE.name()) ? 0 : 1;
- if (requires.size()+javaBase != md.requires().size()) {
- trace("mismatch requires: %d != %d%n", requires.size()+1, md.requires().size());
- return false;
- }
- Set<String> unused = md.requires().stream()
- .map(ModuleDescriptor.Requires::name)
- .filter(req -> !requires.contains(req) && !req.equals(JAVA_BASE.name()))
- .collect(Collectors.toSet());
+ Set<ModuleDescriptor.Requires> unused = md.requires().stream()
+ .filter(req -> !other.requires().contains(req))
+ .collect(Collectors.toSet());
+
if (!unused.isEmpty()) {
trace("mismatch requires: %s%n", unused);
return false;
@@ -302,371 +389,50 @@
return true;
}
- private void printModuleDescriptor(PrintStream out, ModuleDescriptor descriptor) {
- if (descriptor.name().equals("java.base"))
- return;
-
- out.format("module %s%n", descriptor.name());
- descriptor.requires()
- .stream()
- .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
- .forEach(req -> out.format(" requires %s;%n", req));
- }
-
/**
- * Returns a graph of modules required by the specified module.
+ * Generate dotfile from module descriptor
*
- * Requires public edges of the dependences are added to the graph.
+ * @param dir output directory
*/
- private Graph<Module> buildGraph(Analyzer analyzer, Module module) {
- Graph.Builder<Module> builder = new Graph.Builder<>();
- builder.addNode(module);
- Set<Module> visited = new HashSet<>();
- visited.add(module);
- Deque<Module> deque = new LinkedList<>();
- analyzer.requires(module)
- .map(Archive::getModule)
- .filter(m -> m != JAVA_BASE)
- .forEach(m -> {
- deque.add(m);
- builder.addEdge(module, m);
- });
-
- // read requires public from ModuleDescription
- Module source;
- while ((source = deque.poll()) != null) {
- if (visited.contains(source))
- continue;
- visited.add(source);
- builder.addNode(source);
- Module from = source;
- source.descriptor().requires().stream()
- .filter(req -> req.modifiers().contains(PUBLIC))
- .map(ModuleDescriptor.Requires::name)
- .map(req -> modulePaths.getModules().get(req))
- .filter(m -> m != JAVA_BASE)
- .forEach(m -> {
- deque.add(m);
- builder.addEdge(from, m);
- });
+ public boolean genDotFiles(Path dir) throws IOException {
+ Files.createDirectories(dir);
+ for (Module m : modules.keySet()) {
+ genDotFile(dir, m.name());
}
- return builder.build();
+ return true;
}
- static class Graph<T> {
- private final Set<T> nodes;
- private final Map<T, Set<T>> edges;
- private Graph(Set<T> nodes, Map<T, Set<T>> edges) {
- this.nodes = nodes;
- this.edges = edges;
- }
-
- public Set<T> nodes() {
- return nodes;
- }
-
- public Map<T, Set<T>> edges() {
- return edges;
- }
-
- public Set<T> adjacentNodes(T u) {
- return edges.get(u);
- }
-
- /**
- * Returns a new Graph after transitive reduction
- */
- public Graph<T> reduce() {
- Graph.Builder<T> builder = new Builder<>();
- nodes.stream()
- .forEach(u -> {
- builder.addNode(u);
- edges.get(u).stream()
- .filter(v -> !pathExists(u, v, false))
- .forEach(v -> builder.addEdge(u, v));
- });
- return builder.build();
- }
-
- /**
- * Returns a new Graph after transitive reduction. All edges in
- * the given g takes precedence over this graph.
- *
- * @throw IllegalArgumentException g must be a subgraph this graph
- */
- public Graph<T> reduce(Graph<T> g) {
- boolean subgraph = nodes.containsAll(g.nodes) && g.edges.keySet().stream()
- .allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));
- if (!subgraph) {
- throw new IllegalArgumentException(g + " is not a subgraph of " + this);
- }
-
- Graph.Builder<T> builder = new Builder<>();
- nodes.stream()
- .forEach(u -> {
- builder.addNode(u);
- // filter the edge if there exists a path from u to v in the given g
- // or there exists another path from u to v in this graph
- edges.get(u).stream()
- .filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))
- .forEach(v -> builder.addEdge(u, v));
- });
-
- // add the overlapped edges from this graph and the given g
- g.edges().keySet().stream()
- .forEach(u -> g.adjacentNodes(u).stream()
- .filter(v -> isAdjacent(u, v))
- .forEach(v -> builder.addEdge(u, v)));
- return builder.build();
- }
-
- /**
- * Returns nodes sorted in topological order.
- */
- public Stream<T> orderedNodes() {
- TopoSorter<T> sorter = new TopoSorter<>(this);
- return sorter.result.stream();
- }
-
- /**
- * Iterates the nodes sorted in topological order and performs the
- * given action.
- */
- public void ordered(Consumer<T> action) {
- TopoSorter<T> sorter = new TopoSorter<>(this);
- sorter.ordered(action);
- }
-
- /**
- * Iterates the nodes sorted in reverse topological order and
- * performs the given action.
- */
- public void reverse(Consumer<T> action) {
- TopoSorter<T> sorter = new TopoSorter<>(this);
- sorter.reverse(action);
- }
-
- private boolean isAdjacent(T u, T v) {
- return edges.containsKey(u) && edges.get(u).contains(v);
- }
-
- private boolean pathExists(T u, T v) {
- return pathExists(u, v, true);
- }
-
- /**
- * Returns true if there exists a path from u to v in this graph.
- * If includeAdjacent is false, it returns true if there exists
- * another path from u to v of distance > 1
- */
- private boolean pathExists(T u, T v, boolean includeAdjacent) {
- if (!nodes.contains(u) || !nodes.contains(v)) {
- return false;
- }
- if (includeAdjacent && isAdjacent(u, v)) {
- return true;
- }
- Deque<T> stack = new LinkedList<>();
- Set<T> visited = new HashSet<>();
- stack.push(u);
- while (!stack.isEmpty()) {
- T node = stack.pop();
- if (node.equals(v)) {
- return true;
- }
- if (!visited.contains(node)) {
- visited.add(node);
- edges.get(node).stream()
- .filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))
- .forEach(e -> stack.push(e));
- }
- }
- assert !visited.contains(v);
- return false;
- }
-
- void printGraph(PrintStream out) {
- out.println("graph for " + nodes);
- nodes.stream()
- .forEach(u -> adjacentNodes(u).stream()
- .forEach(v -> out.format("%s -> %s%n", u, v)));
- }
-
- @Override
- public String toString() {
- return nodes.toString();
- }
-
- static class Builder<T> {
- final Set<T> nodes = new HashSet<>();
- final Map<T, Set<T>> edges = new HashMap<>();
-
- public void addNode(T node) {
- if (nodes.contains(node)) {
- return;
- }
- nodes.add(node);
- edges.computeIfAbsent(node, _e -> new HashSet<>());
- }
-
- public void addEdge(T u, T v) {
- addNode(u);
- addNode(v);
- edges.get(u).add(v);
- }
-
- public Graph<T> build() {
- return new Graph<>(nodes, edges);
- }
-
- void print(PrintStream out) {
- out.println(nodes);
- nodes.stream()
- .forEach(u -> edges.get(u).stream()
- .forEach(v -> out.format("%s -> %s%n", u, v)));
- }
- }
- }
-
- static class TopoSorter<T> {
- final Deque<T> result = new LinkedList<>();
- final Deque<T> nodes;
- final Graph<T> graph;
- TopoSorter(Graph<T> graph) {
- this.graph = graph;
- this.nodes = new LinkedList<>(graph.nodes);
- sort();
- }
-
- public void ordered(Consumer<T> action) {
- result.iterator().forEachRemaining(action);
- }
-
- public void reverse(Consumer<T> action) {
- result.descendingIterator().forEachRemaining(action);
- }
-
- private void sort() {
- Deque<T> visited = new LinkedList<>();
- Deque<T> done = new LinkedList<>();
- T node;
- while ((node = nodes.poll()) != null) {
- if (!visited.contains(node)) {
- visit(node, visited, done);
- }
- }
- }
-
- private void visit(T node, Deque<T> visited, Deque<T> done) {
- if (visited.contains(node)) {
- if (!done.contains(node)) {
- throw new IllegalArgumentException("Cyclic detected: " +
- node + " " + graph.edges().get(node));
- }
- return;
- }
- visited.add(node);
- graph.edges().get(node).stream()
- .forEach(x -> visit(x, visited, done));
- done.add(node);
- result.addLast(node);
- }
- }
-
- static class DotGraph {
- static final String ORANGE = "#e76f00";
- static final String BLUE = "#437291";
- static final String GRAY = "#dddddd";
-
- static final String REEXPORTS = "";
- static final String REQUIRES = "style=\"dashed\"";
- static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
-
- static final Set<String> javaModules = modules(name ->
- (name.startsWith("java.") && !name.equals("java.smartcardio")));
- static final Set<String> jdkModules = modules(name ->
- (name.startsWith("java.") ||
- name.startsWith("jdk.") ||
- name.startsWith("javafx.")) && !javaModules.contains(name));
-
- private static Set<String> modules(Predicate<String> predicate) {
- return ModuleFinder.ofSystem().findAll()
- .stream()
- .map(ModuleReference::descriptor)
- .map(ModuleDescriptor::name)
- .filter(predicate)
- .collect(Collectors.toSet());
- }
-
- static void printAttributes(PrintStream out) {
- out.format(" size=\"25,25\";%n");
- out.format(" nodesep=.5;%n");
- out.format(" ranksep=1.5;%n");
- out.format(" pencolor=transparent;%n");
- out.format(" node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
- out.format(" edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
- }
-
- static void printNodes(PrintStream out, Graph<String> graph) {
- out.format(" subgraph se {%n");
- graph.nodes().stream()
- .filter(javaModules::contains)
- .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
- mn, ORANGE, "java"));
- out.format(" }%n");
- graph.nodes().stream()
- .filter(jdkModules::contains)
- .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
- mn, BLUE, "jdk"));
-
- graph.nodes().stream()
- .filter(mn -> !javaModules.contains(mn) && !jdkModules.contains(mn))
- .forEach(mn -> out.format(" \"%s\";%n", mn));
- }
-
- static void printEdges(PrintStream out, Graph<String> graph,
- String node, Set<String> requiresPublic) {
- graph.adjacentNodes(node).forEach(dn -> {
- String attr = dn.equals("java.base") ? REQUIRES_BASE
- : (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
- out.format(" \"%s\" -> \"%s\" [%s];%n", node, dn, attr);
- });
- }
- }
-
- public void genDotFile(Path dir) throws IOException {
- String name = root.name();
- try (PrintStream out
- = new PrintStream(Files.newOutputStream(dir.resolve(name + ".dot")))) {
- Configuration cf = modulePaths.configuration(name);
+ private void genDotFile(Path dir, String name) throws IOException {
+ try (OutputStream os = Files.newOutputStream(dir.resolve(name + ".dot"));
+ PrintWriter out = new PrintWriter(os)) {
+ Set<Module> modules = configuration.resolve(Set.of(name))
+ .collect(Collectors.toSet());
// transitive reduction
- Graph<String> graph = gengraph(cf);
+ Graph<String> graph = gengraph(modules);
out.format("digraph \"%s\" {%n", name);
DotGraph.printAttributes(out);
DotGraph.printNodes(out, graph);
- cf.modules().stream()
- .map(ResolvedModule::reference)
- .map(ModuleReference::descriptor)
- .sorted(Comparator.comparing(ModuleDescriptor::name))
- .forEach(md -> {
- String mn = md.name();
- Set<String> requiresPublic = md.requires().stream()
+ modules.stream()
+ .map(Module::descriptor)
+ .sorted(Comparator.comparing(ModuleDescriptor::name))
+ .forEach(md -> {
+ String mn = md.name();
+ Set<String> requiresPublic = md.requires().stream()
.filter(d -> d.modifiers().contains(PUBLIC))
.map(d -> d.name())
- .collect(Collectors.toSet());
+ .collect(toSet());
- DotGraph.printEdges(out, graph, mn, requiresPublic);
- });
+ DotGraph.printEdges(out, graph, mn, requiresPublic);
+ });
out.println("}");
}
}
-
/**
* Returns a Graph of the given Configuration after transitive reduction.
*
@@ -675,12 +441,12 @@
* (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V)
* in which V would not be re-exported from U.
*/
- private Graph<String> gengraph(Configuration cf) {
+ private Graph<String> gengraph(Set<Module> modules) {
// build a Graph containing only requires public edges
// with transitive reduction.
Graph.Builder<String> rpgbuilder = new Graph.Builder<>();
- for (ResolvedModule resolvedModule : cf.modules()) {
- ModuleDescriptor md = resolvedModule.reference().descriptor();
+ for (Module module : modules) {
+ ModuleDescriptor md = module.descriptor();
String mn = md.name();
md.requires().stream()
.filter(d -> d.modifiers().contains(PUBLIC))
@@ -692,12 +458,12 @@
// build the readability graph
Graph.Builder<String> builder = new Graph.Builder<>();
- for (ResolvedModule resolvedModule : cf.modules()) {
- ModuleDescriptor md = resolvedModule.reference().descriptor();
+ for (Module module : modules) {
+ ModuleDescriptor md = module.descriptor();
String mn = md.name();
builder.addNode(mn);
- resolvedModule.reads().stream()
- .map(ResolvedModule::name)
+ configuration.reads(module)
+ .map(Module::name)
.forEach(d -> builder.addEdge(mn, d));
}
@@ -705,4 +471,25 @@
return builder.build().reduce(rpg);
}
+ // ---- for testing purpose
+ public ModuleDescriptor[] descriptors(String name) {
+ ModuleDeps moduleDeps = modules.keySet().stream()
+ .filter(m -> m.name().equals(name))
+ .map(modules::get)
+ .findFirst().get();
+
+ ModuleDescriptor[] descriptors = new ModuleDescriptor[3];
+ descriptors[0] = moduleDeps.root.descriptor();
+ descriptors[1] = moduleDeps.descriptor();
+ descriptors[2] = moduleDeps.reduced();
+ return descriptors;
+ }
+
+ public Map<String, Set<String>> unusedQualifiedExports(String name) {
+ ModuleDeps moduleDeps = modules.keySet().stream()
+ .filter(m -> m.name().equals(name))
+ .map(modules::get)
+ .findFirst().get();
+ return moduleDeps.unusedQualifiedExports;
+ }
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java Thu May 19 10:55:33 2016 -0700
@@ -24,15 +24,24 @@
*/
package com.sun.tools.jdeps;
-import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
+import static com.sun.tools.jdeps.JdepsTask.*;
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
-import static com.sun.tools.jdeps.Module.trace;
+import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleFinder;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -40,168 +49,159 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+
public class ModuleInfoBuilder {
- final ModulePaths modulePaths;
+ final JdepsConfiguration configuration;
+ final Path outputdir;
+
final DependencyFinder dependencyFinder;
- final JdepsFilter filter;
final Analyzer analyzer;
- final Map<Module, Module> strictModules = new HashMap<>();
- ModuleInfoBuilder(ModulePaths modulePaths, DependencyFinder finder) {
- this.modulePaths = modulePaths;
- this.dependencyFinder = finder;
- this.filter = new JdepsFilter.Builder().filter(true, true).build();
- this.analyzer = new Analyzer(CLASS, filter);
+ final Map<Module, Module> strictModules;
+ public ModuleInfoBuilder(JdepsConfiguration configuration,
+ List<String> args,
+ Path outputdir) {
+ this.configuration = configuration;
+ this.outputdir = outputdir;
+
+ this.dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
+ this.analyzer = new Analyzer(configuration, Analyzer.Type.CLASS, DEFAULT_FILTER);
+
+ // add targets to modulepath if it has module-info.class
+ List<Path> paths = args.stream()
+ .map(fn -> Paths.get(fn))
+ .collect(Collectors.toList());
+
+ // automatic module to convert to strict module
+ this.strictModules = ModuleFinder.of(paths.toArray(new Path[0]))
+ .findAll().stream()
+ .map(configuration::toModule)
+ .collect(Collectors.toMap(Function.identity(), Function.identity()));
+
+ Optional<Module> om = strictModules.keySet().stream()
+ .filter(m -> !m.descriptor().isAutomatic())
+ .findAny();
+ if (om.isPresent()) {
+ throw new UncheckedBadArgs(new BadArgs("err.genmoduleinfo.not.jarfile",
+ om.get().getPathName()));
+ }
+ if (strictModules.isEmpty()) {
+ throw new UncheckedBadArgs(new BadArgs("err.invalid.path", args));
+ }
}
- private Stream<Module> automaticModules() {
- return modulePaths.getModules().values()
- .stream()
- .filter(Module::isAutomatic);
+ public boolean run() throws IOException {
+ try {
+ // pass 1: find API dependencies
+ Map<Archive, Set<Archive>> requiresPublic = computeRequiresPublic();
+
+ // pass 2: analyze all class dependences
+ dependencyFinder.parse(automaticModules().stream());
+
+ analyzer.run(automaticModules(), dependencyFinder.locationToArchive());
+
+ // computes requires and requires public
+ automaticModules().forEach(m -> {
+ Map<String, Boolean> requires;
+ if (requiresPublic.containsKey(m)) {
+ requires = requiresPublic.get(m).stream()
+ .map(Archive::getModule)
+ .collect(Collectors.toMap(Module::name, (v) -> Boolean.TRUE));
+ } else {
+ requires = new HashMap<>();
+ }
+ analyzer.requires(m)
+ .map(Archive::getModule)
+ .forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));
+
+ strictModules.put(m, m.toStrictModule(requires));
+ });
+
+ // generate module-info.java
+ descriptors().forEach(md -> writeModuleInfo(outputdir, md));
+
+ // find any missing dependences
+ return automaticModules().stream()
+ .flatMap(analyzer::requires)
+ .allMatch(m -> !m.equals(NOT_FOUND));
+ } finally {
+ dependencyFinder.shutdown();
+ }
+ }
+
+ /**
+ * Returns the stream of resulting modules
+ */
+ Stream<Module> modules() {
+ return strictModules.values().stream();
+ }
+
+ /**
+ * Returns the stream of resulting ModuleDescriptors
+ */
+ public Stream<ModuleDescriptor> descriptors() {
+ return strictModules.values().stream().map(Module::descriptor);
+ }
+
+ void visitMissingDeps(Analyzer.Visitor visitor) {
+ automaticModules().stream()
+ .filter(m -> analyzer.requires(m).anyMatch(d -> d.equals(NOT_FOUND)))
+ .forEach(m -> {
+ analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE);
+ });
+ }
+ void writeModuleInfo(Path dir, ModuleDescriptor descriptor) {
+ String mn = descriptor.name();
+ Path srcFile = dir.resolve(mn).resolve("module-info.java");
+ try {
+ Files.createDirectories(srcFile.getParent());
+ System.out.println("writing to " + srcFile);
+ try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
+ printModuleInfo(pw, descriptor);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void printModuleInfo(PrintWriter writer, ModuleDescriptor descriptor) {
+ writer.format("module %s {%n", descriptor.name());
+
+ Map<String, Module> modules = configuration.getModules();
+ // first print the JDK modules
+ descriptor.requires().stream()
+ .filter(req -> !req.name().equals("java.base")) // implicit requires
+ .sorted(Comparator.comparing(Requires::name))
+ .forEach(req -> writer.format(" requires %s;%n", req));
+
+ descriptor.exports().stream()
+ .peek(exp -> {
+ if (exp.targets().size() > 0)
+ throw new InternalError(descriptor.name() + " qualified exports: " + exp);
+ })
+ .sorted(Comparator.comparing(Exports::source))
+ .forEach(exp -> writer.format(" exports %s;%n", exp.source()));
+
+ descriptor.provides().values().stream()
+ .sorted(Comparator.comparing(Provides::service))
+ .forEach(p -> p.providers().stream()
+ .sorted()
+ .forEach(impl -> writer.format(" provides %s with %s;%n", p.service(), impl)));
+
+ writer.println("}");
+ }
+
+
+ private Set<Module> automaticModules() {
+ return strictModules.keySet();
}
/**
* Compute 'requires public' dependences by analyzing API dependencies
*/
- Map<Module, Set<Module>> computeRequiresPublic() throws IOException {
- dependencyFinder.findDependencies(filter, true /* api only */, 1);
- Analyzer pass1 = new Analyzer(Analyzer.Type.CLASS, filter);
-
- pass1.run(dependencyFinder.archives());
-
- return automaticModules().collect(Collectors.toMap(Function.identity(),
- source -> pass1.requires(source)
- .map(Archive::getModule)
- .collect(Collectors.toSet())));
- }
-
- boolean run(Analyzer.Type verbose, boolean quiet) throws IOException {
- // add all automatic modules to the root set
- automaticModules().forEach(dependencyFinder::addRoot);
-
- // pass 1: find API dependencies
- Map<Module, Set<Module>> requiresPublic = computeRequiresPublic();
-
- // pass 2: analyze all class dependences
- dependencyFinder.findDependencies(filter, false /* all classes */, 1);
- analyzer.run(dependencyFinder.archives());
-
- // computes requires and requires public
- automaticModules().forEach(m -> {
- Map<String, Boolean> requires;
- if (requiresPublic.containsKey(m)) {
- requires = requiresPublic.get(m)
- .stream()
- .collect(Collectors.toMap(Archive::getName, (v) -> Boolean.TRUE));
- } else {
- requires = new HashMap<>();
- }
- analyzer.requires(m)
- .forEach(d -> requires.putIfAbsent(d.getName(), Boolean.FALSE));
-
- trace("strict module %s requires %s%n", m.name(), requires);
- strictModules.put(m, m.toStrictModule(requires));
- });
-
- // find any missing dependences
- Optional<Module> missingDeps = automaticModules()
- .filter(this::missingDep)
- .findAny();
- if (missingDeps.isPresent()) {
- automaticModules()
- .filter(this::missingDep)
- .forEach(m -> {
- System.err.format("Missing dependencies from %s%n", m.name());
- analyzer.visitDependences(m,
- new Analyzer.Visitor() {
- @Override
- public void visitDependence(String origin, Archive originArchive,
- String target, Archive targetArchive) {
- if (targetArchive == NOT_FOUND)
- System.err.format(" %-50s -> %-50s %s%n",
- origin, target, targetArchive.getName());
- }
- }, verbose);
- System.err.println();
- });
-
- System.err.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
- }
- return missingDeps.isPresent() ? false : true;
- }
+ private Map<Archive, Set<Archive>> computeRequiresPublic() throws IOException {
+ // parse the input modules
+ dependencyFinder.parseExportedAPIs(automaticModules().stream());
- private boolean missingDep(Archive m) {
- return analyzer.requires(m).filter(a -> a.equals(NOT_FOUND))
- .findAny().isPresent();
- }
-
- void build(Path dir) throws IOException {
- ModuleInfoWriter writer = new ModuleInfoWriter(dir);
- writer.generateOutput(strictModules.values(), analyzer);
- }
-
- private class ModuleInfoWriter {
- private final Path outputDir;
- ModuleInfoWriter(Path dir) {
- this.outputDir = dir;
- }
-
- void generateOutput(Iterable<Module> modules, Analyzer analyzer) throws IOException {
- // generate module-info.java file for each archive
- for (Module m : modules) {
- if (m.packages().contains("")) {
- System.err.format("ERROR: %s contains unnamed package. " +
- "module-info.java not generated%n", m.getPathName());
- continue;
- }
-
- String mn = m.getName();
- Path srcFile = outputDir.resolve(mn).resolve("module-info.java");
- Files.createDirectories(srcFile.getParent());
- System.out.println("writing to " + srcFile);
- try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
- printModuleInfo(pw, m);
- }
- }
- }
-
- private void printModuleInfo(PrintWriter writer, Module m) {
- writer.format("module %s {%n", m.name());
-
- Map<String, Module> modules = modulePaths.getModules();
- Map<String, Boolean> requires = m.requires();
- // first print the JDK modules
- requires.keySet().stream()
- .filter(mn -> !mn.equals("java.base")) // implicit requires
- .filter(mn -> modules.containsKey(mn) && modules.get(mn).isJDK())
- .sorted()
- .forEach(mn -> {
- String modifier = requires.get(mn) ? "public " : "";
- writer.format(" requires %s%s;%n", modifier, mn);
- });
-
- // print requires non-JDK modules
- requires.keySet().stream()
- .filter(mn -> !modules.containsKey(mn) || !modules.get(mn).isJDK())
- .sorted()
- .forEach(mn -> {
- String modifier = requires.get(mn) ? "public " : "";
- writer.format(" requires %s%s;%n", modifier, mn);
- });
-
- m.packages().stream()
- .sorted()
- .forEach(pn -> writer.format(" exports %s;%n", pn));
-
- m.provides().entrySet().stream()
- .sorted(Map.Entry.comparingByKey())
- .forEach(e -> {
- String service = e.getKey();
- e.getValue().stream()
- .sorted()
- .forEach(impl -> writer.format(" provides %s with %s;%n", service, impl));
- });
-
- writer.println("}");
- }
+ return dependencyFinder.dependences();
}
}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModulePaths.java Thu May 19 10:55:33 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. 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.FileNotFoundException;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.lang.module.Configuration;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReference;
-import java.lang.module.ResolvedModule;
-import java.net.URI;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.ProviderNotFoundException;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
-
-public class ModulePaths {
- final ModuleFinder finder;
- final Map<String, Module> modules = new LinkedHashMap<>();
-
- public ModulePaths(String upgradeModulePath, String modulePath) {
- this(upgradeModulePath, modulePath, Collections.emptyList());
- }
-
- public ModulePaths(String upgradeModulePath, String modulePath, List<Path> jars) {
- ModuleFinder finder = ModuleFinder.ofSystem();
- if (upgradeModulePath != null) {
- finder = ModuleFinder.compose(createModulePathFinder(upgradeModulePath), finder);
- }
- if (jars.size() > 0) {
- finder = ModuleFinder.compose(finder, ModuleFinder.of(jars.toArray(new Path[0])));
- }
- if (modulePath != null) {
- finder = ModuleFinder.compose(finder, createModulePathFinder(modulePath));
- }
- this.finder = finder;
-
- // add modules from modulepaths
- finder.findAll().stream().forEach(mref ->
- modules.computeIfAbsent(mref.descriptor().name(), mn -> toModule(mn, mref))
- );
- }
-
- /**
- * Returns the list of Modules that can be found in the specified
- * module paths.
- */
- Map<String, Module> getModules() {
- return modules;
- }
-
- Set<Module> dependences(String... roots) {
- Configuration cf = configuration(roots);
- return cf.modules().stream()
- .map(ResolvedModule::name)
- .map(modules::get)
- .collect(Collectors.toSet());
- }
-
- Configuration configuration(String... roots) {
- return Configuration.empty().resolveRequires(finder, ModuleFinder.empty(), Set.of(roots));
- }
-
- private static ModuleFinder createModulePathFinder(String mpaths) {
- if (mpaths == null) {
- return null;
- } else {
- String[] dirs = mpaths.split(File.pathSeparator);
- Path[] paths = new Path[dirs.length];
- int i = 0;
- for (String dir : dirs) {
- paths[i++] = Paths.get(dir);
- }
- return ModuleFinder.of(paths);
- }
- }
-
- private static Module toModule(String mn, ModuleReference mref) {
- return SystemModulePath.find(mn)
- .orElse(toModule(new Module.Builder(mn), mref));
- }
-
- private static Module toModule(Module.Builder builder, ModuleReference mref) {
- ModuleDescriptor md = mref.descriptor();
- builder.descriptor(md);
- for (ModuleDescriptor.Requires req : md.requires()) {
- builder.require(req.name(), req.modifiers().contains(PUBLIC));
- }
- for (ModuleDescriptor.Exports exp : md.exports()) {
- builder.export(exp.source(), exp.targets());
- }
- builder.packages(md.packages());
-
- try {
- URI location = mref.location()
- .orElseThrow(FileNotFoundException::new);
- builder.location(location);
- builder.classes(getClassReader(location, md.name()));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- return builder.build();
- }
-
- static class SystemModulePath {
- final static Module JAVA_BASE;
-
- private final static FileSystem fs;
- private final static Path root;
- private final static Map<String, Module> installed = new HashMap<>();
- static {
- if (isJrtAvailable()) {
- // jrt file system
- fs = FileSystems.getFileSystem(URI.create("jrt:/"));
- root = fs.getPath("/modules");
- } else {
- // exploded image
- String javahome = System.getProperty("java.home");
- fs = FileSystems.getDefault();
- root = Paths.get(javahome, "modules");
- }
-
- ModuleFinder.ofSystem().findAll().stream()
- .forEach(mref ->
- installed.computeIfAbsent(mref.descriptor().name(),
- mn -> toModule(new Module.Builder(mn, true), mref))
- );
- JAVA_BASE = installed.get("java.base");
-
- Profile.init(installed);
- }
-
- private static boolean isJrtAvailable() {
- try {
- FileSystems.getFileSystem(URI.create("jrt:/"));
- return true;
- } catch (ProviderNotFoundException | FileSystemNotFoundException e) {
- return false;
- }
- }
-
- public static Optional<Module> find(String mn) {
- return installed.containsKey(mn) ? Optional.of(installed.get(mn))
- : Optional.empty();
- }
-
- public static boolean contains(Module m) {
- return installed.containsValue(m);
- }
-
- public static ClassFileReader getClassReader(String modulename) throws IOException {
- Path mp = root.resolve(modulename);
- if (Files.exists(mp) && Files.isDirectory(mp)) {
- return ClassFileReader.newInstance(fs, mp);
- } else {
- throw new FileNotFoundException(mp.toString());
- }
- }
- }
-
- /**
- * Returns a ModuleClassReader that only reads classes for the given modulename.
- */
- public static ClassFileReader getClassReader(URI location, String modulename)
- throws IOException {
- if (location.getScheme().equals("jrt")) {
- return SystemModulePath.getClassReader(modulename);
- } else {
- Path path = Paths.get(location);
- return ClassFileReader.newInstance(path);
- }
- }
-}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java Thu May 19 10:55:33 2016 -0700
@@ -26,9 +26,13 @@
package com.sun.tools.jdeps;
import java.io.IOException;
+import java.lang.module.ModuleDescriptor;
+import java.util.Arrays;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -44,17 +48,18 @@
// need a way to determine JRE modules
SE_JRE("Java SE JRE", 4, "java.se", "jdk.charsets",
"jdk.crypto.ec", "jdk.crypto.pkcs11",
- "jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
+ "jdk.crypto.mscapi", "jdk.crypto.ucrypto",
"jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs"),
FULL_JRE("Full JRE", 5, "java.se.ee", "jdk.charsets",
"jdk.crypto.ec", "jdk.crypto.pkcs11",
"jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
- "jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs");
+ "jdk.localedata", "jdk.scripting.nashorn",
+ "jdk.unsupported", "jdk.zipfs");
final String name;
final int profile;
final String[] mnames;
- final Set<Module> modules = new HashSet<>();
+ final Map<String, Module> modules = new HashMap<>();
Profile(String name, int profile, String... mnames) {
this.name = name;
@@ -75,12 +80,18 @@
return JDK.isEmpty() ? 0 : Profile.values().length;
}
+ Optional<Module> findModule(String name) {
+ return modules.containsKey(name)
+ ? Optional.of(modules.get(name))
+ : Optional.empty();
+ }
+
/**
* Returns the Profile for the given package name; null if not found.
*/
public static Profile getProfile(String pn) {
for (Profile p : Profile.values()) {
- for (Module m : p.modules) {
+ for (Module m : p.modules.values()) {
if (m.packages().contains(pn)) {
return p;
}
@@ -94,7 +105,7 @@
*/
public static Profile getProfile(Module m) {
for (Profile p : Profile.values()) {
- if (p.modules.contains(m)) {
+ if (p.modules.containsValue(m)) {
return p;
}
}
@@ -102,34 +113,28 @@
}
private final static Set<Module> JDK = new HashSet<>();
- static synchronized void init(Map<String, Module> installed) {
- for (Profile p : Profile.values()) {
- for (String mn : p.mnames) {
- // this includes platform-dependent module that may not exist
- Module m = installed.get(mn);
- if (m != null) {
- p.addModule(installed, m);
- }
- }
- }
+ static synchronized void init(Map<String, Module> systemModules) {
+ Arrays.stream(Profile.values()).forEach(p ->
+ // this includes platform-dependent module that may not exist
+ Arrays.stream(p.mnames)
+ .filter(systemModules::containsKey)
+ .map(systemModules::get)
+ .forEach(m -> p.addModule(systemModules, m)));
// JDK modules should include full JRE plus other jdk.* modules
// Just include all installed modules. Assume jdeps is running
// in JDK image
- JDK.addAll(installed.values());
+ JDK.addAll(systemModules.values());
}
- private void addModule(Map<String, Module> installed, Module m) {
- modules.add(m);
- for (String n : m.requires().keySet()) {
- Module d = installed.get(n);
- if (d == null) {
- throw new InternalError("module " + n + " required by " +
- m.name() + " doesn't exist");
- }
- modules.add(d);
- }
+ private void addModule(Map<String, Module> systemModules, Module module) {
+ modules.put(module.name(), module);
+ module.descriptor().requires().stream()
+ .map(ModuleDescriptor.Requires::name)
+ .map(systemModules::get)
+ .forEach(m -> modules.put(m.name(), m));
}
+
// for debugging
public static void main(String[] args) throws IOException {
// find platform modules
@@ -139,14 +144,6 @@
for (Profile p : Profile.values()) {
String profileName = p.name;
System.out.format("%2d: %-10s %s%n", p.profile, profileName, p.modules);
- for (Module m: p.modules) {
- System.out.format("module %s%n", m.name());
- System.out.format(" requires %s%n", m.requires());
- for (Map.Entry<String,Set<String>> e: m.exports().entrySet()) {
- System.out.format(" exports %s %s%n", e.getKey(),
- e.getValue().isEmpty() ? "" : "to " + e.getValue());
- }
- }
}
System.out.println("All JDK modules:-");
JDK.stream().sorted(Comparator.comparing(Module::name))
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Thu May 19 10:55:33 2016 -0700
@@ -1,12 +1,10 @@
main.usage.summary=\
-Usage: {0} <options> [-m <module-name> | <classes...>]\n\
+Usage: {0} <options> <path ...>]\n\
use -h, -? or -help for a list of possible options
main.usage=\
-Usage: {0} <options> [-m <module-name> | <classes...>]\n\
-If -m <module-name> is specified, the specified module will be analyzed\n\
-otherwise, <classes> can be a pathname to a .class file, a directory,\n\
-a JAR file, or a fully-qualified class name.\n\
+Usage: {0} <options> <path ...>]\n\
+<path> can be a pathname to a .class file, a directory, a JAR file.\n\
\n\
Possible options include:
@@ -14,134 +12,138 @@
warn.prefix=Warning:
main.opt.h=\
-\ -h -? -help Print this usage message
+\ -h -? -help Print this usage message
main.opt.version=\
-\ -version Version information
+\ -version Version information
main.opt.v=\
-\ -v -verbose Print all class level dependencies\n\
-\ Equivalent to -verbose:class -filter:none.\n\
-\ -verbose:package Print package-level dependencies excluding\n\
-\ dependencies within the same package by default\n\
-\ -verbose:class Print class-level dependencies excluding\n\
-\ dependencies within the same package by default
-
-main.opt.f=\
-\ -f <regex> -filter <regex> Filter dependences matching the given pattern\n\
-\ If given multiple times, the last one will be used.\n\
-\ -filter:package Filter dependences within the same package (default)\n\
-\ -filter:module Filter dependences within the same module\n\
-\ -filter:archive Filter dependences within the same archive\n\
-\ -filter:none No -filter:package and -filter:archive filtering\n\
-\ Filtering specified via the -filter option still applies.
+\ -v -verbose Print all class level dependencies\n\
+\ Equivalent to -verbose:class -filter:none.\n\
+\ -verbose:package Print package-level dependencies excluding\n\
+\ dependencies within the same package by default\n\
+\ -verbose:class Print class-level dependencies excluding\n\
+\ dependencies within the same package by default
main.opt.s=\
-\ -s -summary Print dependency summary only.\n\
-\ If -s option is used with -m, the module descriptor of\n\
-\ the given module will be read to generate the graph.
+\ -s -summary Print dependency summary only.
-main.opt.p=\
-\ -p <pkgname> Finds dependences matching the given package name\n\
-\ -package <pkgname> (may be given multiple times).
+main.opt.f=\
+\ -f <regex> -filter <regex> Filter dependences matching the given\n\
+\ pattern. If given multiple times, the last\n\
+\ one will be used.\n\
+\ -filter:package Filter dependences within the same package.\n\
+\ This is the default.\n\
+\ -filter:archive Filter dependences within the same archive.\n\
+\ -filter:module Filter dependences within the same module.\n\
+\ -filter:none No -filter:package and -filter:archive\n\
+\ filtering. Filtering specified via the\n\
+\ -filter option still applies.\n\
+
+main.opt.p=\n\
+\Options to filter dependencies:\n\
+\ -p <pkgname> -package <pkgname> Finds dependences matching the given package\n\
+\ name (may be given multiple times).
main.opt.e=\
-\ -e <regex>\n\
-\ -regex <regex> Finds dependences matching the given pattern.
+\ -e <regex> -regex <regex> Finds dependences matching the given pattern.
main.opt.module=\
-\ -module <module-name> Finds dependences matching the given module name\n\
-\ (may be given multiple times).\n\
-\ -package, -regex, -requires are mutual exclusive.
+\ -module <module-name> Finds dependences matching the given module\n\
+\ name (may be given multiple times).\n\
+\ -package, -regex, -module are mutual exclusive.
-main.opt.include=\
-\ -include <regex> Restrict analysis to classes matching pattern\n\
-\ This option filters the list of classes to\n\
-\ be analyzed. It can be used together with\n\
-\ -p and -e which apply pattern to the dependences
+main.opt.include=\n\
+ \Options to filter classes to be analyzed:\n\
+\ -include <regex> Restrict analysis to classes matching pattern\n\
+\ This option filters the list of classes to\n\
+\ be analyzed. It can be used together with\n\
+\ -p and -e which apply pattern to the dependences
main.opt.P=\
-\ -P -profile Show profile containing a package
-
-main.opt.M=\
-\ -M Show module containing a package
+\ -P -profile Show profile containing a package
main.opt.cp=\
-\ -cp <path> -classpath <path> Specify where to find class files
+\ -cp <path> -classpath <path> Specify where to find class files
main.opt.mp=\
\ -mp <module path>...\n\
-\ -modulepath <module path>... Specify module path
+\ -modulepath <module path>... Specify module path
main.opt.upgrademodulepath=\
\ -upgrademodulepath <module path>... Specify upgrade module path
main.opt.m=\
-\ -m <module-name> Specify the name of the module and its transitive\n\
-\ dependences to be analyzed.
+\ -m <module-name> Specify the root module for analysis
main.opt.R=\
-\ -R -recursive Recursively traverse all run-time dependencies.\n\
-\ The -R option implies -filter:none. If -p, -e, -f\n\
-\ option is specified, only the matching dependences\n\
-\ are analyzed.
+\ -R -recursive Recursively traverse all run-time dependencies.\n\
+\ The -R option implies -filter:none. If -p,\n\
+\ -e, -foption is specified, only the matching\n\
+\ dependences are analyzed.
main.opt.ct=\
-\ -ct -compile-time Compile-time view of transitive dependencies\n\
-\ i.e. compile-time view of -R option. If a dependence\n\
-\ is found from a directory, a JAR file or a module,\n\
-\ all class files in that containing archive are analyzed.
+\ -ct -compile-time Compile-time view of transitive dependencies\n\
+\ i.e. compile-time view of -R option.\n\
+\ Analyzes the dependences per other given options\n\
+\ If a dependence is found from a directory,\n\
+\ a JAR file or a module, all classes in that \n\
+\ containing archive are analyzed.
main.opt.apionly=\
-\ -apionly Restrict analysis to APIs i.e. dependences\n\
-\ from the signature of public and protected\n\
-\ members of public classes including field\n\
-\ type, method parameter types, returned type,\n\
-\ checked exception types etc
+\ -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.genmoduleinfo=\
-\ -genmoduleinfo <dir> Generate module-info.java under the specified directory.\n\
-\ The specified JAR files will be analyzed.\n\
-\ This option cannot be used with -dotoutput or -cp.
+\ -genmoduleinfo <dir> Generate module-info.java under the specified\n\
+\ directory. The specified JAR files will be\n\
+\ analyzed. This option cannot be used with\n\
+\ -dotoutput or -cp.
main.opt.check=\
-\ -check Analyze the dependence of a given module specified via\n\
-\ -m option. It prints out the resulting module dependency\n\
-\ graph after transition reduction and also identifies any\n\
-\ unused qualified exports.
+\ -check <module-name>[,<module-name>...\n\
+\ Analyze the dependence of the specified modules\n\
+\ It prints the module descriptor, the resulting\n\
+\ module dependences after analysis and the\n\
+\ graph after transition reduction. It also\n\
+\ identifies any unused qualified exports.
main.opt.dotoutput=\
-\ -dotoutput <dir> Destination directory for DOT file output
+\ -dotoutput <dir> Destination directory for DOT file output
main.opt.jdkinternals=\
-\ -jdkinternals Finds class-level dependences on JDK internal APIs.\n\
-\ By default, it analyzes all classes on -classpath\n\
-\ and input files unless -include option is specified.\n\
-\ This option cannot be used with -p, -e and -s options.\n\
-\ WARNING: JDK internal APIs may not be accessible in\n\
-\ the next release.
+\ -jdkinternals Finds class-level dependences on JDK internal\n\
+\ APIs. By default, it analyzes all classes\n\
+\ on -classpath and input files unless -include\n\
+\ option is specified. This option cannot be\n\
+\ used with -p, -e and -s options.\n\
+\ WARNING: JDK internal APIs are inaccessible.
main.opt.depth=\
-\ -depth=<depth> Specify the depth of the transitive\n\
-\ dependency analysis
+\ -depth=<depth> Specify the depth of the transitive\n\
+\ dependency analysis
main.opt.q=\
-\ -q -quiet Do not show missing dependencies from -genmoduleinfo output.
+\ -q -quiet Do not show missing dependencies from \n\
+\ -genmoduleinfo output.
err.unknown.option=unknown option: {0}
err.missing.arg=no value given for {0}
err.invalid.arg.for.option=invalid argument for option: {0}
err.option.after.class=option must be specified before classes: {0}
-err.genmoduleinfo.not.jarfile={0} not valid for -genmoduleinfo option (must be JAR file)
+err.genmoduleinfo.not.jarfile={0} not valid for -genmoduleinfo option (must be non-modular JAR file)
err.profiles.msg=No profile information
err.exception.message={0}
err.invalid.path=invalid path: {0}
-err.invalid.module.option=-m {0} is set but {1} is specified.
-err.invalid.filters=Only one of -package (-p), -regex (-e), -requires option can be set
+err.invalid.module.option=Cannot set {0} with {1} option.
+err.invalid.filters=Only one of -package (-p), -regex (-e), -module option can be set
err.module.not.found=module not found: {0}
-err.root.module.not.set=-m is not set
-warn.invalid.arg=Invalid classname or pathname not exist: {0}
+err.root.module.not.set=root module set empty
+warn.invalid.arg=Path not exist: {0}
warn.split.package=package {0} defined in {1} {2}
warn.replace.useJDKInternals=\
JDK internal APIs are unsupported and private to JDK implementation that are\n\
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties Thu May 19 10:55:33 2016 -0700
@@ -1,6 +1,5 @@
-// No translation needed
+# No translation needed
com.sun.crypto.provider.SunJCE=Use java.security.Security.getProvider(provider-name) @since 1.3
-com.sun.image.codec=Use javax.imageio @since 1.4
com.sun.org.apache.xml.internal.security=Use java.xml.crypto @since 1.6
com.sun.org.apache.xml.internal.security.utils.Base64=Use java.util.Base64 @since 1.8
com.sun.org.apache.xml.internal.resolver=Use javax.xml.catalog @since 9
@@ -9,17 +8,34 @@
com.sun.rowset=Use javax.sql.rowset.RowSetProvider @since 1.7
com.sun.tools.javac.tree=Use com.sun.source @since 1.6
com.sun.tools.javac=Use javax.tools and javax.lang.model @since 1.6
+java.awt.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
+java.awt.dnd.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
+jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
sun.awt.image.codec=Use javax.imageio @since 1.4
-sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
-sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
-sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
-sun.misc.Service=Use java.util.ServiceLoader @since 1.6
+sun.awt.CausedFocusEvent=Use java.awt.event.FocusEvent::getCause @since 9
+sun.font.FontUtilities=See java.awt.Font.textRequiresLayout @since 9
+sun.reflect.Reflection=See StackWalker API @since 9
+sun.reflect.ReflectionFactory=See http://openjdk.java.net/jeps/260
+sun.misc.Unsafe=See http://openjdk.java.net/jeps/260
+sun.misc.Signal=See http://openjdk.java.net/jeps/260
+sun.misc.SignalHandler=See http://openjdk.java.net/jeps/260
sun.security.action=Use java.security.PrivilegedAction @since 1.1
sun.security.krb5=Use com.sun.security.jgss
sun.security.provider.PolicyFile=Use java.security.Policy.getInstance("JavaPolicy", new URIParameter(uri)) @since 1.6
sun.security.provider.Sun=Use java.security.Security.getProvider(provider-name) @since 1.3
sun.security.util.SecurityConstants=Use appropriate java.security.Permission subclass @since 1.1
sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4
-sun.tools.jar=Use java.util.jar or jar tool @since 1.2
-jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
+sun.tools.jar=Use java.util.jar or jar tool @since 1.2\
+# Internal APIs removed in JDK 9
+com.apple.eawt=Use java.awt.desktop and JEP 272 @since 9
+com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187
+com.sun.image.codec=Use javax.imageio @since 1.4
+sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
+sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
+sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
+sun.misc.Service=Use java.util.ServiceLoader @since 1.6
+sun.misc=Removed. See http://openjdk.java.net/jeps/260
+sun.reflect=Removed. See http://openjdk.java.net/jeps/260
+
+
--- a/langtools/test/tools/jdeps/APIDeps.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/APIDeps.java Thu May 19 10:55:33 2016 -0700
@@ -25,6 +25,7 @@
* @test
* @bug 8015912 8029216 8048063 8050804
* @summary Test -apionly and -jdkinternals options
+ * @library lib
* @modules java.base/sun.security.x509
* java.management
* jdk.jdeps/com.sun.tools.classfile
@@ -154,7 +155,8 @@
Map<String,String> jdeps(String... args) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- System.err.println("jdeps " + Arrays.toString(args));
+ System.err.println("jdeps " + Arrays.stream(args)
+ .collect(Collectors.joining(" ")));
int rc = com.sun.tools.jdeps.Main.run(args, pw);
pw.close();
String out = sw.toString();
--- a/langtools/test/tools/jdeps/Basic.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/Basic.java Thu May 19 10:55:33 2016 -0700
@@ -39,6 +39,8 @@
import java.nio.file.Path;
import java.util.*;
import java.util.regex.*;
+import java.util.stream.Collectors;
+
import static java.nio.file.StandardCopyOption.*;
public class Basic {
@@ -157,7 +159,7 @@
Map<String,String> jdeps(String... args) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- System.err.println("jdeps " + Arrays.toString(args));
+ System.err.println("jdeps " + Arrays.stream(args).collect(Collectors.joining(" ")));
int rc = com.sun.tools.jdeps.Main.run(args, pw);
pw.close();
String out = sw.toString();
--- a/langtools/test/tools/jdeps/CompilerUtils.java Thu May 19 10:55:33 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import javax.tools.JavaCompiler;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.StandardLocation;
-import javax.tools.ToolProvider;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-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.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public final class CompilerUtils {
- private CompilerUtils() { }
-
- /**
- * Compile all the java sources in {@code <source>/**} to
- * {@code <destination>/**}. The destination directory will be created if
- * it doesn't exist.
- *
- * All warnings/errors emitted by the compiler are output to System.out/err.
- *
- * @return true if the compilation is successful
- *
- * @throws IOException if there is an I/O error scanning the source tree or
- * creating the destination directory
- */
- public static boolean compile(Path source, Path destination, String... options)
- throws IOException
- {
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
-
- List<Path> sources
- = Files.find(source, Integer.MAX_VALUE,
- (file, attrs) -> (file.toString().endsWith(".java")))
- .collect(Collectors.toList());
-
- Files.createDirectories(destination);
- jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
- Arrays.asList(destination));
-
- List<String> opts = Arrays.asList(options);
- JavaCompiler.CompilationTask task
- = compiler.getTask(null, jfm, null, opts, null,
- jfm.getJavaFileObjectsFromPaths(sources));
-
- return task.call();
- }
-
- /**
- * Compile the specified module from the given module sourcepath
- *
- * All warnings/errors emitted by the compiler are output to System.out/err.
- *
- * @return true if the compilation is successful
- *
- * @throws IOException if there is an I/O error scanning the source tree or
- * creating the destination directory
- */
- public static boolean compileModule(Path source, Path destination,
- String moduleName, String... options) {
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
-
- try {
- Files.createDirectories(destination);
- jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
- Arrays.asList(destination));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
-
- Stream<String> opts = Arrays.stream(new String[] {
- "-modulesourcepath", source.toString(), "-m", moduleName
- });
- List<String> javacOpts = Stream.concat(opts, Arrays.stream(options))
- .collect(Collectors.toList());
- JavaCompiler.CompilationTask task
- = compiler.getTask(null, jfm, null, javacOpts, null, null);
- return task.call();
- }
-
-
- public static void cleanDir(Path dir) throws IOException {
- if (Files.notExists(dir)) return;
-
- Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
- throws IOException
- {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException e)
- throws IOException
- {
- if (e == null) {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- } else {
- // directory iteration failed
- throw e;
- }
- }
- });
- Files.deleteIfExists(dir);
- }
-}
--- a/langtools/test/tools/jdeps/DotFileTest.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/DotFileTest.java Thu May 19 10:55:33 2016 -0700
@@ -41,6 +41,7 @@
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.*;
+import java.util.stream.Collectors;
public class DotFileTest {
public static void main(String... args) throws Exception {
@@ -182,7 +183,7 @@
// invoke jdeps
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- System.err.println("jdeps " + args);
+ System.err.println("jdeps " + args.stream().collect(Collectors.joining(" ")));
int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[0]), pw);
pw.close();
String out = sw.toString();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/lib/CompilerUtils.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.tools.JavaCompiler;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+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.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public final class CompilerUtils {
+ private CompilerUtils() { }
+
+ /**
+ * Compile all the java sources in {@code <source>/**} to
+ * {@code <destination>/**}. The destination directory will be created if
+ * it doesn't exist.
+ *
+ * All warnings/errors emitted by the compiler are output to System.out/err.
+ *
+ * @return true if the compilation is successful
+ *
+ * @throws IOException if there is an I/O error scanning the source tree or
+ * creating the destination directory
+ */
+ public static boolean compile(Path source, Path destination, String... options)
+ throws IOException
+ {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
+
+ List<Path> sources
+ = Files.find(source, Integer.MAX_VALUE,
+ (file, attrs) -> (file.toString().endsWith(".java")))
+ .collect(Collectors.toList());
+
+ Files.createDirectories(destination);
+ jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
+ Arrays.asList(destination));
+
+ List<String> opts = Arrays.asList(options);
+ JavaCompiler.CompilationTask task
+ = compiler.getTask(null, jfm, null, opts, null,
+ jfm.getJavaFileObjectsFromPaths(sources));
+
+ return task.call();
+ }
+
+ /**
+ * Compile the specified module from the given module sourcepath
+ *
+ * All warnings/errors emitted by the compiler are output to System.out/err.
+ *
+ * @return true if the compilation is successful
+ *
+ * @throws IOException if there is an I/O error scanning the source tree or
+ * creating the destination directory
+ */
+ public static boolean compileModule(Path source, Path destination,
+ String moduleName, String... options) {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
+
+ try {
+ Files.createDirectories(destination);
+ jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
+ Arrays.asList(destination));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ Stream<String> opts = Arrays.stream(new String[] {
+ "-modulesourcepath", source.toString(), "-m", moduleName
+ });
+ List<String> javacOpts = Stream.concat(opts, Arrays.stream(options))
+ .collect(Collectors.toList());
+ JavaCompiler.CompilationTask task
+ = compiler.getTask(null, jfm, null, javacOpts, null, null);
+ return task.call();
+ }
+
+
+ public static void cleanDir(Path dir) throws IOException {
+ if (Files.notExists(dir)) return;
+
+ Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ throws IOException
+ {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException e)
+ throws IOException
+ {
+ if (e == null) {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ } else {
+ // directory iteration failed
+ throw e;
+ }
+ }
+ });
+ Files.deleteIfExists(dir);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/lib/JdepsUtil.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.tools.jdeps.Analyzer;
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.JdepsConfiguration;
+import com.sun.tools.jdeps.JdepsFilter;
+import com.sun.tools.jdeps.JdepsWriter;
+import com.sun.tools.jdeps.ModuleAnalyzer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Utilities to run jdeps command
+ */
+public final class JdepsUtil {
+ /*
+ * Runs jdeps with the given arguments
+ */
+ public static String[] jdeps(String... args) {
+ String lineSep = System.getProperty("line.separator");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ System.err.println("jdeps " + Arrays.stream(args).collect(Collectors.joining(" ")));
+ 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 out.split(lineSep);
+ }
+
+ public static Command newCommand(String cmd) {
+ return new Command(cmd);
+ }
+
+ public static class Command {
+
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ final JdepsFilter.Builder filter = new JdepsFilter.Builder().filter(true, true);
+ final JdepsConfiguration.Builder builder = new JdepsConfiguration.Builder();
+ final Set<String> requires = new HashSet<>();
+
+ Analyzer.Type verbose = Analyzer.Type.PACKAGE;
+ boolean apiOnly = false;
+
+ public Command(String cmd) {
+ System.err.println("============ ");
+ System.err.println(cmd);
+ }
+
+ public Command verbose(String verbose) {
+ switch (verbose) {
+ case "-verbose":
+ this.verbose = Analyzer.Type.VERBOSE;
+ filter.filter(false, false);
+ break;
+ case "-verbose:package":
+ this.verbose = Analyzer.Type.PACKAGE;
+ break;
+ case "-verbose:class":
+ this.verbose = Analyzer.Type.CLASS;
+ break;
+ case "-summary":
+ this.verbose = Analyzer.Type.SUMMARY;
+ break;
+ default:
+ throw new IllegalArgumentException(verbose);
+ }
+ return this;
+ }
+
+ public Command filter(String value) {
+ switch (value) {
+ case "-filter:package":
+ filter.filter(true, false);
+ break;
+ case "-filter:archive":
+ case "-filter:module":
+ filter.filter(false, true);
+ break;
+ default:
+ throw new IllegalArgumentException(value);
+ }
+ return this;
+ }
+
+ public Command addClassPath(String classpath) {
+ builder.addClassPath(classpath);
+ return this;
+ }
+
+ public Command addRoot(Path path) {
+ builder.addRoot(path);
+ return this;
+ }
+
+ public Command appModulePath(String modulePath) {
+ builder.appModulePath(modulePath);
+ return this;
+ }
+
+ public Command addmods(Set<String> mods) {
+ builder.addmods(mods);
+ return this;
+ }
+
+ public Command requires(Set<String> mods) {
+ requires.addAll(mods);
+ return this;
+ }
+
+ public Command matchPackages(Set<String> pkgs) {
+ filter.packages(pkgs);
+ return this;
+ }
+
+ public Command regex(String regex) {
+ filter.regex(Pattern.compile(regex));
+ return this;
+ }
+
+ public Command include(String regex) {
+ filter.includePattern(Pattern.compile(regex));
+ return this;
+ }
+
+ public Command includeSystemMoudles(String regex) {
+ filter.includeSystemModules(Pattern.compile(regex));
+ return this;
+ }
+
+ public Command apiOnly() {
+ this.apiOnly = true;
+ return this;
+ }
+
+ public JdepsConfiguration configuration() throws IOException {
+ JdepsConfiguration config = builder.build();
+ requires.forEach(name -> {
+ ModuleDescriptor md = config.findModuleDescriptor(name).get();
+ filter.requires(name, md.packages());
+ });
+ return config;
+ }
+
+ private JdepsWriter writer() {
+ return JdepsWriter.newSimpleWriter(pw, verbose);
+ }
+
+ public DepsAnalyzer getDepsAnalyzer() throws IOException {
+ return new DepsAnalyzer(configuration(), filter.build(), writer(),
+ verbose, apiOnly);
+ }
+
+ public ModuleAnalyzer getModuleAnalyzer(Set<String> mods) throws IOException {
+ // if -check is set, add to the root set and all modules are observable
+ addmods(mods);
+ builder.allModules();
+ return new ModuleAnalyzer(configuration(), pw, mods);
+ }
+
+ public void dumpOutput(PrintStream out) {
+ out.println(sw.toString());
+ }
+ }
+
+ /**
+ * Create a jar file using the list of files provided.
+ */
+ public static void createJar(Path jarfile, Path root, Stream<Path> files)
+ throws IOException {
+ Path dir = jarfile.getParent();
+ if (dir != null && Files.notExists(dir)) {
+ Files.createDirectories(dir);
+ }
+ try (JarOutputStream target = new JarOutputStream(
+ Files.newOutputStream(jarfile))) {
+ files.forEach(file -> add(root.relativize(file), file, target));
+ }
+ }
+
+ private static void add(Path path, Path source, JarOutputStream target) {
+ try {
+ String name = path.toString().replace(File.separatorChar, '/');
+ JarEntry entry = new JarEntry(name);
+ entry.setTime(source.toFile().lastModified());
+ target.putNextEntry(entry);
+ Files.copy(source, target);
+ target.closeEntry();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
--- a/langtools/test/tools/jdeps/m/Foo.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/m/Foo.java Thu May 19 10:55:33 2016 -0700
@@ -30,4 +30,3 @@
setF(new f.F());
}
}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/CheckModuleTest.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Tests split packages
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng CheckModuleTest
+ */
+
+import java.lang.module.ModuleDescriptor;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Set;
+
+import com.sun.tools.jdeps.ModuleAnalyzer;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+
+
+public class CheckModuleTest {
+ private static final String TEST_SRC = System.getProperty("test.src");
+ private static final String TEST_CLASSES = System.getProperty("test.classes");
+
+ private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+ private static final Path MODS_DIR = Paths.get("mods");
+
+ // m4 and m5 are analyzed. Others are compiled to make sure they are present
+ // on the module path for analysis
+ private static final Set<String> modules = Set.of("unsafe", "m4", "m5", "m6", "m7", "m8");
+
+ private static final String JAVA_BASE = "java.base";
+
+ /**
+ * Compiles classes used by the test
+ */
+ @BeforeTest
+ public void compileAll() throws Exception {
+ CompilerUtils.cleanDir(MODS_DIR);
+ modules.forEach(mn ->
+ assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)));
+ }
+
+ @DataProvider(name = "javaBase")
+ public Object[][] base() {
+ return new Object[][] {
+ { JAVA_BASE, new ModuleMetaData(JAVA_BASE)
+ },
+ };
+ };
+
+ @Test(dataProvider = "javaBase")
+ public void testJavaBase(String name, ModuleMetaData data) throws Exception {
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -check %s -mp %s%n", name, MODS_DIR)
+ );
+ jdeps.appModulePath(MODS_DIR.toString());
+
+ ModuleAnalyzer analyzer = jdeps.getModuleAnalyzer(Set.of(name));
+ assertTrue(analyzer.run());
+ jdeps.dumpOutput(System.err);
+
+ ModuleDescriptor[] descriptors = analyzer.descriptors(name);
+ for (int i=0; i < 3; i++) {
+ descriptors[i].requires().stream()
+ .forEach(req -> data.checkRequires(req));
+ }
+ }
+
+ @DataProvider(name = "modules")
+ public Object[][] unnamed() {
+ return new Object[][]{
+ { "m4", new ModuleMetaData[] {
+ // original
+ new ModuleMetaData("m4")
+ .requiresPublic("java.compiler")
+ .requires("java.logging")
+ // unnused exports
+ .exports("p4.internal", Set.of("m6", "m7")),
+ // suggested version
+ new ModuleMetaData("m4")
+ .requires("java.compiler"),
+ // reduced version
+ new ModuleMetaData("m4")
+ .requires("java.compiler")
+ }
+ },
+ { "m5", new ModuleMetaData[] {
+ // original
+ new ModuleMetaData("m5")
+ .requiresPublic("java.compiler")
+ .requiresPublic("java.logging")
+ .requires("java.sql")
+ .requiresPublic("m4"),
+ // suggested version
+ new ModuleMetaData("m5")
+ .requiresPublic("java.compiler")
+ .requires("java.logging")
+ .requiresPublic("java.sql")
+ .requiresPublic("m4"),
+ // reduced version
+ new ModuleMetaData("m5")
+ .requiresPublic("java.compiler")
+ .requiresPublic("java.sql")
+ .requiresPublic("m4"),
+ }
+ },
+ };
+ }
+
+ @Test(dataProvider = "modules")
+ public void modularTest(String name, ModuleMetaData[] data) throws Exception {
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -check %s -mp %s%n", name, MODS_DIR)
+ );
+ jdeps.appModulePath(MODS_DIR.toString());
+
+ ModuleAnalyzer analyzer = jdeps.getModuleAnalyzer(Set.of(name));
+ assertTrue(analyzer.run());
+ jdeps.dumpOutput(System.err);
+
+ // compare the module descriptors and the suggested versions
+ ModuleDescriptor[] descriptors = analyzer.descriptors(name);
+ for (int i=0; i < 3; i++) {
+ ModuleMetaData metaData = data[i];
+ descriptors[i].requires().stream()
+ .forEach(req -> metaData.checkRequires(req));
+ }
+
+ Map<String, Set<String>> unused = analyzer.unusedQualifiedExports(name);
+ // verify unuused qualified exports
+ assertEquals(unused, data[0].exports);
+ }
+
+}
--- a/langtools/test/tools/jdeps/modules/GenModuleInfo.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/GenModuleInfo.java Thu May 19 10:55:33 2016 -0700
@@ -24,8 +24,8 @@
/*
* @test
* @summary Tests jdeps -genmoduleinfo option
- * @library ..
- * @build CompilerUtils
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
* @modules jdk.jdeps/com.sun.tools.jdeps
* @run testng GenModuleInfo
*/
@@ -39,16 +39,12 @@
import java.util.Arrays;
import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
-
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@@ -86,7 +82,7 @@
for (String mn : modules) {
Path root = MODS_DIR.resolve(mn);
- createJar(LIBS_DIR.resolve(mn + ".jar"), root,
+ JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root,
Files.walk(root, Integer.MAX_VALUE)
.filter(f -> {
String fn = f.getFileName().toString();
@@ -100,7 +96,7 @@
Stream<String> files = Arrays.stream(modules)
.map(mn -> LIBS_DIR.resolve(mn + ".jar"))
.map(Path::toString);
- jdeps(Stream.concat(Stream.of("-cp"), files).toArray(String[]::new));
+ JdepsUtil.jdeps(Stream.concat(Stream.of("-cp"), files).toArray(String[]::new));
}
@Test
@@ -109,7 +105,7 @@
.map(mn -> LIBS_DIR.resolve(mn + ".jar"))
.map(Path::toString);
- jdeps(Stream.concat(Stream.of("-genmoduleinfo", DEST_DIR.toString()),
+ JdepsUtil.jdeps(Stream.concat(Stream.of("-genmoduleinfo", DEST_DIR.toString()),
files)
.toArray(String[]::new));
@@ -148,7 +144,7 @@
try (InputStream in1 = Files.newInputStream(p1);
InputStream in2 = Files.newInputStream(p2)) {
verify(ModuleDescriptor.read(in1),
- ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
+ ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
}
}
}
@@ -188,46 +184,4 @@
}
}
- /*
- * Runs jdeps with the given arguments
- */
- public static String[] jdeps(String... args) {
- String lineSep = System.getProperty("line.separator");
- 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 out.split(lineSep);
- }
-
- /**
- * Create a jar file using the list of files provided.
- */
- public static void createJar(Path jarfile, Path root, Stream<Path> files)
- throws IOException {
- try (JarOutputStream target = new JarOutputStream(
- Files.newOutputStream(jarfile))) {
- files.forEach(file -> add(root.relativize(file), file, target));
- }
- }
-
- private static void add(Path path, Path source, JarOutputStream target) {
- try {
- String name = path.toString().replace(File.separatorChar, '/');
- JarEntry entry = new JarEntry(name);
- entry.setTime(source.toFile().lastModified());
- target.putNextEntry(entry);
- Files.copy(source, target);
- target.closeEntry();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/ModuleMetaData.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.sun.tools.jdeps.DepsAnalyzer.Info.*;
+import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import static java.lang.module.ModuleDescriptor.*;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+
+public class ModuleMetaData {
+ public static final String JAVA_BASE = "java.base";
+
+ static final String INTERNAL = "(internal)";
+ static final String QUALIFIED = "(qualified)";
+ static final String JDK_INTERNAL = "JDK internal API";
+
+ final String moduleName;
+ final boolean isNamed;
+ final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
+ final Map<String, Dependence> references = new LinkedHashMap<>();
+ final Map<String, Set<String>> exports = new LinkedHashMap<>();
+
+ ModuleMetaData(String name) {
+ this(name, true);
+ }
+
+ ModuleMetaData(String name, boolean isNamed) {
+ this.moduleName = name;
+ this.isNamed = isNamed;
+ requires(JAVA_BASE); // implicit requires
+ }
+
+ String name() {
+ return moduleName;
+ }
+
+ ModuleMetaData requires(String name) {
+ requires.put(name, new ModuleRequires(name));
+ return this;
+ }
+
+ ModuleMetaData requiresPublic(String name) {
+ requires.put(name, new ModuleRequires(name, PUBLIC));
+ return this;
+ }
+
+ // for unnamed module
+ ModuleMetaData depends(String name) {
+ requires.put(name, new ModuleRequires(name));
+ return this;
+ }
+
+ ModuleMetaData reference(String origin, String target, String module) {
+ return dependence(origin, target, module, "");
+ }
+
+ ModuleMetaData internal(String origin, String target, String module) {
+ return dependence(origin, target, module, INTERNAL);
+ }
+
+ ModuleMetaData qualified(String origin, String target, String module) {
+ return dependence(origin, target, module, QUALIFIED);
+ }
+
+ ModuleMetaData jdkInternal(String origin, String target, String module) {
+ return dependence(origin, target, module, JDK_INTERNAL);
+ }
+
+ ModuleMetaData exports(String pn, Set<String> targets) {
+ exports.put(pn, targets);
+ return this;
+ }
+
+ private ModuleMetaData dependence(String origin, String target, String module, String access) {
+ references.put(key(origin, target), new Dependence(origin, target, module, access));
+ return this;
+ }
+
+ String key(String origin, String target) {
+ return origin + ":" + target;
+ }
+
+ void checkRequires(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
+ // System.err.format("%s: Expected %s Found %s %n", name, requires, adjacentNodes);
+ adjacentNodes.stream()
+ .forEach(v -> checkRequires(v.name));
+ assertEquals(adjacentNodes.size(), requires.size());
+ }
+
+ void checkRequires(String name) {
+ ModuleRequires req = requires.get(name);
+ if (req == null)
+ System.err.println(moduleName + ": unexpected requires " + name);
+ assertTrue(requires.containsKey(name));
+ }
+
+ void checkRequires(Requires require) {
+ String name = require.name();
+ if (name.equals(JAVA_BASE))
+ return;
+
+ ModuleRequires req = requires.get(name);
+ if (req == null)
+ System.err.format("%s: unexpected dependence %s%n", moduleName, name);
+
+ assertTrue(requires.containsKey(name));
+
+ assertEquals(require.modifiers(), req.modifiers());
+ }
+
+ void checkDependences(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
+ // System.err.format("%s: Expected %s Found %s %n", name, references, adjacentNodes);
+
+ adjacentNodes.stream()
+ .forEach(v -> checkDependence(name, v.name, v.source, v.info));
+ assertEquals(adjacentNodes.size(), references.size());
+ }
+
+ void checkDependence(String origin, String target, String module, DepsAnalyzer.Info info) {
+ String key = key(origin, target);
+ Dependence dep = references.get(key);
+ String access = "";
+ if (info == QUALIFIED_EXPORTED_API)
+ access = QUALIFIED;
+ else if (info == JDK_INTERNAL_API)
+ access = JDK_INTERNAL;
+ else if (info == INTERNAL_API)
+ access = INTERNAL;
+
+ assertTrue(references.containsKey(key));
+
+ assertEquals(dep.access, access);
+ assertEquals(dep.module, module);
+ }
+
+
+ public static class ModuleRequires {
+ final String name;
+ final Requires.Modifier mod;
+
+ ModuleRequires(String name) {
+ this.name = name;
+ this.mod = null;
+ }
+
+ ModuleRequires(String name, Requires.Modifier mod) {
+ this.name = name;
+ this.mod = mod;
+ }
+
+ Set<Requires.Modifier> modifiers() {
+ return mod != null ? Set.of(mod) : Collections.emptySet();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static class Dependence {
+ final String origin;
+ final String target;
+ final String module;
+ final String access;
+
+ Dependence(String origin, String target, String module, String access) {
+ this.origin = origin;
+ this.target = target;
+ this.module = module;
+ this.access = access;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s -> %s (%s) %s", origin, target, module, access);
+ }
+ }
+}
--- a/langtools/test/tools/jdeps/modules/ModuleTest.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/ModuleTest.java Thu May 19 10:55:33 2016 -0700
@@ -24,30 +24,28 @@
/*
* @test
* @summary Tests jdeps -m and -mp options on named modules and unnamed modules
- * @library ..
- * @build CompilerUtils
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
* @modules jdk.jdeps/com.sun.tools.jdeps
* @run testng ModuleTest
*/
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
-import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import java.io.File;
+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.stream.Collectors;
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.Graph;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
public class ModuleTest {
@@ -56,6 +54,7 @@
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
private static final Path MODS_DIR = Paths.get("mods");
+ private static final Path UNNAMED_DIR = Paths.get("unnamed");
// the names of the modules in this test
private static final String UNSUPPORTED = "unsupported";
@@ -66,62 +65,68 @@
@BeforeTest
public void compileAll() throws Exception {
CompilerUtils.cleanDir(MODS_DIR);
+ CompilerUtils.cleanDir(UNNAMED_DIR);
+
assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, UNSUPPORTED,
"-XaddExports:java.base/jdk.internal.perf=" + UNSUPPORTED));
// m4 is not referenced
Arrays.asList("m1", "m2", "m3", "m4")
.forEach(mn -> assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)));
+
+ assertTrue(CompilerUtils.compile(SRC_DIR.resolve("m3"), UNNAMED_DIR, "-mp", MODS_DIR.toString()));
+ Files.delete(UNNAMED_DIR.resolve("module-info.class"));
}
@DataProvider(name = "modules")
public Object[][] expected() {
return new Object[][]{
- { "m3", new Data("m3").requiresPublic("java.sql")
- .requiresPublic("m2")
- .requires("java.logging")
- .requiresPublic("m1")
- .reference("p3", "java.lang", "java.base")
- .reference("p3", "java.sql", "java.sql")
- .reference("p3", "java.util.logging", "java.logging")
- .reference("p3", "p1", "m1")
- .reference("p3", "p2", "m2")
- .qualified("p3", "p2.internal", "m2")
+ { "m3", new ModuleMetaData("m3").requiresPublic("java.sql")
+ .requiresPublic("m2")
+ .requires("java.logging")
+ .requiresPublic("m1")
+ .reference("p3", "java.lang", "java.base")
+ .reference("p3", "java.sql", "java.sql")
+ .reference("p3", "java.util.logging", "java.logging")
+ .reference("p3", "p1", "m1")
+ .reference("p3", "p2", "m2")
+ .qualified("p3", "p2.internal", "m2")
},
- { "m2", new Data("m2").requiresPublic("m1")
- .reference("p2", "java.lang", "java.base")
- .reference("p2", "p1", "m1")
- .reference("p2.internal", "java.lang", "java.base")
- .reference("p2.internal", "java.io", "java.base")
+ { "m2", new ModuleMetaData("m2").requiresPublic("m1")
+ .reference("p2", "java.lang", "java.base")
+ .reference("p2", "p1", "m1")
+ .reference("p2.internal", "java.lang", "java.base")
+ .reference("p2.internal", "java.io", "java.base")
},
- { "m1", new Data("m1").requires("unsupported")
- .reference("p1", "java.lang", "java.base")
- .reference("p1.internal", "java.lang", "java.base")
- .reference("p1.internal", "p1", "m1")
- .reference("p1.internal", "q", "unsupported")
+ { "m1", new ModuleMetaData("m1").requires("unsupported")
+ .reference("p1", "java.lang", "java.base")
+ .reference("p1.internal", "java.lang", "java.base")
+ .reference("p1.internal", "p1", "m1")
+ .reference("p1.internal", "q", "unsupported")
},
- { "unsupported", new Data("unsupported")
- .reference("q", "java.lang", "java.base")
- .jdkInternal("q", "jdk.internal.perf", "(java.base)")
+ { "unsupported", new ModuleMetaData("unsupported")
+ .reference("q", "java.lang", "java.base")
+ .jdkInternal("q", "jdk.internal.perf", "java.base")
},
};
}
@Test(dataProvider = "modules")
- public void modularTest(String name, Data data) {
- // print only the specified module
- String excludes = Arrays.stream(modules)
- .filter(mn -> !mn.endsWith(name))
- .collect(Collectors.joining(","));
- String[] result = jdeps("-exclude-modules", excludes,
- "-mp", MODS_DIR.toString(),
- "-m", name);
- assertTrue(data.check(result));
+ public void modularTest(String name, ModuleMetaData data) throws IOException {
+ // jdeps -modulepath mods -m <name>
+ runTest(data, MODS_DIR.toString(), Set.of(name));
+
+ // jdeps -modulepath libs/m1.jar:.... -m <name>
+ String mp = Arrays.stream(modules)
+ .filter(mn -> !mn.equals(name))
+ .map(mn -> MODS_DIR.resolve(mn).toString())
+ .collect(Collectors.joining(File.pathSeparator));
+ runTest(data, mp, Collections.emptySet(), MODS_DIR.resolve(name));
}
@DataProvider(name = "unnamed")
public Object[][] unnamed() {
return new Object[][]{
- { "m3", new Data("m3", false)
+ { "unnamed", new ModuleMetaData("unnamed", false)
.depends("java.sql")
.depends("java.logging")
.depends("m1")
@@ -133,178 +138,43 @@
.reference("p3", "p2", "m2")
.internal("p3", "p2.internal", "m2")
},
- { "unsupported", new Data("unsupported", false)
- .reference("q", "java.lang", "java.base")
- .jdkInternal("q", "jdk.internal.perf", "(java.base)")
- },
};
}
@Test(dataProvider = "unnamed")
- public void unnamedTest(String name, Data data) {
- String[] result = jdeps("-mp", MODS_DIR.toString(), MODS_DIR.resolve(name).toString());
- assertTrue(data.check(result));
- }
-
- /*
- * Runs jdeps with the given arguments
- */
- public static String[] jdeps(String... args) {
- String lineSep = System.getProperty("line.separator");
- 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 out.split(lineSep);
+ public void unnamedTest(String name, ModuleMetaData data) throws IOException {
+ runTest(data, MODS_DIR.toString(), Set.of("m1", "m2"), UNNAMED_DIR);
}
- static class Data {
- static final String INTERNAL = "(internal)";
- static final String QUALIFIED = "(qualified)";
- static final String JDK_INTERNAL = "JDK internal API";
-
- final String moduleName;
- final boolean isNamed;
- final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
- final Map<String, Dependence> references = new LinkedHashMap<>();
- Data(String name) {
- this(name, true);
- }
- Data(String name, boolean isNamed) {
- this.moduleName = name;
- this.isNamed = isNamed;
- requires("java.base"); // implicit requires
- }
+ private void runTest(ModuleMetaData data, String modulepath,
+ Set<String> roots, Path... paths)
+ throws IOException
+ {
+ // jdeps -modulepath <modulepath> -m root paths
- Data requires(String name) {
- requires.put(name, new ModuleRequires(name));
- return this;
- }
- Data requiresPublic(String name) {
- requires.put(name, new ModuleRequires(name, PUBLIC));
- return this;
- }
- // for unnamed module
- Data depends(String name) {
- requires.put(name, new ModuleRequires(name));
- return this;
- }
- Data reference(String origin, String target, String module) {
- return dependence(origin, target, module, "");
- }
- Data internal(String origin, String target, String module) {
- return dependence(origin, target, module, INTERNAL);
- }
- Data qualified(String origin, String target, String module) {
- return dependence(origin, target, module, QUALIFIED);
- }
- Data jdkInternal(String origin, String target, String module) {
- return dependence(origin, target, module, JDK_INTERNAL);
- }
- private Data dependence(String origin, String target, String module, String access) {
- references.put(key(origin, target), new Dependence(origin, target, module, access));
- return this;
- }
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -modulepath %s -addmods %s %s%n", MODS_DIR,
+ roots.stream().collect(Collectors.joining(",")), paths)
+ );
+ jdeps.appModulePath(modulepath)
+ .addmods(roots);
+ Arrays.stream(paths).forEach(jdeps::addRoot);
- String key(String origin, String target) {
- return origin+":"+target;
- }
- boolean check(String[] lines) {
- System.out.format("verifying module %s%s%n", moduleName, isNamed ? "" : " (unnamed module)");
- for (String l : lines) {
- String[] tokens = l.trim().split("\\s+");
- System.out.println(" " + Arrays.stream(tokens).collect(Collectors.joining(" ")));
- switch (tokens[0]) {
- case "module":
- assertEquals(tokens.length, 2);
- assertEquals(moduleName, tokens[1]);
- break;
- case "requires":
- String name = tokens.length == 2 ? tokens[1] : tokens[2];
- Modifier modifier = null;
- if (tokens.length == 3) {
- assertEquals("public", tokens[1]);
- modifier = PUBLIC;
- }
- checkRequires(name, modifier);
- break;
- default:
- if (tokens.length == 3) {
- // unnamed module requires
- assertFalse(isNamed);
- assertEquals(moduleName, tokens[0]);
- String mn = tokens[2];
- checkRequires(mn, null);
- } else {
- checkDependence(tokens);
- }
- }
- }
- return true;
- }
-
- private void checkRequires(String name, Modifier modifier) {
- assertTrue(requires.containsKey(name));
- ModuleRequires req = requires.get(name);
- assertEquals(req.mod, modifier);
- }
+ // run the analyzer
+ DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+ assertTrue(analyzer.run());
- private void checkDependence(String[] tokens) {
- assertTrue(tokens.length >= 4);
- String origin = tokens[0];
- String target = tokens[2];
- String module = tokens[3];
- String key = key(origin, target);
- assertTrue(references.containsKey(key));
- Dependence dep = references.get(key);
- if (tokens.length == 4) {
- assertEquals(dep.access, "");
- } else if (tokens.length == 5) {
- assertEquals(dep.access, tokens[4]);
- } else {
- // JDK internal API
- module = tokens[6];
- assertEquals(tokens.length, 7);
- assertEquals(tokens[3], "JDK");
- assertEquals(tokens[4], "internal");
- assertEquals(tokens[5], "API");
- }
- assertEquals(dep.module, module);
- }
+ // analyze result
+ Graph<DepsAnalyzer.Node> g1 = analyzer.moduleGraph();
+ g1.nodes().stream()
+ .filter(u -> u.name.equals(data.moduleName))
+ .forEach(u -> data.checkRequires(u.name, g1.adjacentNodes(u)));
- public static class ModuleRequires {
- final String name;
- final ModuleDescriptor.Requires.Modifier mod;
-
- ModuleRequires(String name) {
- this.name = name;
- this.mod = null;
- }
+ Graph<DepsAnalyzer.Node> g2 = analyzer.dependenceGraph();
+ g2.nodes().stream()
+ .filter(u -> u.name.equals(data.moduleName))
+ .forEach(u -> data.checkDependences(u.name, g2.adjacentNodes(u)));
- ModuleRequires(String name, ModuleDescriptor.Requires.Modifier mod) {
- this.name = name;
- this.mod = mod;
- }
- }
-
- public static class Dependence {
- final String origin;
- final String target;
- final String module;
- final String access;
-
- Dependence(String origin, String target, String module, String access) {
- this.origin = origin;
- this.target = target;
- this.module = module;
- this.access = access;
- }
- }
+ jdeps.dumpOutput(System.err);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/SplitPackage.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Tests split packages
+ * @library ../lib
+ * @build CompilerUtils
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng SplitPackage
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.JdepsConfiguration;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+public class SplitPackage {
+ private static final String TEST_SRC = System.getProperty("test.src");
+
+ private static final Path CLASSES_DIR = Paths.get("classes");
+
+ private static final String SPLIT_PKG_NAME = "javax.annotation";
+ private static final String JAVA_ANNOTATIONS_COMMON = "java.annotations.common";
+ /**
+ * Compiles classes used by the test
+ */
+ @BeforeTest
+ public void compileAll() throws Exception {
+ CompilerUtils.cleanDir(CLASSES_DIR);
+ assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "patches"), CLASSES_DIR));
+ }
+
+ @Test
+ public void runTest() throws Exception {
+ // Test jdeps classes
+ runTest(null);
+ // Test jdeps -addmods
+ runTest(JAVA_ANNOTATIONS_COMMON, SPLIT_PKG_NAME);
+ }
+
+ private void runTest(String root, String... splitPackages) throws Exception {
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -verbose:class -addmods %s %s%n",
+ root, CLASSES_DIR)
+ );
+ jdeps.verbose("-verbose:class")
+ .addRoot(CLASSES_DIR);
+ if (root != null)
+ jdeps.addmods(Set.of(root));
+
+
+ JdepsConfiguration config = jdeps.configuration();
+ Map<String, Set<String>> pkgs = config.splitPackages();
+
+ final Set<String> expected;
+ if (splitPackages != null) {
+ expected = Arrays.stream(splitPackages).collect(Collectors.toSet());
+ } else {
+ expected = Collections.emptySet();
+ }
+
+ if (!pkgs.keySet().equals(expected)) {
+ throw new RuntimeException(splitPackages.toString());
+ }
+
+ // java.annotations.common is not observable
+ DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+
+ assertTrue(analyzer.run());
+
+ jdeps.dumpOutput(System.err);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/TransitiveDeps.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Tests jdeps -m and -mp options on named modules and unnamed modules
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng TransitiveDeps
+ */
+
+import java.io.File;
+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.function.Function;
+import java.util.stream.Collectors;
+
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.Graph;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+public class TransitiveDeps {
+ private static final String TEST_SRC = System.getProperty("test.src");
+
+ private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+ private static final Path MODS_DIR = Paths.get("mods");
+ private static final Path LIBS_DIR = Paths.get("libs");
+
+ // the names of the modules in this test
+ private static String[] modules = new String[] {"unsafe", "m6", "m7"};
+ /**
+ * Compiles all modules used by the test
+ */
+ @BeforeTest
+ public void compileAll() throws Exception {
+ CompilerUtils.cleanDir(MODS_DIR);
+ CompilerUtils.cleanDir(LIBS_DIR);
+
+ for (String mn : modules) {
+ // compile a module
+ assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn));
+
+ // create JAR files with no module-info.class
+ Path root = MODS_DIR.resolve(mn);
+ JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root,
+ Files.walk(root, Integer.MAX_VALUE)
+ .filter(f -> {
+ String fn = f.getFileName().toString();
+ return fn.endsWith(".class") && !fn.equals("module-info.class");
+ }));
+ }
+ }
+
+ @DataProvider(name = "modules")
+ public Object[][] expected1() {
+ return new Object[][]{
+ { "m7",
+ List.of(new ModuleMetaData("m7")
+ .requires("m6")
+ .requires("unsafe")
+ .reference("p7.Main", "java.lang.Object", "java.base")
+ .reference("p7.Main", "java.lang.String", "java.base")
+ .reference("p7.Main", "org.safe.Lib", "unsafe")
+ .reference("p7.Main", "p6.safe.Lib", "m6"),
+ new ModuleMetaData("m6")
+ .requires("unsafe")
+ .reference("p6.indirect.UnsafeRef", "java.lang.Object", "java.base")
+ .reference("p6.indirect.UnsafeRef", "org.unsafe.UseUnsafe ", "unsafe")
+ .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
+ .reference("p6.safe.Lib", "java.lang.Class", "java.base")
+ .reference("p6.safe.Lib", "java.lang.Object", "java.base")
+ .reference("p6.safe.Lib", "java.lang.String", "java.base")
+ .reference("p6.safe.Lib", "java.lang.System", "java.base")
+ .reference("p6.safe.Lib", "org.safe.Lib", "unsafe"),
+ new ModuleMetaData("unsafe")
+ .requires("jdk.unsupported")
+ .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
+ .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
+ .reference("org.safe.Lib", "java.lang.Class", "java.base")
+ .reference("org.safe.Lib", "java.lang.Object", "java.base")
+ .reference("org.safe.Lib", "java.lang.String", "java.base")
+ .reference("org.safe.Lib", "java.lang.System", "java.base")
+ .reference("org.unsafe.UseUnsafe", "java.lang.Object", "java.base")
+ .jdkInternal("org.unsafe.UseUnsafe", "sun.misc.Unsafe", "java.base")
+ )
+ },
+ };
+ }
+
+ @Test(dataProvider = "modules")
+ public void testModulePath(String name, List<ModuleMetaData> data) throws IOException {
+ Set<String> roots = Set.of("m6", "unsafe");
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -modulepath %s -addmods %s -m %s%n", MODS_DIR,
+ roots.stream().collect(Collectors.joining(",")), name)
+ );
+ jdeps.verbose("-verbose:class")
+ .appModulePath(MODS_DIR.toString())
+ .addmods(roots)
+ .addmods(Set.of(name));
+
+ runJdeps(jdeps, data);
+
+ // run automatic modules
+ roots = Set.of("ALL-MODULE-PATH", "jdk.unsupported");
+
+ jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -modulepath %s -addmods %s -m %s%n", LIBS_DIR,
+ roots.stream().collect(Collectors.joining(",")), name)
+ );
+ jdeps.verbose("-verbose:class")
+ .appModulePath(LIBS_DIR.toString())
+ .addmods(roots)
+ .addmods(Set.of(name));
+
+ runJdeps(jdeps, data);
+ }
+
+ @DataProvider(name = "jars")
+ public Object[][] expected2() {
+ return new Object[][]{
+ { "m7", List.of(new ModuleMetaData("m7.jar")
+ .requires("m6.jar")
+ .requires("unsafe.jar")
+ .reference("p7.Main", "java.lang.Object", "java.base")
+ .reference("p7.Main", "java.lang.String", "java.base")
+ .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
+ .reference("p7.Main", "p6.safe.Lib", "m6.jar"))
+ },
+ };
+ }
+
+ @Test(dataProvider = "jars")
+ public void testClassPath(String name, List<ModuleMetaData> data) throws IOException {
+ String cpath = Arrays.stream(modules)
+ .filter(mn -> !mn.equals(name))
+ .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
+ .collect(Collectors.joining(File.pathSeparator));
+
+ Path jarfile = LIBS_DIR.resolve(name + ".jar");
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -classpath %s %s%n", cpath, jarfile)
+ );
+ jdeps.verbose("-verbose:class")
+ .addClassPath(cpath)
+ .addRoot(jarfile);
+
+ runJdeps(jdeps, data);
+ }
+
+ @DataProvider(name = "compileTimeView")
+ public Object[][] expected3() {
+ return new Object[][] {
+ {"m7",
+ List.of(new ModuleMetaData("m7.jar")
+ .requires("m6.jar")
+ .requires("unsafe.jar")
+ .reference("p7.Main", "java.lang.Object", "java.base")
+ .reference("p7.Main", "java.lang.String", "java.base")
+ .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
+ .reference("p7.Main", "p6.safe.Lib", "m6.jar"),
+ new ModuleMetaData("m6.jar")
+ .requires("unsafe.jar")
+ .reference("p6.indirect.UnsafeRef", "java.lang.Object", "java.base")
+ .reference("p6.indirect.UnsafeRef", "org.unsafe.UseUnsafe ", "unsafe.jar")
+ .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
+ .reference("p6.safe.Lib", "java.lang.Class", "java.base")
+ .reference("p6.safe.Lib", "java.lang.Object", "java.base")
+ .reference("p6.safe.Lib", "java.lang.String", "java.base")
+ .reference("p6.safe.Lib", "java.lang.System", "java.base")
+ .reference("p6.safe.Lib", "org.safe.Lib", "unsafe.jar"),
+ new ModuleMetaData("unsafe.jar")
+ .requires("jdk.unsupported")
+ .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
+ .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
+ .reference("org.safe.Lib", "java.lang.Class", "java.base")
+ .reference("org.safe.Lib", "java.lang.Object", "java.base")
+ .reference("org.safe.Lib", "java.lang.String", "java.base")
+ .reference("org.safe.Lib", "java.lang.System", "java.base")
+ .reference("org.unsafe.UseUnsafe", "java.lang.Object", "java.base")
+ .jdkInternal("org.unsafe.UseUnsafe", "sun.misc.Unsafe", "java.base")
+ )
+ },
+ };
+ }
+
+ @Test(dataProvider = "compileTimeView")
+ public void compileTimeView(String name, List<ModuleMetaData> data) throws IOException {
+ String cpath = Arrays.stream(modules)
+ .filter(mn -> !mn.equals(name))
+ .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
+ .collect(Collectors.joining(File.pathSeparator));
+
+ Path jarfile = LIBS_DIR.resolve(name + ".jar");
+
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -ct -classpath %s %s%n", cpath, jarfile)
+ );
+
+ jdeps.verbose("-verbose:class")
+ .addClassPath(cpath)
+ .addRoot(jarfile);
+
+ runJdeps(jdeps, data, true, 0 /* -recursive */);
+ }
+
+ @DataProvider(name = "recursiveDeps")
+ public Object[][] expected4() {
+ return new Object[][] {
+ {"m7",
+ List.of(new ModuleMetaData("m7.jar")
+ .requires("m6.jar")
+ .requires("unsafe.jar")
+ .reference("p7.Main", "java.lang.Object", "java.base")
+ .reference("p7.Main", "java.lang.String", "java.base")
+ .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
+ .reference("p7.Main", "p6.safe.Lib", "m6.jar"),
+ new ModuleMetaData("m6.jar")
+ .requires("unsafe.jar")
+ .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
+ .reference("p6.safe.Lib", "java.lang.Class", "java.base")
+ .reference("p6.safe.Lib", "java.lang.Object", "java.base")
+ .reference("p6.safe.Lib", "java.lang.String", "java.base")
+ .reference("p6.safe.Lib", "java.lang.System", "java.base")
+ .reference("p6.safe.Lib", "org.safe.Lib", "unsafe.jar"),
+ new ModuleMetaData("unsafe.jar")
+ .requires("jdk.unsupported")
+ .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
+ .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
+ .reference("org.safe.Lib", "java.lang.Class", "java.base")
+ .reference("org.safe.Lib", "java.lang.Object", "java.base")
+ .reference("org.safe.Lib", "java.lang.String", "java.base")
+ .reference("org.safe.Lib", "java.lang.System", "java.base")
+ )
+ },
+ };
+ }
+ @Test(dataProvider = "recursiveDeps")
+ public void recursiveDeps(String name, List<ModuleMetaData> data) throws IOException {
+ String cpath = Arrays.stream(modules)
+ .filter(mn -> !mn.equals(name))
+ .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
+ .collect(Collectors.joining(File.pathSeparator));
+
+ Path jarfile = LIBS_DIR.resolve(name + ".jar");
+
+ JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+ String.format("jdeps -R -classpath %s %s%n", cpath, jarfile)
+ );
+ jdeps.verbose("-verbose:class").filter("-filter:archive")
+ .addClassPath(cpath)
+ .addRoot(jarfile);
+
+ runJdeps(jdeps, data, true, 0 /* -recursive */);
+ }
+
+ private void runJdeps(JdepsUtil.Command jdeps, List<ModuleMetaData> data)
+ throws IOException
+ {
+ runJdeps(jdeps, data, false, 1 /* depth */);
+ }
+
+ private void runJdeps(JdepsUtil.Command jdeps, List<ModuleMetaData> data,
+ boolean compileTimeView, int depth)
+ throws IOException
+ {
+ // run the analyzer
+ DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+ assertTrue(analyzer.run(compileTimeView, depth));
+ jdeps.dumpOutput(System.err);
+
+ // analyze result
+ Graph<DepsAnalyzer.Node> g1 = analyzer.moduleGraph();
+ Map<String, ModuleMetaData> dataMap = data.stream()
+ .collect(Collectors.toMap(ModuleMetaData::name, Function.identity()));
+
+ // the returned graph contains all nodes such as java.base and jdk.unsupported
+ g1.nodes().stream()
+ .filter(u -> dataMap.containsKey(u.name))
+ .forEach(u -> {
+ ModuleMetaData md = dataMap.get(u.name);
+ md.checkRequires(u.name, g1.adjacentNodes(u));
+ });
+
+ Graph<DepsAnalyzer.Node> g2 = analyzer.dependenceGraph();
+
+ g2.nodes().stream()
+ .filter(u -> dataMap.containsKey(u.name))
+ .forEach(u -> {
+ ModuleMetaData md = dataMap.get(u.name);
+ md.checkDependences(u.name, g2.adjacentNodes(u));
+ });
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/patches/javax/annotation/NonNull.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javax.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+
+@Documented
+@Target({FIELD, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NonNull {
+}
--- a/langtools/test/tools/jdeps/modules/src/m4/module-info.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/src/m4/module-info.java Thu May 19 10:55:33 2016 -0700
@@ -22,8 +22,14 @@
*/
module m4 {
+ // not used in signature
requires public java.compiler;
+
+ // unused dependence
requires java.logging;
+
exports p4;
- exports p4.internal to m1,m2,m3;
+
+ // unuused qualified exports
+ exports p4.internal to m6,m7;
}
--- a/langtools/test/tools/jdeps/modules/src/m4/p4/internal/Impl.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/src/m4/p4/internal/Impl.java Thu May 19 10:55:33 2016 -0700
@@ -23,9 +23,14 @@
package p4.internal;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
public class Impl {
+ private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
public String name() {
return Impl.class.getName();
}
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m5/module-info.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m5 {
+ // m4 requires public java.compilerr
+ requires public m4;
+ requires public java.compiler;
+
+ // java.sql should be requires public
+ requires java.sql;
+
+ // java.logging is used for implementation only
+ requires public java.logging;
+
+ exports p5;
+
+ // m8 is not in the resolved graph but used by m8
+ exports p5.internal to m8;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m5/p5/Main.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p5;
+
+import java.sql.Driver;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+public class Main {
+ public void run(Driver driver) throws Exception {
+ driver.getParentLogger().config("test");
+
+ }
+
+ public p4.Lib getLib() {
+ return new p4.Lib();
+ }
+
+ public JavaCompiler getCompiler() {
+ return ToolProvider.getSystemJavaCompiler();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m5/p5/internal/T.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p5.internal;
+
+public class T {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m6/module-info.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m6 {
+ requires unsafe;
+
+ // no dependency on sun.misc.Unsafe directly or indirectly
+ exports p6.safe;
+
+ // direct dependency on org.unsafe
+ // hence indirect dependency on sun.misc.Unsafe
+ exports p6.indirect;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m6/p6/indirect/UnsafeRef.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p6.indirect;
+
+// indirectly depend on sun.misc.Unsafe
+public class UnsafeRef {
+ public static org.unsafe.UseUnsafe get() {
+ return new org.unsafe.UseUnsafe();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m6/p6/safe/Lib.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p6.safe;
+
+// no direct or indirect dependency on sun.misc.Unsafe
+public class Lib {
+ public static void doit() {
+ System.out.println(Lib.class.getName());
+ org.safe.Lib.doit();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m7/module-info.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m7 {
+ // only use classes that have no direct or indirect dependency
+ // to sun.misc.Unsafe
+ requires unsafe;
+ requires m6;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m7/p7/Main.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p7;
+
+// Only use classes in unsafe and m6 modules with no
+// direct or indirect dependency on sun.misc.Unsafe
+public class Main {
+ public static void main(String... args) {
+ p6.safe.Lib.doit();
+ org.safe.Lib.doit();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m8/module-info.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m8 {
+ requires m5;
+
+ // use p5.internal
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m8/p8/Main.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p8;
+
+import p5.internal.T;
+
+public class Main {
+ public static void main() {
+ T t = new T();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/module-info.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module unsafe {
+ requires jdk.unsupported;
+
+ // direct dependency on sun.misc.Unsafe
+ exports org.unsafe;
+
+ // no dependency on sun.misc.Unsafe directly or indirectly
+ exports org.safe;
+
+ // indirect dependency on sun.misc.Unsafe
+ exports org.indirect;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/org/indirect/UnsafeRef.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.indirect;
+
+// indirectly depend on sun.misc.Unsafe
+public class UnsafeRef {
+ public static org.unsafe.UseUnsafe get() {
+ return new org.unsafe.UseUnsafe();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/org/safe/Lib.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.safe;
+
+// no direct or indirect dependency on sun.misc.Unsafe
+public class Lib {
+ public static void doit() {
+ System.out.println(Lib.class.getName());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/org/unsafe/UseUnsafe.java Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.unsafe;
+
+import sun.misc.Unsafe;
+
+public class UseUnsafe {
+ static Unsafe unsafe = Unsafe.getUnsafe();
+}
--- a/langtools/test/tools/jdeps/unsupported/JDKUnsupportedTest.java Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/unsupported/JDKUnsupportedTest.java Thu May 19 10:55:33 2016 -0700
@@ -62,7 +62,7 @@
public void test(String filename, String[][] expected) {
Path path = Paths.get(TEST_CLASSES, filename);
- Map<String, String> result = jdeps("-M", path.toString());
+ Map<String, String> result = jdeps(path.toString());
for (String[] e : expected) {
String pn = e[0];
String module = e[1];