8168386: Fix jdeps verbose options
authormchung
Fri, 11 Nov 2016 17:32:21 -0800
changeset 41997 79da2a8f4274
parent 41996 389212e0746c
child 41998 feae61dc2280
8168386: Fix jdeps verbose options Reviewed-by: dfuchs, lancea
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
langtools/test/tools/jdeps/DotFileTest.java
langtools/test/tools/jdeps/Options.java
langtools/test/tools/jdeps/lib/JdepsRunner.java
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Fri Nov 11 11:50:11 2016 -0800
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Fri Nov 11 17:32:21 2016 -0800
@@ -25,8 +25,10 @@
 
 package com.sun.tools.jdeps;
 
+import com.sun.tools.jdeps.Analyzer.Type;
 import static com.sun.tools.jdeps.Analyzer.Type.*;
 import static com.sun.tools.jdeps.JdepsWriter.*;
+import static java.util.stream.Collectors.*;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -111,6 +113,10 @@
             this.aliases = aliases;
         }
 
+        Option(boolean hasArg, CommandOption cmd) {
+            this(hasArg, cmd.names());
+        }
+
         boolean isHidden() {
             return false;
         }
@@ -144,25 +150,46 @@
         }
     }
 
+    enum CommandOption {
+        ANALYZE_DEPS(""),
+        GENERATE_DOT_FILE("-dotoutput", "--dot-output"),
+        GENERATE_MODULE_INFO("--generate-module-info"),
+        LIST_DEPS("--list-deps"),
+        LIST_REDUCED_DEPS("--list-reduced-deps"),
+        CHECK_MODULES("--check");
+
+        private final String[] names;
+        CommandOption(String... names) {
+            this.names = names;
+        }
+
+        String[] names() {
+            return names;
+        }
+
+        @Override
+        public String toString() {
+            return names[0];
+        }
+    }
+
     static Option[] recognizedOptions = {
         new Option(false, "-h", "-?", "-help", "--help") {
             void process(JdepsTask task, String opt, String arg) {
                 task.options.help = true;
             }
         },
-        new Option(true, "-dotoutput", "--dot-output") {
+        new Option(true, CommandOption.GENERATE_DOT_FILE) {
             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);
+                if (task.command != null) {
+                    throw new BadArgs("err.command.set", task.command, opt);
                 }
-                task.options.dotOutputDir = Paths.get(arg);;
+                task.command = task.genDotFile(Paths.get(arg));
             }
         },
         new Option(false, "-s", "-summary") {
             void process(JdepsTask task, String opt, String arg) {
                 task.options.showSummary = true;
-                task.options.verbose = SUMMARY;
             }
         },
         new Option(false, "-v", "-verbose",
@@ -196,35 +223,48 @@
                 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, "--generate-module-info") {
-            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", "--jdk-internals") {
             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(false, "--list-deps", "--list-reduced-deps") {
-            void process(JdepsTask task, String opt, String arg) {
-                task.options.showModulesAddExports = true;
-                task.options.reduced = opt.equals("--list-reduced-deps");
+
+        new Option(true, CommandOption.CHECK_MODULES) {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                if (task.command != null) {
+                    throw new BadArgs("err.command.set", task.command, opt);
+                }
+                Set<String> mods =  Set.of(arg.split(","));
+                task.options.addmods.addAll(mods);
+                task.command = task.checkModuleDeps(mods);
+            }
+        },
+        new Option(true, CommandOption.GENERATE_MODULE_INFO) {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                if (task.command != null) {
+                    throw new BadArgs("err.command.set", task.command, opt);
+                }
+                task.command = task.genModuleInfo(Paths.get(arg));
+            }
+        },
+        new Option(false, CommandOption.LIST_DEPS) {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                if (task.command != null) {
+                    throw new BadArgs("err.command.set", task.command, opt);
+                }
+                task.command = task.listModuleDeps(false);
+            }
+        },
+        new Option(false, CommandOption.LIST_REDUCED_DEPS) {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                if (task.command != null) {
+                    throw new BadArgs("err.command.set", task.command, opt);
+                }
+                task.command = task.listModuleDeps(true);
             }
         },
 
@@ -419,6 +459,7 @@
     private final Options options = new Options();
     private final List<String> inputArgs = new ArrayList<>();
 
+    private Command command;
     private PrintWriter log;
     void setLog(PrintWriter out) {
         log = out;
@@ -445,55 +486,30 @@
             if (options.version || options.fullVersion) {
                 showVersion(options.fullVersion);
             }
+            if (options.help || options.version || options.fullVersion) {
+                return EXIT_OK;
+            }
+
             if (!inputArgs.isEmpty() && options.rootModule != null) {
                 reportError("err.invalid.arg.for.option", "-m");
             }
-            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 {
-                    showHelp();
-                    return EXIT_CMDERR;
-                }
-            }
-            if (options.genModuleInfo != null) {
-                if (options.dotOutputDir != null || options.classpath != null || options.hasFilter()) {
-                    showHelp();
-                    return EXIT_CMDERR;
-                }
-            }
 
             if (options.numFilters() > 1) {
                 reportError("err.invalid.filters");
                 return EXIT_CMDERR;
             }
 
-            if (options.inverse && options.depth != 1) {
-                reportError("err.invalid.inverse.option", "-R");
-                return EXIT_CMDERR;
-            }
-
-            if (options.inverse && options.numFilters() == 0) {
-                reportError("err.invalid.filters");
-                return EXIT_CMDERR;
+            // default command to analyze dependences
+            if (command == null) {
+                command = analyzeDeps();
             }
-
-            if ((options.findJDKInternals) && (options.hasFilter() || options.showSummary)) {
-                showHelp();
-                return EXIT_CMDERR;
-            }
-            if (options.showSummary && options.verbose != SUMMARY) {
-                showHelp();
-                return EXIT_CMDERR;
-            }
-            if (options.checkModuleDeps != null && !inputArgs.isEmpty()) {
-                reportError("err.invalid.module.option", inputArgs, "--check");
+            if (!command.checkOptions()) {
                 return EXIT_CMDERR;
             }
 
             boolean ok = run();
             return ok ? EXIT_OK : EXIT_ERROR;
+
         } catch (BadArgs|UncheckedBadArgs e) {
             reportError(e.getKey(), e.getArgs());
             if (e.showUsage()) {
@@ -515,13 +531,14 @@
     }
 
     boolean run() throws IOException {
-        try (JdepsConfiguration config = buildConfig()) {
+        try (JdepsConfiguration config = buildConfig(command.allModules())) {
 
             // detect split packages
-            config.splitPackages().entrySet().stream()
+            config.splitPackages().entrySet()
+                .stream()
                 .sorted(Map.Entry.comparingByKey())
                 .forEach(e -> System.out.format("split package: %s %s%n", e.getKey(),
-                    e.getValue().toString()));
+                                                e.getValue().toString()));
 
             // check if any module specified in --require is missing
             Stream.concat(options.addmods.stream(), options.requires.stream())
@@ -529,38 +546,11 @@
                 .forEach(mn -> config.findModule(mn).orElseThrow(() ->
                     new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
 
-            // --generate-module-info
-            if (options.genModuleInfo != null) {
-                return genModuleInfo(config);
-            }
-
-            // --check
-            if (options.checkModuleDeps != null) {
-                return new ModuleAnalyzer(config, log, options.checkModuleDeps).run();
-            }
-
-            if (options.showModulesAddExports) {
-                return new ModuleExportsAnalyzer(config,
-                                                 dependencyFilter(config),
-                                                 options.reduced,
-                                                 log).run();
-            }
-
-            if (options.dotOutputDir != null &&
-                (options.verbose == SUMMARY || options.verbose == MODULE) &&
-                !options.addmods.isEmpty() && inputArgs.isEmpty()) {
-                return new ModuleAnalyzer(config, log).genDotFiles(options.dotOutputDir);
-            }
-
-            if (options.inverse) {
-                return analyzeInverseDeps(config);
-            } else {
-                return analyzeDeps(config);
-            }
+            return command.run(config);
         }
     }
 
-    private JdepsConfiguration buildConfig() throws IOException {
+    private JdepsConfiguration buildConfig(boolean allModules) throws IOException {
         JdepsConfiguration.Builder builder =
             new JdepsConfiguration.Builder(options.systemModulePath);
 
@@ -568,7 +558,7 @@
                .appModulePath(options.modulePath)
                .addmods(options.addmods);
 
-        if (options.checkModuleDeps != null || options.showModulesAddExports) {
+        if (allModules) {
             // check all system modules in the image
             builder.allModules();
         }
@@ -592,148 +582,420 @@
         return builder.build();
     }
 
-    private boolean analyzeDeps(JdepsConfiguration config) throws IOException {
-        // output result
-        final JdepsWriter writer;
-        if (options.dotOutputDir != null) {
-            writer = new DotFileWriter(options.dotOutputDir,
-                                       options.verbose,
-                                       options.showProfile,
-                                       options.showModule,
-                                       options.showLabel);
-        } else {
-            writer = new SimpleWriter(log,
-                                      options.verbose,
-                                      options.showProfile,
-                                      options.showModule);
+    // ---- factory methods to create a Command
+
+    private AnalyzeDeps analyzeDeps() throws BadArgs {
+        return options.inverse ? new InverseAnalyzeDeps()
+                               : new AnalyzeDeps();
+    }
+
+    private GenDotFile genDotFile(Path dir) throws BadArgs {
+        if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
+            throw new BadArgs("err.invalid.path", dir.toString());
+        }
+        return new GenDotFile(dir);
+    }
+
+    private GenModuleInfo genModuleInfo(Path dir) throws BadArgs {
+        if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
+            throw new BadArgs("err.invalid.path", dir.toString());
+        }
+        return new GenModuleInfo(dir);
+    }
+
+    private ListModuleDeps listModuleDeps(boolean reduced) throws BadArgs {
+        return reduced ? new ListReducedDeps()
+                       : new ListModuleDeps();
+    }
+
+    private CheckModuleDeps checkModuleDeps(Set<String> mods) throws BadArgs {
+        return new CheckModuleDeps(mods);
+    }
+
+    abstract class Command {
+        final CommandOption option;
+        protected Command(CommandOption option) {
+            this.option = option;
+        }
+        /**
+         * Returns true if the command-line options are all valid;
+         * otherwise, returns false.
+         */
+        abstract boolean checkOptions();
+
+        /**
+         * Do analysis
+         */
+        abstract boolean run(JdepsConfiguration config) throws IOException;
+
+        /**
+         * Includes all modules on system module path and application module path
+         */
+        boolean allModules() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return option.toString();
+        }
+    }
+
+
+    /**
+     * Analyze dependences
+     */
+    class AnalyzeDeps extends Command {
+        JdepsWriter writer;
+        AnalyzeDeps() {
+            this(CommandOption.ANALYZE_DEPS);
+        }
+
+        AnalyzeDeps(CommandOption option) {
+            super(option);
+        }
+
+        @Override
+        boolean checkOptions() {
+            if (options.findJDKInternals) {
+                // cannot set any filter, -verbose and -summary option
+                if (options.showSummary || options.verbose != null) {
+                    reportError("err.invalid.options", "-summary or -verbose",
+                                "-jdkinternals");
+                    return false;
+                }
+                if (options.hasFilter()) {
+                    reportError("err.invalid.options", "--package, --regex, --require",
+                                "-jdkinternals");
+                    return false;
+                }
+            }
+            if (options.showSummary) {
+                // -summary cannot use with -verbose option
+                if (options.verbose != null) {
+                    reportError("err.invalid.options", "-v, -verbose", "-s, -summary");
+                    return false;
+                }
+            }
+            if (inputArgs.isEmpty() && !options.hasSourcePath()) {
+                showHelp();
+                return false;
+            }
+            return true;
+        }
+
+        /*
+         * Default is to show package-level dependencies
+         */
+        Type getAnalyzerType() {
+            if (options.showSummary)
+                return Type.SUMMARY;
+
+            if (options.findJDKInternals)
+                return Type.CLASS;
+
+            // default to package-level verbose
+           return options.verbose != null ? options.verbose : PACKAGE;
+        }
+
+        @Override
+        boolean run(JdepsConfiguration config) throws IOException {
+            Type type = getAnalyzerType();
+            // default to package-level verbose
+            JdepsWriter writer = new SimpleWriter(log,
+                                                  type,
+                                                  options.showProfile,
+                                                  options.showModule);
+
+            return run(config, writer, type);
         }
 
-        // analyze the dependencies
-        DepsAnalyzer analyzer = new DepsAnalyzer(config,
-                                                 dependencyFilter(config),
-                                                 writer,
-                                                 options.verbose,
-                                                 options.apiOnly);
+        boolean run(JdepsConfiguration config, JdepsWriter writer, Type type) throws IOException {
+
+
+            // analyze the dependencies
+            DepsAnalyzer analyzer = new DepsAnalyzer(config,
+                                                     dependencyFilter(config),
+                                                     writer,
+                                                     type,
+                                                     options.apiOnly);
+
+            boolean ok = analyzer.run(options.compileTimeView, options.depth);
+
+            // print skipped entries, if any
+            if (!options.nowarning) {
+                analyzer.archives()
+                    .forEach(archive -> archive.reader()
+                        .skippedEntries().stream()
+                        .forEach(name -> warning("warn.skipped.entry", name)));
+            }
+
+            if (options.findJDKInternals && !options.nowarning) {
+                Map<String, String> jdkInternals = new TreeMap<>();
+                Set<String> deps = analyzer.dependences();
+                // find the ones with replacement
+                deps.forEach(cn -> replacementFor(cn).ifPresent(
+                    repl -> jdkInternals.put(cn, repl))
+                );
+
+                if (!deps.isEmpty()) {
+                    log.println();
+                    warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
+                }
 
-        boolean ok = analyzer.run(options.compileTimeView, options.depth);
+                if (!jdkInternals.isEmpty()) {
+                    log.println();
+                    log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
+                    log.format("%-40s %s%n", "----------------", "---------------------");
+                    jdkInternals.entrySet().stream()
+                        .forEach(e -> {
+                            String key = e.getKey();
+                            String[] lines = e.getValue().split("\\n");
+                            for (String s : lines) {
+                                log.format("%-40s %s%n", key, s);
+                                key = "";
+                            }
+                        });
+                }
+            }
+            return ok;
+        }
+    }
+
 
-        // print skipped entries, if any
-        if (!options.nowarning) {
-            analyzer.archives()
-                .forEach(archive -> archive.reader()
-                    .skippedEntries().stream()
-                    .forEach(name -> warning("warn.skipped.entry", name)));
+    class InverseAnalyzeDeps extends AnalyzeDeps {
+        InverseAnalyzeDeps() {
+        }
+
+        @Override
+        boolean checkOptions() {
+            if (options.depth != 1) {
+                reportError("err.invalid.options", "-R", "--inverse");
+                return false;
+            }
+
+            if (options.numFilters() == 0) {
+                reportError("err.filter.not.specified");
+                return false;
+            }
+
+            if (!super.checkOptions()) {
+                return false;
+            }
+
+            return true;
         }
 
-        if (options.findJDKInternals && !options.nowarning) {
-            Map<String, String> jdkInternals = new TreeMap<>();
-            Set<String> deps = analyzer.dependences();
-            // find the ones with replacement
-            deps.forEach(cn -> replacementFor(cn).ifPresent(
-                repl -> jdkInternals.put(cn, repl))
-            );
+        @Override
+        boolean run(JdepsConfiguration config) throws IOException {
+            Type type = getAnalyzerType();
+
+            InverseDepsAnalyzer analyzer =
+                new InverseDepsAnalyzer(config,
+                                        dependencyFilter(config),
+                                        writer,
+                                        type,
+                                        options.apiOnly);
+            boolean ok = analyzer.run();
+
+            log.println();
+            if (!options.requires.isEmpty())
+                log.format("Inverse transitive dependences on %s%n", options.requires);
+            else
+                log.format("Inverse transitive dependences matching %s%n",
+                    options.regex != null
+                        ? options.regex.toString()
+                        : "packages " + options.packageNames);
+
+            analyzer.inverseDependences().stream()
+                .sorted(Comparator.comparing(this::sortPath))
+                .forEach(path -> log.println(path.stream()
+                    .map(Archive::getName)
+                    .collect(joining(" <- "))));
+            return ok;
+        }
+
+        private String sortPath(Deque<Archive> path) {
+            return path.peekFirst().getName();
+        }
+    }
+
 
-            if (!deps.isEmpty()) {
-                log.println();
-                warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
+    class GenModuleInfo extends Command {
+        final Path dir;
+        GenModuleInfo(Path dir) {
+            super(CommandOption.GENERATE_MODULE_INFO);
+            this.dir = dir;
+        }
+
+        @Override
+        boolean checkOptions() {
+            if (options.classpath != null) {
+                reportError("err.invalid.options", "-classpath",
+                            option);
+                return false;
+            }
+            if (options.hasFilter()) {
+                reportError("err.invalid.options", "--package, --regex, --require",
+                            option);
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        boolean run(JdepsConfiguration config) throws IOException {
+            // check if any JAR file contains unnamed package
+            for (String arg : inputArgs) {
+                try (ClassFileReader reader = ClassFileReader.newInstance(Paths.get(arg))) {
+                    Optional<String> classInUnnamedPackage =
+                        reader.entries().stream()
+                             .filter(n -> n.endsWith(".class"))
+                             .filter(cn -> toPackageName(cn).isEmpty())
+                             .findFirst();
+
+                    if (classInUnnamedPackage.isPresent()) {
+                        if (classInUnnamedPackage.get().equals("module-info.class")) {
+                            reportError("err.genmoduleinfo.not.jarfile", arg);
+                        } else {
+                            reportError("err.genmoduleinfo.unnamed.package", arg);
+                        }
+                        return false;
+                    }
+                }
             }
 
-            if (!jdkInternals.isEmpty()) {
-                log.println();
-                log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
-                log.format("%-40s %s%n", "----------------", "---------------------");
-                jdkInternals.entrySet().stream()
-                    .forEach(e -> {
-                        String key = e.getKey();
-                        String[] lines = e.getValue().split("\\n");
-                        for (String s : lines) {
-                            log.format("%-40s %s%n", key, s);
-                            key = "";
+            ModuleInfoBuilder builder
+                 = new ModuleInfoBuilder(config, inputArgs, dir);
+            boolean ok = builder.run();
+
+            if (!ok && !options.nowarning) {
+                log.println("ERROR: missing dependencies");
+                builder.visitMissingDeps(
+                    new Analyzer.Visitor() {
+                        @Override
+                        public void visitDependence(String origin, Archive originArchive,
+                                                    String target, Archive targetArchive) {
+                            if (builder.notFound(targetArchive))
+                                log.format("   %-50s -> %-50s %s%n",
+                                    origin, target, targetArchive.getName());
                         }
                     });
             }
+            return ok;
         }
-        return ok;
+
+        private String toPackageName(String name) {
+            int i = name.lastIndexOf('/');
+            return i > 0 ? name.replace('/', '.').substring(0, i) : "";
+        }
     }
 
-    private boolean analyzeInverseDeps(JdepsConfiguration config) throws IOException {
-        JdepsWriter writer = new SimpleWriter(log,
-                                              options.verbose,
-                                              options.showProfile,
-                                              options.showModule);
-
-        InverseDepsAnalyzer analyzer = new InverseDepsAnalyzer(config,
-                                                               dependencyFilter(config),
-                                                               writer,
-                                                               options.verbose,
-                                                               options.apiOnly);
-        boolean ok = analyzer.run();
+    class CheckModuleDeps extends Command {
+        final Set<String> modules;
+        CheckModuleDeps(Set<String> mods) {
+            super(CommandOption.CHECK_MODULES);
+            this.modules = mods;
+        }
 
-        log.println();
-        if (!options.requires.isEmpty())
-            log.format("Inverse transitive dependences on %s%n", options.requires);
-        else
-            log.format("Inverse transitive dependences matching %s%n",
-                options.regex != null
-                    ? options.regex.toString()
-                    : "packages " + options.packageNames);
+        @Override
+        boolean checkOptions() {
+            if (!inputArgs.isEmpty()) {
+                reportError("err.invalid.options", inputArgs, "--check");
+                return false;
+            }
+            return true;
+        }
 
-        analyzer.inverseDependences().stream()
-                .sorted(Comparator.comparing(this::sortPath))
-                .forEach(path -> log.println(path.stream()
-                                                .map(Archive::getName)
-                                                .collect(Collectors.joining(" <- "))));
-        return ok;
+        @Override
+        boolean run(JdepsConfiguration config) throws IOException {
+            if (!config.initialArchives().isEmpty()) {
+                String list = config.initialArchives().stream()
+                                    .map(Archive::getPathName).collect(joining(" "));
+                throw new UncheckedBadArgs(new BadArgs("err.invalid.options",
+                                                       list, "--check"));
+            }
+            return new ModuleAnalyzer(config, log, modules).run();
+        }
+
+        public boolean allModules() {
+            return true;
+        }
     }
 
-    private String sortPath(Deque<Archive> path) {
-        return path.peekFirst().getName();
+    class ListReducedDeps extends ListModuleDeps {
+        ListReducedDeps() {
+            super(CommandOption.LIST_REDUCED_DEPS, true);
+        }
     }
 
-    private boolean genModuleInfo(JdepsConfiguration config) throws IOException {
-        // check if any JAR file contains unnamed package
-        for (String arg : inputArgs) {
-            try (ClassFileReader reader = ClassFileReader.newInstance(Paths.get(arg))) {
-                Optional<String> classInUnnamedPackage =
-                    reader.entries().stream()
-                        .filter(n -> n.endsWith(".class"))
-                        .filter(cn -> toPackageName(cn).isEmpty())
-                        .findFirst();
+    class ListModuleDeps extends Command {
+        final boolean reduced;
+        ListModuleDeps() {
+            this(CommandOption.LIST_DEPS, false);
+        }
+        ListModuleDeps(CommandOption option, boolean reduced) {
+            super(option);
+            this.reduced = reduced;
+        }
 
-                if (classInUnnamedPackage.isPresent()) {
-                    if (classInUnnamedPackage.get().equals("module-info.class")) {
-                        reportError("err.genmoduleinfo.not.jarfile", arg);
-                    } else {
-                        reportError("err.genmoduleinfo.unnamed.package", arg);
-                    }
-                    return false;
-                }
+        @Override
+        boolean checkOptions() {
+            if (options.showSummary || options.verbose != null) {
+                reportError("err.invalid.options", "-summary or -verbose",
+                            option);
+                return false;
             }
+            if (options.findJDKInternals) {
+                reportError("err.invalid.options", "-jdkinternals",
+                            option);
+                return false;
+            }
+            if (inputArgs.isEmpty() && !options.hasSourcePath()) {
+                showHelp();
+                return false;
+            }
+            return true;
         }
 
-        ModuleInfoBuilder builder
-            = new ModuleInfoBuilder(config, inputArgs, options.genModuleInfo);
-        boolean ok = builder.run();
+        @Override
+        boolean run(JdepsConfiguration config) throws IOException {
+            return new ModuleExportsAnalyzer(config,
+                                             dependencyFilter(config),
+                                             reduced,
+                                             log).run();
+        }
 
-        if (!ok && !options.nowarning) {
-            log.println("ERROR: missing dependencies");
-            builder.visitMissingDeps(
-                new Analyzer.Visitor() {
-                    @Override
-                    public void visitDependence(String origin, Archive originArchive,
-                                                String target, Archive targetArchive) {
-                        if (builder.notFound(targetArchive))
-                            log.format("   %-50s -> %-50s %s%n",
-                                origin, target, targetArchive.getName());
-                    }
-                });
+        public boolean allModules() {
+            return true;
         }
-        return ok;
     }
 
-    private String toPackageName(String name) {
-        int i = name.lastIndexOf('/');
-        return i > 0 ? name.replace('/', '.').substring(0, i) : "";
+
+    class GenDotFile extends AnalyzeDeps {
+        final Path dotOutputDir;
+        GenDotFile(Path dotOutputDir) {
+            super(CommandOption.GENERATE_DOT_FILE);
+
+            this.dotOutputDir = dotOutputDir;
+        }
+
+        @Override
+        boolean run(JdepsConfiguration config) throws IOException {
+            if ((options.showSummary || options.verbose == MODULE) &&
+                !options.addmods.isEmpty() && inputArgs.isEmpty()) {
+                // print module descriptor
+                return new ModuleAnalyzer(config, log).genDotFiles(dotOutputDir);
+            }
+
+            Type type = getAnalyzerType();
+            JdepsWriter writer = new DotFileWriter(dotOutputDir,
+                                                   type,
+                                                   options.showProfile,
+                                                   options.showModule,
+                                                   options.showLabel);
+            return run(config, writer, type);
+        }
     }
 
     /**
@@ -875,14 +1137,11 @@
         boolean showLabel;
         boolean findJDKInternals;
         boolean nowarning = false;
-        // default is to show package-level dependencies
-        // and filter references from same package
-        Analyzer.Type verbose = PACKAGE;
+        Analyzer.Type verbose;
+        // default filter references from same package
         boolean filterSamePackage = true;
         boolean filterSameArchive = false;
         Pattern filterRegex;
-        Path dotOutputDir;
-        Path genModuleInfo;
         String classpath;
         int depth = 1;
         Set<String> requires = new HashSet<>();
@@ -892,15 +1151,17 @@
         Pattern includeSystemModulePattern;
         boolean inverse = false;
         boolean compileTimeView = false;
-        Set<String> checkModuleDeps;
         String systemModulePath = System.getProperty("java.home");
         String upgradeModulePath;
         String modulePath;
         String rootModule;
         Set<String> addmods = new HashSet<>();
         Runtime.Version multiRelease;
-        boolean showModulesAddExports;
-        boolean reduced;
+
+        boolean hasSourcePath() {
+            return !addmods.isEmpty() || includePattern != null ||
+                        includeSystemModulePattern != null;
+        }
 
         boolean hasFilter() {
             return numFilters() > 0;
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java	Fri Nov 11 11:50:11 2016 -0800
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java	Fri Nov 11 17:32:21 2016 -0800
@@ -70,14 +70,6 @@
     public ModuleAnalyzer(JdepsConfiguration config,
                           PrintWriter log,
                           Set<String> names) {
-
-        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;
 
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Fri Nov 11 11:50:11 2016 -0800
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Fri Nov 11 17:32:21 2016 -0800
@@ -151,7 +151,9 @@
 
 main.opt.list-deps=\
 \  --list-deps                   Lists the dependences and use of JDK internal\n\
-\                                APIs.\n\
+\                                APIs.
+
+main.opt.list-reduced-deps=\
 \  --list-reduced-deps           Same as --list-deps with not listing\n\
 \                                the implied reads edges from the module graph\n\
 \                                If module M1 depends on M2 and M3,\n\
@@ -171,6 +173,7 @@
 \                                multi-release jar files.  <version> should\n\
 \                                be integer >= 9 or base.
 
+err.command.set={0} and {1} options are specified.
 err.unknown.option=unknown option: {0}
 err.missing.arg=no value given for {0}
 err.invalid.arg.for.option=invalid argument for option: {0}
@@ -180,11 +183,10 @@
 err.profiles.msg=No profile information
 err.exception.message={0}
 err.invalid.path=invalid path: {0}
-err.invalid.module.option=Cannot set {0} with {1} option.
-err.invalid.filters=Only one of --package (-p), --regex (-e), --require option can be set
+err.invalid.options={0} cannot be used with {1} option
 err.module.not.found=module not found: {0}
 err.root.module.not.set=root module set empty
-err.invalid.inverse.option={0} cannot be used with --inverse option
+err.filter.not.specified=--package (-p), --regex (-e), --require option must be specified
 err.multirelease.option.exists={0} is not a multi-release jar file, but the --multi-release option is set
 err.multirelease.option.notfound={0} is a multi-release jar file, but the --multi-release option is not set
 err.multirelease.version.associated=class {0} already associated with version {1}, trying to add version {2}
--- a/langtools/test/tools/jdeps/DotFileTest.java	Fri Nov 11 11:50:11 2016 -0800
+++ b/langtools/test/tools/jdeps/DotFileTest.java	Fri Nov 11 17:32:21 2016 -0800
@@ -136,8 +136,6 @@
 
         // with -P option
         List<String> argsWithDashP = new ArrayList<>();
-        argsWithDashP.add("-dotoutput");
-        argsWithDashP.add(dotoutput.toString());
         argsWithDashP.add("-P");
         argsWithDashP.addAll(args);
 
@@ -162,8 +160,6 @@
 
         // with -P option
         List<String> argsWithDashP = new ArrayList<>();
-        argsWithDashP.add("-dotoutput");
-        argsWithDashP.add(dotoutput.toString());
         argsWithDashP.add("-P");
         argsWithDashP.addAll(args);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/Options.java	Fri Nov 11 17:32:21 2016 -0800
@@ -0,0 +1,92 @@
+/*
+ * 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
+ * @bug 8168386
+ * @summary Test option validation
+ * @modules jdk.jdeps
+ * @library lib
+ * @build JdepsRunner
+ * @run testng Options
+ */
+
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+public class Options {
+    private static final String TEST_CLASSES = System.getProperty("test.classes");
+
+    @DataProvider(name = "errors")
+    public Object[][] errors() {
+        return new Object[][]{
+            {
+                new String[] { "-summary", "-v", TEST_CLASSES },
+                "-v, -verbose cannot be used with -s, -summary option"
+            },
+            {
+                new String[] { "-jdkinternal", "-summary", TEST_CLASSES },
+                "-summary or -verbose cannot be used with -jdkinternals option"
+            },
+            {
+                new String[] { "-jdkinternal", "-p", "java.lang", TEST_CLASSES },
+                "--package, --regex, --require cannot be used with  -jdkinternals option"
+            },
+            {
+                new String[] { "--inverse", TEST_CLASSES },
+                "--package (-p), --regex (-e), --require option must be specified"
+            },
+            {
+                new String[] { "--inverse", "-R", TEST_CLASSES },
+                "-R cannot be used with --inverse option"
+            },
+            {
+                new String[] { "--generate-module-info", "dots", "-cp", TEST_CLASSES },
+                "-classpath cannot be used with --generate-module-info option"
+            },
+            {
+                new String[] { "--list-deps", "-summary", TEST_CLASSES },
+                "--list-deps and --list-reduced-deps options are specified"
+            },
+            {
+                new String[] { "--list-deps", "--list-reduced-deps", TEST_CLASSES },
+                "--list-deps and --list-reduced-deps options are specified"
+            },
+        };
+    }
+
+    @Test(dataProvider = "errors")
+    public void test(String[] options, String expected) {
+        jdepsError(options).outputContains(expected);
+    }
+
+
+    public static JdepsRunner jdepsError(String... args) {
+        JdepsRunner jdeps = new JdepsRunner(args);
+        assertTrue(jdeps.run(true) != 0);
+        return jdeps;
+    }
+}
--- a/langtools/test/tools/jdeps/lib/JdepsRunner.java	Fri Nov 11 11:50:11 2016 -0800
+++ b/langtools/test/tools/jdeps/lib/JdepsRunner.java	Fri Nov 11 17:32:21 2016 -0800
@@ -75,7 +75,7 @@
     }
 
     public boolean outputContains(String s) {
-        return stdout.toString().contains(s);
+        return stdout.toString().contains(s) || stderr.toString().contains(s);
     }
 
     public void printStdout(PrintStream stream) {