mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-03 23:18:28 +00:00
8167057: jdeps option to list modules and internal APIs for @modules for test dev
Reviewed-by: dfuchs
This commit is contained in:
parent
4f435e9295
commit
97cee45deb
@ -50,7 +50,7 @@ import static java.util.stream.Collectors.*;
|
||||
*
|
||||
* Type of filters:
|
||||
* source filter: -include <pattern>
|
||||
* target filter: -package, -regex, -requires
|
||||
* target filter: -package, -regex, --require
|
||||
*
|
||||
* The initial archive set for analysis includes
|
||||
* 1. archives specified in the command line arguments
|
||||
@ -146,7 +146,9 @@ public class DepsAnalyzer {
|
||||
// analyze the dependencies collected
|
||||
analyzer.run(archives, finder.locationToArchive());
|
||||
|
||||
writer.generateOutput(archives, analyzer);
|
||||
if (writer != null) {
|
||||
writer.generateOutput(archives, analyzer);
|
||||
}
|
||||
} finally {
|
||||
finder.shutdown();
|
||||
}
|
||||
@ -156,7 +158,7 @@ public class DepsAnalyzer {
|
||||
/**
|
||||
* Returns the archives for reporting that has matching dependences.
|
||||
*
|
||||
* If -requires is set, they should be excluded.
|
||||
* If --require is set, they should be excluded.
|
||||
*/
|
||||
Set<Archive> archives() {
|
||||
if (filter.requiresFilter().isEmpty()) {
|
||||
@ -165,7 +167,7 @@ public class DepsAnalyzer {
|
||||
.filter(Archive::hasDependences)
|
||||
.collect(Collectors.toSet());
|
||||
} else {
|
||||
// use the archives that have dependences and not specified in -requires
|
||||
// use the archives that have dependences and not specified in --require
|
||||
return archives.stream()
|
||||
.filter(filter::include)
|
||||
.filter(source -> !filter.requiresFilter().contains(source))
|
||||
@ -360,16 +362,17 @@ public class DepsAnalyzer {
|
||||
Archive source = dep.originArchive();
|
||||
Archive target = dep.targetArchive();
|
||||
String pn = dep.target();
|
||||
if ((verbose == CLASS || verbose == VERBOSE)) {
|
||||
if (verbose == CLASS || verbose == VERBOSE) {
|
||||
int i = dep.target().lastIndexOf('.');
|
||||
pn = i > 0 ? dep.target().substring(0, i) : "";
|
||||
}
|
||||
final Info info;
|
||||
Module targetModule = target.getModule();
|
||||
if (source == target) {
|
||||
info = Info.MODULE_PRIVATE;
|
||||
} else if (!target.getModule().isNamed()) {
|
||||
} else if (!targetModule.isNamed()) {
|
||||
info = Info.EXPORTED_API;
|
||||
} else if (target.getModule().isExported(pn)) {
|
||||
} else if (targetModule.isExported(pn) && !targetModule.isJDKUnsupported()) {
|
||||
info = Info.EXPORTED_API;
|
||||
} else {
|
||||
Module module = target.getModule();
|
||||
|
||||
@ -41,7 +41,7 @@ import java.util.stream.Stream;
|
||||
* 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. -requires specifies to match target dependence from the given module
|
||||
* 3. --require 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
|
||||
@ -166,7 +166,7 @@ public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
// accepts target that is JDK class but not exported
|
||||
Module module = targetArchive.getModule();
|
||||
return originArchive != targetArchive &&
|
||||
module.isJDK() && !module.isExported(target.getPackageName());
|
||||
isJDKInternalPackage(module, target.getPackageName());
|
||||
} else if (filterSameArchive) {
|
||||
// accepts origin and target that from different archive
|
||||
return originArchive != targetArchive;
|
||||
@ -174,6 +174,18 @@ public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the package is an internal package of the given module.
|
||||
*/
|
||||
public boolean isJDKInternalPackage(Module module, String pn) {
|
||||
if (module.isJDKUnsupported()) {
|
||||
// its exported APIs are unsupported
|
||||
return true;
|
||||
}
|
||||
|
||||
return module.isJDK() && !module.isExported(pn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@ -221,6 +221,12 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
},
|
||||
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");
|
||||
}
|
||||
},
|
||||
|
||||
// ---- paths option ----
|
||||
new Option(true, "-cp", "-classpath", "--class-path") {
|
||||
@ -517,13 +523,13 @@ class JdepsTask {
|
||||
.forEach(e -> System.out.format("split package: %s %s%n", e.getKey(),
|
||||
e.getValue().toString()));
|
||||
|
||||
// check if any module specified in -requires is missing
|
||||
// check if any module specified in --require is missing
|
||||
Stream.concat(options.addmods.stream(), options.requires.stream())
|
||||
.filter(mn -> !config.isValidToken(mn))
|
||||
.forEach(mn -> config.findModule(mn).orElseThrow(() ->
|
||||
new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
|
||||
|
||||
// --gen-module-info
|
||||
// --generate-module-info
|
||||
if (options.genModuleInfo != null) {
|
||||
return genModuleInfo(config);
|
||||
}
|
||||
@ -533,6 +539,13 @@ class JdepsTask {
|
||||
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()) {
|
||||
@ -555,7 +568,7 @@ class JdepsTask {
|
||||
.appModulePath(options.modulePath)
|
||||
.addmods(options.addmods);
|
||||
|
||||
if (options.checkModuleDeps != null) {
|
||||
if (options.checkModuleDeps != null || options.showModulesAddExports) {
|
||||
// check all system modules in the image
|
||||
builder.allModules();
|
||||
}
|
||||
@ -597,10 +610,10 @@ class JdepsTask {
|
||||
|
||||
// analyze the dependencies
|
||||
DepsAnalyzer analyzer = new DepsAnalyzer(config,
|
||||
dependencyFilter(config),
|
||||
writer,
|
||||
options.verbose,
|
||||
options.apiOnly);
|
||||
dependencyFilter(config),
|
||||
writer,
|
||||
options.verbose,
|
||||
options.apiOnly);
|
||||
|
||||
boolean ok = analyzer.run(options.compileTimeView, options.depth);
|
||||
|
||||
@ -727,7 +740,7 @@ class JdepsTask {
|
||||
* Returns a filter used during dependency analysis
|
||||
*/
|
||||
private JdepsFilter dependencyFilter(JdepsConfiguration config) {
|
||||
// Filter specified by -filter, -package, -regex, and -requires options
|
||||
// Filter specified by -filter, -package, -regex, and --require options
|
||||
JdepsFilter.Builder builder = new JdepsFilter.Builder();
|
||||
|
||||
// source filters
|
||||
@ -737,7 +750,7 @@ class JdepsTask {
|
||||
builder.filter(options.filterSamePackage, options.filterSameArchive);
|
||||
builder.findJDKInternals(options.findJDKInternals);
|
||||
|
||||
// -requires
|
||||
// --require
|
||||
if (!options.requires.isEmpty()) {
|
||||
options.requires.stream()
|
||||
.forEach(mn -> {
|
||||
@ -757,8 +770,8 @@ class JdepsTask {
|
||||
|
||||
// check if system module is set
|
||||
config.rootModules().stream()
|
||||
.map(Module::name)
|
||||
.forEach(builder::includeIfSystemModule);
|
||||
.map(Module::name)
|
||||
.forEach(builder::includeIfSystemModule);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@ -886,6 +899,8 @@ class JdepsTask {
|
||||
String rootModule;
|
||||
Set<String> addmods = new HashSet<>();
|
||||
Runtime.Version multiRelease;
|
||||
boolean showModulesAddExports;
|
||||
boolean reduced;
|
||||
|
||||
boolean hasFilter() {
|
||||
return numFilters() > 0;
|
||||
|
||||
@ -50,7 +50,7 @@ public abstract class JdepsWriter {
|
||||
final boolean showProfile;
|
||||
final boolean showModule;
|
||||
|
||||
private JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
|
||||
JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
|
||||
this.type = type;
|
||||
this.showProfile = showProfile;
|
||||
this.showModule = showModule;
|
||||
@ -318,8 +318,7 @@ public abstract class JdepsWriter {
|
||||
}
|
||||
|
||||
// exported API
|
||||
boolean jdkunsupported = Module.JDK_UNSUPPORTED.equals(module.name());
|
||||
if (module.isExported(pn) && !jdkunsupported) {
|
||||
if (module.isExported(pn) && !module.isJDKUnsupported()) {
|
||||
return showProfileOrModule(module);
|
||||
}
|
||||
|
||||
|
||||
@ -126,12 +126,13 @@ class Module extends Archive {
|
||||
* 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;
|
||||
}
|
||||
|
||||
public boolean isJDKUnsupported() {
|
||||
return JDK_UNSUPPORTED.equals(this.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this module to a strict module with the given dependences
|
||||
*
|
||||
|
||||
@ -41,10 +41,8 @@ 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;
|
||||
@ -179,50 +177,43 @@ public class ModuleAnalyzer {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
ModuleDescriptor reduced() {
|
||||
Graph.Builder<Module> bd = new Graph.Builder<>();
|
||||
private Graph<Module> buildReducedGraph() {
|
||||
ModuleGraphBuilder rpBuilder = new ModuleGraphBuilder(configuration);
|
||||
rpBuilder.addModule(root);
|
||||
requiresPublic.stream()
|
||||
.forEach(m -> {
|
||||
bd.addNode(m);
|
||||
bd.addEdge(root, m);
|
||||
});
|
||||
.forEach(m -> rpBuilder.addEdge(root, m));
|
||||
|
||||
// requires public graph
|
||||
Graph<Module> rbg = bd.build().reduce();
|
||||
Graph<Module> rbg = rpBuilder.build().reduce();
|
||||
|
||||
ModuleGraphBuilder gb = new ModuleGraphBuilder(configuration);
|
||||
gb.addModule(root);
|
||||
requires.stream()
|
||||
.forEach(m -> gb.addEdge(root, m));
|
||||
|
||||
// transitive reduction
|
||||
Graph<Module> newGraph = buildGraph(requires).reduce(rbg);
|
||||
Graph<Module> newGraph = gb.buildGraph().reduce(rbg);
|
||||
if (DEBUG) {
|
||||
System.err.println("after transitive reduction: ");
|
||||
newGraph.printGraph(log);
|
||||
}
|
||||
|
||||
return descriptor(requiresPublic, newGraph.adjacentNodes(root));
|
||||
return newGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the transitive reduction on the module graph
|
||||
* and returns the corresponding ModuleDescriptor
|
||||
*/
|
||||
ModuleDescriptor reduced() {
|
||||
Graph<Module> g = buildReducedGraph();
|
||||
return descriptor(requiresPublic, g.adjacentNodes(root));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@ -276,45 +267,6 @@ public class ModuleAnalyzer {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
|
||||
// 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.
|
||||
*/
|
||||
|
||||
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
|
||||
|
||||
/**
|
||||
* Analyze module dependences and any reference to JDK internal APIs.
|
||||
* It can apply transition reduction on the resulting module graph.
|
||||
*
|
||||
* The result prints one line per module it depends on
|
||||
* one line per JDK internal API package it references:
|
||||
* $MODULE[/$PACKAGE]
|
||||
*
|
||||
*/
|
||||
public class ModuleExportsAnalyzer extends DepsAnalyzer {
|
||||
// source archive to its dependences and JDK internal APIs it references
|
||||
private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>();
|
||||
private final boolean reduced;
|
||||
private final PrintWriter writer;
|
||||
public ModuleExportsAnalyzer(JdepsConfiguration config,
|
||||
JdepsFilter filter,
|
||||
boolean reduced,
|
||||
PrintWriter writer) {
|
||||
super(config, filter, null,
|
||||
Analyzer.Type.PACKAGE,
|
||||
false /* all classes */);
|
||||
this.reduced = reduced;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run() throws IOException {
|
||||
// analyze dependences
|
||||
boolean rc = super.run();
|
||||
|
||||
// A visitor to record the module-level dependences as well as
|
||||
// use of JDK internal APIs
|
||||
Analyzer.Visitor visitor = new Analyzer.Visitor() {
|
||||
@Override
|
||||
public void visitDependence(String origin, Archive originArchive,
|
||||
String target, Archive targetArchive)
|
||||
{
|
||||
Set<String> jdkInternals =
|
||||
deps.computeIfAbsent(originArchive, _k -> new HashMap<>())
|
||||
.computeIfAbsent(targetArchive, _k -> new HashSet<>());
|
||||
|
||||
Module module = targetArchive.getModule();
|
||||
if (originArchive.getModule() != module &&
|
||||
module.isJDK() && !module.isExported(target)) {
|
||||
// use of JDK internal APIs
|
||||
jdkInternals.add(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// visit the dependences
|
||||
archives.stream()
|
||||
.filter(analyzer::hasDependences)
|
||||
.sorted(Comparator.comparing(Archive::getName))
|
||||
.forEach(archive -> analyzer.visitDependences(archive, visitor));
|
||||
|
||||
|
||||
// print the dependences on named modules
|
||||
printDependences();
|
||||
|
||||
// print the dependences on unnamed module
|
||||
deps.values().stream()
|
||||
.flatMap(map -> map.keySet().stream())
|
||||
.filter(archive -> !archive.getModule().isNamed())
|
||||
.map(archive -> archive != NOT_FOUND
|
||||
? "unnamed module: " + archive.getPathName()
|
||||
: archive.getPathName())
|
||||
.distinct()
|
||||
.sorted()
|
||||
.forEach(archive -> writer.format(" %s%n", archive));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
private void printDependences() {
|
||||
// find use of JDK internals
|
||||
Map<Module, Set<String>> jdkinternals = new HashMap<>();
|
||||
deps.keySet().stream()
|
||||
.filter(source -> !source.getModule().isNamed())
|
||||
.map(deps::get)
|
||||
.flatMap(map -> map.entrySet().stream())
|
||||
.filter(e -> e.getValue().size() > 0)
|
||||
.forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(),
|
||||
_k -> new HashSet<>())
|
||||
.addAll(e.getValue()));
|
||||
|
||||
|
||||
// build module graph
|
||||
ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration);
|
||||
Module root = new RootModule("root");
|
||||
builder.addModule(root);
|
||||
// find named module dependences
|
||||
deps.keySet().stream()
|
||||
.filter(source -> !source.getModule().isNamed())
|
||||
.map(deps::get)
|
||||
.flatMap(map -> map.keySet().stream())
|
||||
.filter(m -> m.getModule().isNamed())
|
||||
.map(Archive::getModule)
|
||||
.forEach(m -> builder.addEdge(root, m));
|
||||
|
||||
// module dependences
|
||||
Set<Module> modules = builder.build().adjacentNodes(root);
|
||||
|
||||
// if reduced is set, apply transition reduction
|
||||
Set<Module> reducedSet = reduced ? builder.reduced().adjacentNodes(root)
|
||||
: modules;
|
||||
|
||||
modules.stream()
|
||||
.sorted(Comparator.comparing(Module::name))
|
||||
.forEach(m -> {
|
||||
if (jdkinternals.containsKey(m)) {
|
||||
jdkinternals.get(m).stream()
|
||||
.sorted()
|
||||
.forEach(pn -> writer.format(" %s/%s%n", m, pn));
|
||||
} else if (reducedSet.contains(m)){
|
||||
// if the transition reduction is applied, show the reduced graph
|
||||
writer.format(" %s%n", m);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class RootModule extends Module {
|
||||
final ModuleDescriptor descriptor;
|
||||
RootModule(String name) {
|
||||
super(name);
|
||||
|
||||
ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(name);
|
||||
this.descriptor = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleDescriptor descriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
|
||||
import static com.sun.tools.jdeps.Module.*;
|
||||
|
||||
/**
|
||||
* A builder to create a Graph<Module>
|
||||
*/
|
||||
public class ModuleGraphBuilder extends Graph.Builder<Module> {
|
||||
final JdepsConfiguration config;
|
||||
|
||||
ModuleGraphBuilder(JdepsConfiguration config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a module to the graph.
|
||||
*/
|
||||
ModuleGraphBuilder addModule(Module module) {
|
||||
addNode(module);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply transitive reduction on the resulting graph
|
||||
*/
|
||||
public Graph<Module> reduced() {
|
||||
Graph<Module> graph = build();
|
||||
// transitive reduction
|
||||
Graph<Module> newGraph = buildGraph(graph.edges()).reduce();
|
||||
|
||||
if (DEBUG) {
|
||||
PrintWriter log = new PrintWriter(System.err);
|
||||
System.err.println("before transitive reduction: ");
|
||||
graph.printGraph(log);
|
||||
System.err.println("after transitive reduction: ");
|
||||
newGraph.printGraph(log);
|
||||
}
|
||||
|
||||
return newGraph;
|
||||
}
|
||||
|
||||
public Graph<Module> buildGraph() {
|
||||
Graph<Module> graph = build();
|
||||
return buildGraph(graph.edges());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a graph of module from the given dependences.
|
||||
*
|
||||
* It transitively includes all implied read edges.
|
||||
*/
|
||||
private Graph<Module> buildGraph(Map<Module, Set<Module>> edges) {
|
||||
Graph.Builder<Module> builder = new Graph.Builder<>();
|
||||
Set<Module> visited = new HashSet<>();
|
||||
Deque<Module> deque = new LinkedList<>();
|
||||
edges.entrySet().stream().forEach(e -> {
|
||||
Module m = e.getKey();
|
||||
deque.add(m);
|
||||
e.getValue().stream().forEach(v -> {
|
||||
deque.add(v);
|
||||
builder.addEdge(m, v);
|
||||
});
|
||||
});
|
||||
|
||||
// read requires public from ModuleDescriptor
|
||||
Module source;
|
||||
while ((source = deque.poll()) != null) {
|
||||
if (visited.contains(source))
|
||||
continue;
|
||||
|
||||
visited.add(source);
|
||||
builder.addNode(source);
|
||||
Module from = source;
|
||||
requiresPublic(from).forEach(m -> {
|
||||
deque.add(m);
|
||||
builder.addEdge(from, m);
|
||||
});
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a stream of modules upon which the given module `requires public`
|
||||
*/
|
||||
public Stream<Module> requiresPublic(Module m) {
|
||||
// find requires public
|
||||
return m.descriptor()
|
||||
.requires().stream()
|
||||
.filter(req -> req.modifiers().contains(PUBLIC))
|
||||
.map(ModuleDescriptor.Requires::name)
|
||||
.map(config::findModule)
|
||||
.flatMap(Optional::stream);
|
||||
}
|
||||
}
|
||||
@ -149,6 +149,15 @@ main.opt.jdkinternals=\
|
||||
\ used with -p, -e and -s options.\n\
|
||||
\ WARNING: JDK internal APIs are inaccessible.
|
||||
|
||||
main.opt.list-deps=\
|
||||
\ --list-deps Lists the dependences and use of JDK internal\n\
|
||||
\ APIs.\n\
|
||||
\ --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\
|
||||
\ M2 requires public on M3, then M1 reading M3 is\n\
|
||||
\ implied and removed from the module graph.
|
||||
|
||||
main.opt.depth=\
|
||||
\ -depth=<depth> Specify the depth of the transitive\n\
|
||||
\ dependency analysis
|
||||
|
||||
@ -27,16 +27,19 @@ import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.spi.ToolProvider;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JdepsRunner class to invoke jdeps with the given command line argument
|
||||
*/
|
||||
public class JdepsRunner {
|
||||
private static final ToolProvider JDEPS_TOOL = ToolProvider.findFirst("jdeps")
|
||||
.orElseThrow(() -> new RuntimeException("jdeps tool not found"));
|
||||
|
||||
public static JdepsRunner run(String... args) {
|
||||
JdepsRunner jdeps = new JdepsRunner(args);
|
||||
int rc = jdeps.run();
|
||||
jdeps.printStdout(System.err);
|
||||
int rc = jdeps.run(true);
|
||||
if (rc != 0)
|
||||
throw new Error("jdeps failed: rc=" + rc);
|
||||
return jdeps;
|
||||
@ -46,7 +49,7 @@ public class JdepsRunner {
|
||||
final StringWriter stderr = new StringWriter();
|
||||
final String[] args;
|
||||
public JdepsRunner(String... args) {
|
||||
System.err.println("jdeps " + Arrays.stream(args)
|
||||
System.out.println("jdeps " + Arrays.stream(args)
|
||||
.collect(Collectors.joining(" ")));
|
||||
this.args = args;
|
||||
}
|
||||
@ -60,10 +63,12 @@ public class JdepsRunner {
|
||||
}
|
||||
|
||||
public int run(boolean showOutput) {
|
||||
try (PrintWriter pw = new PrintWriter(stdout)) {
|
||||
int rc = com.sun.tools.jdeps.Main.run(args, pw);
|
||||
try (PrintWriter pwout = new PrintWriter(stdout);
|
||||
PrintWriter pwerr = new PrintWriter(stderr)) {
|
||||
int rc = JDEPS_TOOL.run(pwout, pwerr, args);
|
||||
if (showOutput) {
|
||||
System.err.println(stdout.toString());
|
||||
printStdout(System.out);
|
||||
printStderr(System.out);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
180
langtools/test/tools/jdeps/listdeps/ListModuleDeps.java
Normal file
180
langtools/test/tools/jdeps/listdeps/ListModuleDeps.java
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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 8167057
|
||||
* @summary Tests split packages
|
||||
* @modules java.logging
|
||||
* java.xml
|
||||
* jdk.compiler
|
||||
* jdk.jdeps
|
||||
* jdk.unsupported
|
||||
* @library ../lib
|
||||
* @build CompilerUtils JdepsRunner
|
||||
* @run testng ListModuleDeps
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class ListModuleDeps {
|
||||
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 CLASSES_DIR = Paths.get("classes");
|
||||
private static final Path LIB_DIR = Paths.get("lib");
|
||||
|
||||
private static final Path FOO_CLASS =
|
||||
CLASSES_DIR.resolve("z").resolve("Foo.class");
|
||||
private static final Path BAR_CLASS =
|
||||
CLASSES_DIR.resolve("z").resolve("Bar.class");
|
||||
private static final Path UNSAFE_CLASS =
|
||||
CLASSES_DIR.resolve("z").resolve("UseUnsafe.class");
|
||||
|
||||
/**
|
||||
* Compiles classes used by the test
|
||||
*/
|
||||
@BeforeTest
|
||||
public void compileAll() throws Exception {
|
||||
// compile library
|
||||
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "lib"), LIB_DIR));
|
||||
|
||||
// compile classes in unnamed module
|
||||
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "z"),
|
||||
CLASSES_DIR,
|
||||
"-cp", LIB_DIR.toString(),
|
||||
"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
|
||||
"--add-exports=java.base/sun.security.util=ALL-UNNAMED",
|
||||
"--add-exports=java.xml/jdk.xml.internal=ALL-UNNAMED"
|
||||
));
|
||||
}
|
||||
|
||||
@Test(dataProvider = "listdeps")
|
||||
public void testListDeps(Path classes, String[] expected) {
|
||||
JdepsRunner jdeps = JdepsRunner.run(
|
||||
"--class-path", LIB_DIR.toString(),
|
||||
"--list-deps", classes.toString()
|
||||
);
|
||||
String[] output = Arrays.stream(jdeps.output())
|
||||
.map(s -> s.trim())
|
||||
.toArray(String[]::new);
|
||||
assertEquals(output, expected);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "reduceddeps")
|
||||
public void testListReducedDeps(Path classes, String[] expected) {
|
||||
JdepsRunner jdeps = JdepsRunner.run(
|
||||
"--class-path", LIB_DIR.toString(),
|
||||
"--list-reduced-deps", classes.toString()
|
||||
);
|
||||
String[] output = Arrays.stream(jdeps.output())
|
||||
.map(s -> s.trim())
|
||||
.toArray(String[]::new);
|
||||
assertEquals(output, expected);
|
||||
}
|
||||
|
||||
|
||||
@DataProvider(name = "listdeps")
|
||||
public Object[][] listdeps() {
|
||||
return new Object[][] {
|
||||
{ CLASSES_DIR, new String[] {
|
||||
"java.base/jdk.internal.misc",
|
||||
"java.base/sun.security.util",
|
||||
"java.logging",
|
||||
"java.sql",
|
||||
"java.xml/jdk.xml.internal",
|
||||
"jdk.unsupported",
|
||||
"unnamed module: lib"
|
||||
}
|
||||
},
|
||||
|
||||
{ FOO_CLASS, new String[] {
|
||||
"java.base",
|
||||
"java.logging",
|
||||
"java.sql",
|
||||
"java.xml",
|
||||
"unnamed module: lib"
|
||||
}
|
||||
},
|
||||
|
||||
{ BAR_CLASS, new String[] {
|
||||
"java.base/sun.security.util",
|
||||
"java.xml/jdk.xml.internal",
|
||||
}
|
||||
},
|
||||
|
||||
{ UNSAFE_CLASS, new String[] {
|
||||
"java.base/jdk.internal.misc",
|
||||
"jdk.unsupported",
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "reduceddeps")
|
||||
public Object[][] reduceddeps() {
|
||||
Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
|
||||
|
||||
return new Object[][] {
|
||||
{ CLASSES_DIR, new String[] {
|
||||
"java.base/jdk.internal.misc",
|
||||
"java.base/sun.security.util",
|
||||
"java.sql",
|
||||
"java.xml/jdk.xml.internal",
|
||||
"jdk.unsupported",
|
||||
"unnamed module: lib"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{ FOO_CLASS, new String[] {
|
||||
"java.base",
|
||||
"java.sql",
|
||||
"unnamed module: lib"
|
||||
}
|
||||
},
|
||||
|
||||
{ BAR_CLASS, new String[] {
|
||||
"java.base/sun.security.util",
|
||||
"java.xml/jdk.xml.internal",
|
||||
}
|
||||
},
|
||||
|
||||
{ UNSAFE_CLASS, new String[] {
|
||||
"java.base/jdk.internal.misc",
|
||||
"jdk.unsupported",
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
31
langtools/test/tools/jdeps/listdeps/src/lib/Lib.java
Normal file
31
langtools/test/tools/jdeps/listdeps/src/lib/Lib.java
Normal file
@ -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 lib;
|
||||
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
|
||||
public class Lib {
|
||||
public static final String isCoalescing = XMLInputFactory.IS_COALESCING;
|
||||
public static boolean check() { return true; }
|
||||
}
|
||||
36
langtools/test/tools/jdeps/listdeps/src/z/Bar.java
Normal file
36
langtools/test/tools/jdeps/listdeps/src/z/Bar.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 z;
|
||||
|
||||
import sun.security.util.HostnameChecker;
|
||||
import jdk.xml.internal.JdkXmlUtils;
|
||||
|
||||
public class Bar {
|
||||
// internal API from java.xml
|
||||
private static final String name = JdkXmlUtils.USE_CATALOG;
|
||||
|
||||
public static void main(String[] argv) throws Exception {
|
||||
HostnameChecker hc = HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP);
|
||||
}
|
||||
}
|
||||
47
langtools/test/tools/jdeps/listdeps/src/z/Foo.java
Normal file
47
langtools/test/tools/jdeps/listdeps/src/z/Foo.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 z;
|
||||
|
||||
import java.sql.Driver;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
/*
|
||||
* Dependences on java.sql and java.logging which can be reduced.
|
||||
*/
|
||||
public class Foo {
|
||||
// dependence to java.logging
|
||||
static Logger log = Logger.getLogger("foo");
|
||||
static final String isCoalescing = XMLInputFactory.IS_COALESCING;
|
||||
|
||||
// dependence to java.sql
|
||||
public Driver getDriver() { return null; }
|
||||
|
||||
// dependence to same package
|
||||
public Bar getBar() { return new Bar(); }
|
||||
|
||||
// dependence to module m
|
||||
public String isCoalescing() { return lib.Lib.isCoalescing; }
|
||||
|
||||
|
||||
}
|
||||
33
langtools/test/tools/jdeps/listdeps/src/z/UseUnsafe.java
Normal file
33
langtools/test/tools/jdeps/listdeps/src/z/UseUnsafe.java
Normal file
@ -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 z;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
public class UseUnsafe {
|
||||
private static Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private static boolean booted = VM.isBooted();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user