47 * |
47 * |
48 */ |
48 */ |
49 public class ModuleExportsAnalyzer extends DepsAnalyzer { |
49 public class ModuleExportsAnalyzer extends DepsAnalyzer { |
50 // source archive to its dependences and JDK internal APIs it references |
50 // source archive to its dependences and JDK internal APIs it references |
51 private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>(); |
51 private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>(); |
52 private final boolean showJdkInternals; |
52 private final Map<String, Set<String>> missingDeps = new HashMap<>(); |
|
53 private final boolean showInternals; |
53 private final boolean reduced; |
54 private final boolean reduced; |
54 private final PrintWriter writer; |
55 private final PrintWriter writer; |
55 private final String separator; |
56 private final String separator; |
56 public ModuleExportsAnalyzer(JdepsConfiguration config, |
57 public ModuleExportsAnalyzer(JdepsConfiguration config, |
57 JdepsFilter filter, |
58 JdepsFilter filter, |
58 boolean showJdkInternals, |
59 boolean showInternals, |
59 boolean reduced, |
60 boolean reduced, |
60 PrintWriter writer, |
61 PrintWriter writer, |
61 String separator) { |
62 String separator) { |
62 super(config, filter, null, |
63 super(config, filter, null, |
63 Analyzer.Type.PACKAGE, |
64 Analyzer.Type.PACKAGE, |
64 false /* all classes */); |
65 false /* all classes */); |
65 this.showJdkInternals = showJdkInternals; |
66 this.showInternals = showInternals; |
66 this.reduced = reduced; |
67 this.reduced = reduced; |
67 this.writer = writer; |
68 this.writer = writer; |
68 this.separator = separator; |
69 this.separator = separator; |
69 } |
70 } |
70 |
71 |
71 @Override |
72 public boolean run(int maxDepth, boolean ignoreMissingDeps) throws IOException { |
72 public boolean run() throws IOException { |
73 // use compile time view so that the entire archive on classpath is analyzed |
73 // analyze dependences |
74 boolean rc = super.run(true, maxDepth); |
74 boolean rc = super.run(); |
|
75 |
75 |
76 // A visitor to record the module-level dependences as well as |
76 // A visitor to record the module-level dependences as well as |
77 // use of JDK internal APIs |
77 // use of internal APIs |
78 Analyzer.Visitor visitor = (origin, originArchive, target, targetArchive) -> { |
78 Analyzer.Visitor visitor = (origin, originArchive, target, targetArchive) -> { |
79 Set<String> jdkInternals = |
79 Set<String> internals = |
80 deps.computeIfAbsent(originArchive, _k -> new HashMap<>()) |
80 deps.computeIfAbsent(originArchive, _k -> new HashMap<>()) |
81 .computeIfAbsent(targetArchive, _k -> new HashSet<>()); |
81 .computeIfAbsent(targetArchive, _k -> new HashSet<>()); |
82 |
82 |
83 Module module = targetArchive.getModule(); |
83 Module module = targetArchive.getModule(); |
84 if (showJdkInternals && originArchive.getModule() != module && |
84 if (showInternals && originArchive.getModule() != module && |
85 module.isJDK() && !module.isExported(target)) { |
85 module.isNamed() && !module.isExported(target, module.name())) { |
86 // use of JDK internal APIs |
86 // use of internal APIs |
87 jdkInternals.add(target); |
87 internals.add(target); |
|
88 } |
|
89 if (!ignoreMissingDeps && Analyzer.notFound(targetArchive)) { |
|
90 Set<String> notFound = |
|
91 missingDeps.computeIfAbsent(origin, _k -> new HashSet<>()); |
|
92 notFound.add(target); |
88 } |
93 } |
89 }; |
94 }; |
90 |
95 |
91 // visit the dependences |
96 // visit the dependences |
92 archives.stream() |
97 archives.stream() |
93 .filter(analyzer::hasDependences) |
98 .filter(analyzer::hasDependences) |
94 .sorted(Comparator.comparing(Archive::getName)) |
99 .sorted(Comparator.comparing(Archive::getName)) |
95 .forEach(archive -> analyzer.visitDependences(archive, visitor)); |
100 .forEach(archive -> analyzer.visitDependences(archive, visitor)); |
96 |
101 |
|
102 // error if any missing dependence |
|
103 if (!rc || !missingDeps.isEmpty()) { |
|
104 return false; |
|
105 } |
|
106 |
|
107 Map<Module, Set<String>> internalPkgs = internalPackages(); |
97 Set<Module> modules = modules(); |
108 Set<Module> modules = modules(); |
98 if (showJdkInternals) { |
109 if (showInternals) { |
99 // print modules and JDK internal API dependences |
110 // print modules and JDK internal API dependences |
100 printDependences(modules); |
111 Stream.concat(modules.stream(), internalPkgs.keySet().stream()) |
|
112 .sorted(Comparator.comparing(Module::name)) |
|
113 .distinct() |
|
114 .forEach(m -> { |
|
115 if (internalPkgs.containsKey(m)) { |
|
116 internalPkgs.get(m).stream() |
|
117 .forEach(pn -> writer.format(" %s/%s%s", m, pn, separator)); |
|
118 } else { |
|
119 writer.format(" %s%s", m, separator); |
|
120 } |
|
121 }); |
101 } else { |
122 } else { |
102 // print module dependences |
123 // print module dependences |
103 writer.println(modules.stream().map(Module::name).sorted() |
124 writer.println(modules.stream().map(Module::name).sorted() |
104 .collect(Collectors.joining(separator))); |
125 .collect(Collectors.joining(separator))); |
105 } |
126 } |
106 return rc; |
127 return rc; |
107 } |
128 } |
108 |
129 |
|
130 /* |
|
131 * Prints missing dependences |
|
132 */ |
|
133 void visitMissingDeps(Analyzer.Visitor visitor) { |
|
134 archives.stream() |
|
135 .filter(analyzer::hasDependences) |
|
136 .sorted(Comparator.comparing(Archive::getName)) |
|
137 .filter(m -> analyzer.requires(m).anyMatch(Analyzer::notFound)) |
|
138 .forEach(m -> { |
|
139 analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE, Analyzer::notFound); |
|
140 }); |
|
141 } |
|
142 |
109 private Set<Module> modules() { |
143 private Set<Module> modules() { |
110 // build module graph |
144 // build module graph |
111 ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration); |
145 ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration); |
112 Module root = new RootModule("root"); |
146 Module root = new RootModule(); |
113 builder.addModule(root); |
147 builder.addModule(root); |
114 // find named module dependences |
148 // find named module dependences |
115 dependenceStream() |
149 dependenceStream() |
116 .flatMap(map -> map.keySet().stream()) |
150 .flatMap(map -> map.keySet().stream()) |
117 .filter(m -> m.getModule().isNamed() |
151 .filter(m -> m.getModule().isNamed() && !configuration.rootModules().contains(m)) |
118 && !configuration.rootModules().contains(m)) |
|
119 .map(Archive::getModule) |
152 .map(Archive::getModule) |
120 .forEach(m -> builder.addEdge(root, m)); |
153 .forEach(m -> builder.addEdge(root, m)); |
121 |
154 |
122 // build module dependence graph |
155 // build module dependence graph |
123 // if reduced is set, apply transition reduction |
156 // if reduced is set, apply transition reduction |
124 Graph<Module> g = reduced ? builder.reduced() : builder.build(); |
157 Graph<Module> g = reduced ? builder.reduced() : builder.build(); |
125 return g.adjacentNodes(root); |
158 return g.adjacentNodes(root); |
126 } |
159 } |
127 |
160 |
128 private void printDependences(Set<Module> modules) { |
161 private Map<Module, Set<String>> internalPackages() { |
129 // find use of JDK internals |
162 Map<Module, Set<String>> internalPkgs = new HashMap<>(); |
130 Map<Module, Set<String>> jdkinternals = new HashMap<>(); |
|
131 dependenceStream() |
163 dependenceStream() |
132 .flatMap(map -> map.entrySet().stream()) |
164 .flatMap(map -> map.entrySet().stream()) |
133 .filter(e -> e.getValue().size() > 0) |
165 .filter(e -> e.getValue().size() > 0) |
134 .forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(), |
166 .forEach(e -> internalPkgs.computeIfAbsent(e.getKey().getModule(), |
135 _k -> new TreeSet<>()) |
167 _k -> new TreeSet<>()) |
136 .addAll(e.getValue())); |
168 .addAll(e.getValue())); |
137 |
169 return internalPkgs; |
138 // print modules and JDK internal API dependences |
|
139 Stream.concat(modules.stream(), jdkinternals.keySet().stream()) |
|
140 .sorted(Comparator.comparing(Module::name)) |
|
141 .distinct() |
|
142 .forEach(m -> { |
|
143 if (jdkinternals.containsKey(m)) { |
|
144 jdkinternals.get(m).stream() |
|
145 .forEach(pn -> writer.format(" %s/%s%s", m, pn, separator)); |
|
146 } else { |
|
147 writer.format(" %s%s", m, separator); |
|
148 } |
|
149 }); |
|
150 } |
170 } |
151 |
171 |
152 /* |
172 /* |
153 * Returns a stream of dependence map from an Archive to the set of JDK |
173 * Returns a stream of dependence map from an Archive to the set of JDK |
154 * internal APIs being used. |
174 * internal APIs being used. |