--- old/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java 2018-11-20 12:38:38.000000000 -0800 +++ new/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java 2018-11-20 12:38:38.000000000 -0800 @@ -38,6 +38,7 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Paths; import java.util.Collections; import java.util.Deque; import java.util.HashMap; @@ -172,7 +173,7 @@ parsedArchives.get(finder).add(archive); - trace("parsing %s %s%n", archive.getName(), archive.path()); + trace("parsing %s %s%n", archive.getName(), archive.getPathName()); FutureTask> task = new FutureTask<>(() -> { Set targets = new HashSet<>(); for (ClassFile cf : archive.reader().getClassFiles()) { --- old/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java 2018-11-20 12:38:39.000000000 -0800 +++ new/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java 2018-11-20 12:38:39.000000000 -0800 @@ -425,15 +425,19 @@ } }, - new Option(false, "-R", "-recursive") { - void process(JdepsTask task, String opt, String arg) { - task.options.depth = 0; + new Option(false, "-R", "-recursive", "--recursive") { + void process(JdepsTask task, String opt, String arg) throws BadArgs { + task.options.recursive = Options.RECURSIVE; // turn off filtering task.options.filterSameArchive = false; task.options.filterSamePackage = false; } }, - + new Option(false, "--no-recursive") { + void process(JdepsTask task, String opt, String arg) throws BadArgs { + task.options.recursive = Options.NO_RECURSIVE; + } + }, new Option(false, "-I", "--inverse") { void process(JdepsTask task, String opt, String arg) { task.options.inverse = true; @@ -447,9 +451,9 @@ new Option(false, "--compile-time") { void process(JdepsTask task, String opt, String arg) { task.options.compileTimeView = true; + task.options.recursive = Options.RECURSIVE; task.options.filterSamePackage = true; task.options.filterSameArchive = true; - task.options.depth = 0; } }, @@ -621,11 +625,13 @@ } private ListModuleDeps listModuleDeps(CommandOption option) throws BadArgs { + // do transitive dependence analysis unless --no-recursive is set + if (options.recursive != Options.NO_RECURSIVE) { + options.recursive = Options.RECURSIVE; + } // no need to record the dependences on the same archive or same package options.filterSameArchive = true; options.filterSamePackage = true; - // do transitive dependence analysis - options.depth = 0; switch (option) { case LIST_DEPS: return new ListModuleDeps(option, true, false); @@ -759,7 +765,7 @@ type, options.apiOnly); - boolean ok = analyzer.run(options.compileTimeView, options.depth); + boolean ok = analyzer.run(options.compileTimeView, options.depth()); // print skipped entries, if any if (!options.nowarning) { @@ -812,8 +818,8 @@ @Override boolean checkOptions() { - if (options.depth != 1) { - reportError("err.invalid.options", "-R", "--inverse"); + if (options.recursive != -1 || options.depth != -1) { + reportError("err.invalid.options", "--recursive and --no-recursive", "--inverse"); return false; } @@ -1033,7 +1039,7 @@ reduced, log, separator); - boolean ok = analyzer.run(options.depth, options.ignoreMissingDeps); + boolean ok = analyzer.run(options.depth(), options.ignoreMissingDeps); if (!ok) { reportError("err.cant.list.module.deps"); log.println(); @@ -1205,6 +1211,8 @@ } private static class Options { + static final int NO_RECURSIVE = 0; + static final int RECURSIVE = 1; boolean help; boolean version; boolean fullVersion; @@ -1223,7 +1231,8 @@ boolean filterSameArchive = false; Pattern filterRegex; String classpath; - int depth = 1; + int recursive = -1; // 0: --no-recursive, 1: --recursive + int depth = -1; Set requires = new HashSet<>(); Set packageNames = new HashSet<>(); Pattern regex; // apply to the dependences @@ -1252,6 +1261,19 @@ if (packageNames.size() > 0) count++; return count; } + + int depth() { + // ignore -depth if --no-recursive is set + if (recursive == NO_RECURSIVE) + return 1; + + // depth == 0 if recursive + if (recursive == RECURSIVE && depth == -1) + return 0; + + // default depth is 1 unless specified via -depth option + return depth == -1 ? 1 : depth; + } } private static class ResourceBundleHelper { --- old/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties 2018-11-20 12:38:41.000000000 -0800 +++ new/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties 2018-11-20 12:38:40.000000000 -0800 @@ -93,13 +93,18 @@ \ Adds modules to the root set for analysis main.opt.R=\ -\ -R -recursive Recursively traverse all run-time dependences.\n\ +\ -R\n\ +\ --recursive Recursively traverse all run-time dependences.\n\ \ The -R option implies -filter:none. If -p,\n\ \ -e, -f option is specified, only the matching\n\ \ dependences are analyzed. +main.opt.no-recursive=\ +\ --no-recursive Do not recursively traverse dependences. + main.opt.I=\ -\ -I --inverse Analyzes the dependences per other given options\n\ +\ -I\n\ +\ --inverse Analyzes the dependences per other given options\n\ \ and then find all artifacts that directly\n\ \ and indirectly depend on the matching nodes.\n\ \ This is equivalent to the inverse of\n\ @@ -166,7 +171,9 @@ \ --list-deps Lists the module dependences. It also prints\n\ \ any internal API packages if referenced.\n\ \ This option transitively analyzes libraries on\n\ -\ class path and module path if referenced. +\ class path and module path if referenced.\n\ +\ Use --no-recursive option for non-transitive\n\ +\ dependency analysis. main.opt.list-reduced-deps=\ \ --list-reduced-deps Same as --list-deps with not listing\n\ --- old/test/langtools/tools/jdeps/listdeps/ListModuleDeps.java 2018-11-20 12:38:42.000000000 -0800 +++ new/test/langtools/tools/jdeps/listdeps/ListModuleDeps.java 2018-11-20 12:38:42.000000000 -0800 @@ -247,6 +247,32 @@ }; } + @Test(dataProvider = "noRecursiveModuledeps") + public void testNoRecursiveModuleDeps(Path classes, String expected) { + JdepsRunner jdeps = JdepsRunner.run( + "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(), + "--print-module-deps", "--no-recursive", classes.toString() + ); + String output = Arrays.stream(jdeps.output()) + .map(s -> s.trim()) + .collect(Collectors.joining(",")); + assertEquals(output, expected); + } + + @DataProvider(name = "noRecursiveModuledeps") + public Object[][] noRecursiveModuledeps() { + Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class"); + + return new Object[][] { + // java.xml is an implied reads edge from java.sql + { CLASSES_DIR, "java.base,java.sql,jdk.unsupported"}, + { HI_CLASS, "java.base"}, + { FOO_CLASS, "java.base,java.sql"}, + { BAR_CLASS, "java.base,java.xml"}, + { UNSAFE_CLASS, "java.base,jdk.unsupported"}, + }; + } + @DataProvider(name = "recursiveDeps") public Object[][] recursiveDeps() { return new Object[][] {