36 import java.nio.file.Files; |
36 import java.nio.file.Files; |
37 import java.nio.file.Path; |
37 import java.nio.file.Path; |
38 import java.nio.file.Paths; |
38 import java.nio.file.Paths; |
39 import java.text.MessageFormat; |
39 import java.text.MessageFormat; |
40 import java.util.*; |
40 import java.util.*; |
|
41 import java.util.function.Function; |
|
42 import java.util.function.ToIntFunction; |
41 import java.util.jar.JarFile; |
43 import java.util.jar.JarFile; |
42 import java.util.regex.Pattern; |
44 import java.util.regex.Pattern; |
43 import java.util.stream.Collectors; |
|
44 import java.util.stream.Stream; |
|
45 |
45 |
46 /** |
46 /** |
47 * Implementation for the jdeps tool for static class dependency analysis. |
47 * Implementation for the jdeps tool for static class dependency analysis. |
48 */ |
48 */ |
49 class JdepsTask { |
49 class JdepsTask { |
312 task.options.addmods.addAll(mods); |
312 task.options.addmods.addAll(mods); |
313 } |
313 } |
314 }, |
314 }, |
315 new Option(true, "-m", "--module") { |
315 new Option(true, "-m", "--module") { |
316 void process(JdepsTask task, String opt, String arg) throws BadArgs { |
316 void process(JdepsTask task, String opt, String arg) throws BadArgs { |
317 task.options.rootModule = arg; |
317 if (!task.options.rootModules.isEmpty()) { |
|
318 throw new BadArgs("err.option.already.specified", opt); |
|
319 } |
|
320 task.options.rootModules.add(arg); |
318 task.options.addmods.add(arg); |
321 task.options.addmods.add(arg); |
319 } |
322 } |
320 }, |
323 }, |
321 new Option(true, "--multi-release") { |
324 new Option(true, "--multi-release") { |
322 void process(JdepsTask task, String opt, String arg) throws BadArgs { |
325 void process(JdepsTask task, String opt, String arg) throws BadArgs { |
348 } |
351 } |
349 }, |
352 }, |
350 new Option(true, "--require") { |
353 new Option(true, "--require") { |
351 void process(JdepsTask task, String opt, String arg) { |
354 void process(JdepsTask task, String opt, String arg) { |
352 task.options.requires.add(arg); |
355 task.options.requires.add(arg); |
|
356 task.options.addmods.add(arg); |
353 } |
357 } |
354 }, |
358 }, |
355 new Option(true, "-f", "-filter") { |
359 new Option(true, "-f", "-filter") { |
356 void process(JdepsTask task, String opt, String arg) { |
360 void process(JdepsTask task, String opt, String arg) { |
357 task.options.filterRegex = Pattern.compile(arg); |
361 task.options.filterRegex = Pattern.compile(arg); |
489 showVersion(options.fullVersion); |
493 showVersion(options.fullVersion); |
490 } |
494 } |
491 if (options.help || options.version || options.fullVersion) { |
495 if (options.help || options.version || options.fullVersion) { |
492 return EXIT_OK; |
496 return EXIT_OK; |
493 } |
497 } |
494 |
|
495 if (!inputArgs.isEmpty() && options.rootModule != null) { |
|
496 reportError("err.invalid.arg.for.option", "-m"); |
|
497 } |
|
498 |
|
499 if (options.numFilters() > 1) { |
498 if (options.numFilters() > 1) { |
500 reportError("err.invalid.filters"); |
499 reportError("err.invalid.filters"); |
501 return EXIT_CMDERR; |
500 return EXIT_CMDERR; |
502 } |
501 } |
503 |
502 |
541 .sorted(Map.Entry.comparingByKey()) |
540 .sorted(Map.Entry.comparingByKey()) |
542 .forEach(e -> log.println(getMessage("split.package", |
541 .forEach(e -> log.println(getMessage("split.package", |
543 e.getKey(), |
542 e.getKey(), |
544 e.getValue().toString()))); |
543 e.getValue().toString()))); |
545 |
544 |
546 // check if any module specified in --require is missing |
545 // check if any module specified in --add-modules, --require, and -m is missing |
547 Stream.concat(options.addmods.stream(), options.requires.stream()) |
546 options.addmods.stream() |
548 .filter(mn -> !config.isValidToken(mn)) |
547 .filter(mn -> !config.isValidToken(mn)) |
549 .forEach(mn -> config.findModule(mn).orElseThrow(() -> |
548 .forEach(mn -> config.findModule(mn).orElseThrow(() -> |
550 new UncheckedBadArgs(new BadArgs("err.module.not.found", mn)))); |
549 new UncheckedBadArgs(new BadArgs("err.module.not.found", mn)))); |
551 |
550 |
552 return command.run(config); |
551 return command.run(config); |
618 abstract class Command { |
617 abstract class Command { |
619 final CommandOption option; |
618 final CommandOption option; |
620 protected Command(CommandOption option) { |
619 protected Command(CommandOption option) { |
621 this.option = option; |
620 this.option = option; |
622 } |
621 } |
|
622 |
623 /** |
623 /** |
624 * Returns true if the command-line options are all valid; |
624 * Returns true if the command-line options are all valid; |
625 * otherwise, returns false. |
625 * otherwise, returns false. |
626 */ |
626 */ |
627 abstract boolean checkOptions(); |
627 abstract boolean checkOptions(); |
631 */ |
631 */ |
632 abstract boolean run(JdepsConfiguration config) throws IOException; |
632 abstract boolean run(JdepsConfiguration config) throws IOException; |
633 |
633 |
634 /** |
634 /** |
635 * Includes all modules on system module path and application module path |
635 * Includes all modules on system module path and application module path |
|
636 * |
|
637 * When a named module is analyzed, it will analyze the dependences |
|
638 * only. The method should be overridden when this command should |
|
639 * analyze all modules instead. |
636 */ |
640 */ |
637 boolean allModules() { |
641 boolean allModules() { |
638 return false; |
642 return false; |
639 } |
643 } |
640 |
644 |
677 // -summary cannot use with -verbose option |
681 // -summary cannot use with -verbose option |
678 if (options.verbose != null) { |
682 if (options.verbose != null) { |
679 reportError("err.invalid.options", "-v, -verbose", "-s, -summary"); |
683 reportError("err.invalid.options", "-v, -verbose", "-s, -summary"); |
680 return false; |
684 return false; |
681 } |
685 } |
|
686 } |
|
687 |
|
688 if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) { |
|
689 reportError("err.invalid.arg.for.option", "-m"); |
682 } |
690 } |
683 if (inputArgs.isEmpty() && !options.hasSourcePath()) { |
691 if (inputArgs.isEmpty() && !options.hasSourcePath()) { |
684 showHelp(); |
692 showHelp(); |
685 return false; |
693 return false; |
686 } |
694 } |
806 boolean ok = analyzer.run(); |
814 boolean ok = analyzer.run(); |
807 |
815 |
808 log.println(); |
816 log.println(); |
809 if (!options.requires.isEmpty()) |
817 if (!options.requires.isEmpty()) |
810 log.println(getMessage("inverse.transitive.dependencies.on", |
818 log.println(getMessage("inverse.transitive.dependencies.on", |
811 options.requires)); |
819 options.requires)); |
812 else |
820 else |
813 log.println(getMessage("inverse.transitive.dependencies.matching", |
821 log.println(getMessage("inverse.transitive.dependencies.matching", |
814 options.regex != null |
822 options.regex != null |
815 ? options.regex.toString() |
823 ? options.regex.toString() |
816 : "packages " + options.packageNames)); |
824 : "packages " + options.packageNames)); |
817 |
825 |
818 analyzer.inverseDependences().stream() |
826 analyzer.inverseDependences() |
819 .sorted(Comparator.comparing(this::sortPath)) |
827 .stream() |
820 .forEach(path -> log.println(path.stream() |
828 .sorted(comparator()) |
821 .map(Archive::getName) |
829 .map(this::toInversePath) |
822 .collect(joining(" <- ")))); |
830 .forEach(log::println); |
823 return ok; |
831 return ok; |
824 } |
832 } |
825 |
833 |
826 private String sortPath(Deque<Archive> path) { |
834 private String toInversePath(Deque<Archive> path) { |
827 return path.peekFirst().getName(); |
835 return path.stream() |
|
836 .map(Archive::getName) |
|
837 .collect(joining(" <- ")); |
|
838 } |
|
839 |
|
840 /* |
|
841 * Returns a comparator for sorting the inversed path, grouped by |
|
842 * the first module name, then the shortest path and then sort by |
|
843 * the module names of each path |
|
844 */ |
|
845 private Comparator<Deque<Archive>> comparator() { |
|
846 return Comparator.<Deque<Archive>, String> |
|
847 comparing(deque -> deque.peekFirst().getName()) |
|
848 .thenComparingInt(Deque::size) |
|
849 .thenComparing(this::toInversePath); |
|
850 } |
|
851 |
|
852 /* |
|
853 * Returns true if --require is specified so that all modules are |
|
854 * analyzed to find all modules that depend on the modules specified in the |
|
855 * --require option directly and indirectly |
|
856 */ |
|
857 public boolean allModules() { |
|
858 return options.requires.size() > 0; |
828 } |
859 } |
829 } |
860 } |
830 |
861 |
831 |
862 |
832 class GenModuleInfo extends Command { |
863 class GenModuleInfo extends Command { |
955 if (options.findJDKInternals) { |
989 if (options.findJDKInternals) { |
956 reportError("err.invalid.options", "-jdkinternals", |
990 reportError("err.invalid.options", "-jdkinternals", |
957 option); |
991 option); |
958 return false; |
992 return false; |
959 } |
993 } |
|
994 |
|
995 if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) { |
|
996 reportError("err.invalid.arg.for.option", "-m"); |
|
997 } |
960 if (inputArgs.isEmpty() && !options.hasSourcePath()) { |
998 if (inputArgs.isEmpty() && !options.hasSourcePath()) { |
961 showHelp(); |
999 showHelp(); |
962 return false; |
1000 return false; |
963 } |
1001 } |
964 return true; |
1002 return true; |
968 boolean run(JdepsConfiguration config) throws IOException { |
1006 boolean run(JdepsConfiguration config) throws IOException { |
969 return new ModuleExportsAnalyzer(config, |
1007 return new ModuleExportsAnalyzer(config, |
970 dependencyFilter(config), |
1008 dependencyFilter(config), |
971 reduced, |
1009 reduced, |
972 log).run(); |
1010 log).run(); |
973 } |
|
974 |
|
975 @Override |
|
976 boolean allModules() { |
|
977 return true; |
|
978 } |
1011 } |
979 } |
1012 } |
980 |
1013 |
981 |
1014 |
982 class GenDotFile extends AnalyzeDeps { |
1015 class GenDotFile extends AnalyzeDeps { |
1153 boolean inverse = false; |
1186 boolean inverse = false; |
1154 boolean compileTimeView = false; |
1187 boolean compileTimeView = false; |
1155 String systemModulePath = System.getProperty("java.home"); |
1188 String systemModulePath = System.getProperty("java.home"); |
1156 String upgradeModulePath; |
1189 String upgradeModulePath; |
1157 String modulePath; |
1190 String modulePath; |
1158 String rootModule; |
1191 Set<String> rootModules = new HashSet<>(); |
1159 Set<String> addmods = new HashSet<>(); |
1192 Set<String> addmods = new HashSet<>(); |
1160 Runtime.Version multiRelease; |
1193 Runtime.Version multiRelease; |
1161 |
1194 |
1162 boolean hasSourcePath() { |
1195 boolean hasSourcePath() { |
1163 return !addmods.isEmpty() || includePattern != null; |
1196 return !addmods.isEmpty() || includePattern != null; |