8193192: jdeps --generate-module-info does not look at module path

Reviewed-by: dfuchs
This commit is contained in:
Mandy Chung 2017-12-12 11:31:38 -08:00
parent 4f080a83af
commit 0b0340fe0f
9 changed files with 202 additions and 116 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2017, 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
@ -25,18 +25,14 @@
package com.sun.tools.jdeps;
import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -161,7 +157,7 @@ public final class Graph<T> {
* Returns all nodes reachable from the given set of roots.
*/
public Set<T> dfs(Set<T> roots) {
Deque<T> deque = new LinkedList<>(roots);
Deque<T> deque = new ArrayDeque<>(roots);
Set<T> visited = new HashSet<>();
while (!deque.isEmpty()) {
T u = deque.pop();
@ -197,7 +193,7 @@ public final class Graph<T> {
if (includeAdjacent && isAdjacent(u, v)) {
return true;
}
Deque<T> stack = new LinkedList<>();
Deque<T> stack = new ArrayDeque<>();
Set<T> visited = new HashSet<>();
stack.push(u);
while (!stack.isEmpty()) {
@ -292,12 +288,10 @@ public final class Graph<T> {
* Topological sort
*/
static class TopoSorter<T> {
final Deque<T> result = new LinkedList<>();
final Deque<T> nodes;
final Deque<T> result = new ArrayDeque<>();
final Graph<T> graph;
TopoSorter(Graph<T> graph) {
this.graph = graph;
this.nodes = new LinkedList<>(graph.nodes);
sort();
}
@ -310,17 +304,16 @@ public final class Graph<T> {
}
private void sort() {
Deque<T> visited = new LinkedList<>();
Deque<T> done = new LinkedList<>();
T node;
while ((node = nodes.poll()) != null) {
Set<T> visited = new HashSet<>();
Set<T> done = new HashSet<>();
for (T node : graph.nodes()) {
if (!visited.contains(node)) {
visit(node, visited, done);
}
}
}
private void visit(T node, Deque<T> visited, Deque<T> done) {
private void visit(T node, Set<T> visited, Set<T> done) {
if (visited.contains(node)) {
if (!done.contains(node)) {
throw new IllegalArgumentException("Cyclic detected: " +
@ -330,7 +323,7 @@ public final class Graph<T> {
}
visited.add(node);
graph.edges().get(node).stream()
.forEach(x -> visit(x, visited, done));
.forEach(x -> visit(x, visited, done));
done.add(node);
result.addLast(node);
}

View File

@ -38,8 +38,6 @@ import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
@ -71,6 +69,7 @@ public class JdepsConfiguration implements AutoCloseable {
public static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
public static final String ALL_DEFAULT = "ALL-DEFAULT";
public static final String ALL_SYSTEM = "ALL-SYSTEM";
public static final String MODULE_INFO = "module-info.class";
private final SystemModuleFinder system;
@ -91,8 +90,7 @@ public class JdepsConfiguration implements AutoCloseable {
Set<String> roots,
List<Path> classpaths,
List<Archive> initialArchives,
boolean allDefaultModules,
boolean allSystemModules,
Set<String> tokens,
Runtime.Version version)
throws IOException
{
@ -104,16 +102,13 @@ public class JdepsConfiguration implements AutoCloseable {
// build root set for resolution
Set<String> mods = new HashSet<>(roots);
// add all system modules to the root set for unnamed module or set explicitly
boolean unnamed = !initialArchives.isEmpty() || !classpaths.isEmpty();
if (allSystemModules || (unnamed && !allDefaultModules)) {
if (tokens.contains(ALL_SYSTEM)) {
systemModulePath.findAll().stream()
.map(mref -> mref.descriptor().name())
.forEach(mods::add);
}
if (allDefaultModules) {
if (tokens.contains(ALL_DEFAULT)) {
mods.addAll(systemModulePath.defaultSystemRoots());
}
@ -200,10 +195,10 @@ public class JdepsConfiguration implements AutoCloseable {
return m!= null ? Optional.of(m.descriptor()) : Optional.empty();
}
boolean isValidToken(String name) {
public static boolean isToken(String name) {
return ALL_MODULE_PATH.equals(name) ||
ALL_DEFAULT.equals(name) ||
ALL_SYSTEM.equals(name);
ALL_DEFAULT.equals(name) ||
ALL_SYSTEM.equals(name);
}
/**
@ -482,13 +477,10 @@ public class JdepsConfiguration implements AutoCloseable {
final List<Archive> initialArchives = new ArrayList<>();
final List<Path> paths = new ArrayList<>();
final List<Path> classPaths = new ArrayList<>();
final Set<String> tokens = new HashSet<>();
ModuleFinder upgradeModulePath;
ModuleFinder appModulePath;
boolean addAllApplicationModules;
boolean addAllDefaultModules;
boolean addAllSystemModules;
boolean allModules;
Runtime.Version version;
public Builder() {
@ -513,34 +505,15 @@ public class JdepsConfiguration implements AutoCloseable {
public Builder addmods(Set<String> addmods) {
for (String mn : addmods) {
switch (mn) {
case ALL_MODULE_PATH:
this.addAllApplicationModules = true;
break;
case ALL_DEFAULT:
this.addAllDefaultModules = true;
break;
case ALL_SYSTEM:
this.addAllSystemModules = true;
break;
default:
this.rootModules.add(mn);
if (isToken(mn)) {
tokens.add(mn);
} else {
rootModules.add(mn);
}
}
return this;
}
/*
* This method is for --check option to find all target modules specified
* in qualified exports.
*
* Include all system modules and modules found on modulepath
*/
public Builder allModules() {
this.allModules = true;
return this;
}
public Builder multiRelease(Runtime.Version version) {
this.version = version;
return this;
@ -579,7 +552,9 @@ public class JdepsConfiguration implements AutoCloseable {
.forEach(rootModules::add);
}
if ((addAllApplicationModules || allModules) && appModulePath != null) {
// add all modules to the root set for unnamed module or set explicitly
boolean unnamed = !initialArchives.isEmpty() || !classPaths.isEmpty();
if ((unnamed || tokens.contains(ALL_MODULE_PATH)) && appModulePath != null) {
appModulePath.findAll().stream()
.map(mref -> mref.descriptor().name())
.forEach(rootModules::add);
@ -587,7 +562,7 @@ public class JdepsConfiguration implements AutoCloseable {
// no archive is specified for analysis
// add all system modules as root if --add-modules ALL-SYSTEM is specified
if (addAllSystemModules && rootModules.isEmpty() &&
if (tokens.contains(ALL_SYSTEM) && rootModules.isEmpty() &&
initialArchives.isEmpty() && classPaths.isEmpty()) {
systemModulePath.findAll()
.stream()
@ -595,13 +570,16 @@ public class JdepsConfiguration implements AutoCloseable {
.forEach(rootModules::add);
}
if (unnamed && !tokens.contains(ALL_DEFAULT)) {
tokens.add(ALL_SYSTEM);
}
return new JdepsConfiguration(systemModulePath,
finder,
rootModules,
classPaths,
initialArchives,
addAllDefaultModules,
allModules,
tokens,
version);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2017, 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
@ -540,7 +540,7 @@ class JdepsTask {
}
boolean run() throws IOException {
try (JdepsConfiguration config = buildConfig(command.allModules())) {
try (JdepsConfiguration config = buildConfig()) {
if (!options.nowarning) {
// detect split packages
config.splitPackages().entrySet()
@ -553,7 +553,7 @@ class JdepsTask {
// check if any module specified in --add-modules, --require, and -m is missing
options.addmods.stream()
.filter(mn -> !config.isValidToken(mn))
.filter(mn -> !JdepsConfiguration.isToken(mn))
.forEach(mn -> config.findModule(mn).orElseThrow(() ->
new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
@ -561,18 +561,14 @@ class JdepsTask {
}
}
private JdepsConfiguration buildConfig(boolean allModules) throws IOException {
private JdepsConfiguration buildConfig() throws IOException {
JdepsConfiguration.Builder builder =
new JdepsConfiguration.Builder(options.systemModulePath);
builder.upgradeModulePath(options.upgradeModulePath)
.appModulePath(options.modulePath)
.addmods(options.addmods);
if (allModules) {
// check all system modules in the image
builder.allModules();
}
.addmods(options.addmods)
.addmods(command.addModules());
if (options.classpath != null)
builder.addClassPath(options.classpath);
@ -655,8 +651,8 @@ class JdepsTask {
* only. The method should be overridden when this command should
* analyze all modules instead.
*/
boolean allModules() {
return false;
Set<String> addModules() {
return Set.of();
}
@Override
@ -871,8 +867,8 @@ class JdepsTask {
* analyzed to find all modules that depend on the modules specified in the
* --require option directly and indirectly
*/
public boolean allModules() {
return options.requires.size() > 0;
Set<String> addModules() {
return options.requires.size() > 0 ? Set.of("ALL-SYSTEM") : Set.of();
}
}
@ -975,8 +971,8 @@ class JdepsTask {
/*
* Returns true to analyze all modules
*/
public boolean allModules() {
return true;
Set<String> addModules() {
return Set.of("ALL-SYSTEM", "ALL-MODULE-PATH");
}
}

View File

@ -32,7 +32,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
@ -138,7 +137,7 @@ enum Profile {
// for debugging
public static void main(String[] args) throws IOException {
// initialize Profiles
new JdepsConfiguration.Builder().allModules().build();
new JdepsConfiguration.Builder().addmods(Set.of("ALL-SYSTEM")).build();
// find platform modules
if (Profile.getProfileCount() == 0) {

View File

@ -175,7 +175,7 @@ public final class JdepsUtil {
public ModuleAnalyzer getModuleAnalyzer(Set<String> mods) throws IOException {
// if --check is set, add to the root set and all modules are observable
addmods(mods);
builder.allModules();
builder.addmods(Set.of("ALL-SYSTEM", "ALL-MODULE-PATH"));
return new ModuleAnalyzer(configuration(), pw, mods);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2017, 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
@ -23,11 +23,12 @@
/*
* @test
* @summary Tests jdeps --generate-module-info option
* @bug 8193192
* @library ../lib
* @build CompilerUtils JdepsUtil JdepsRunner
* @modules jdk.jdeps/com.sun.tools.jdeps
* @run testng GenModuleInfo
* @summary Tests jdeps --generate-module-info option
*/
import java.io.File;
@ -58,15 +59,21 @@ public class GenModuleInfo {
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
private static final Path MODS_DIR = Paths.get("mods");
private static final Path LIBS_DIR = Paths.get("libs");
private static final Path MLIBS_DIR = Paths.get("mlibs");
private static final Path DEST_DIR = Paths.get("moduleinfosrc");
private static final Path NEW_MODS_DIR = Paths.get("new_mods");
// the names of the modules in this test
public static final String UNSUPPORTED = "unsupported";
public static final Set<String> MODULES = Set.of(
"mI", "mII", "mIII", "provider", UNSUPPORTED
"mI", "mII", "mIII", "provider", "test", UNSUPPORTED
);
@BeforeTest
public void setup() throws Exception {
compileAndCreateJars();
}
/**
* Compile modules
*/
@ -78,6 +85,21 @@ public class GenModuleInfo {
.forEach(mn -> assertTrue(CompilerUtils.compileModule(SRC_DIR, dest, mn)));
}
/**
* Create JAR files with no module-info.class
*/
public static void createModularJARs(Path mods, Path dest, String... modules) throws IOException {
Files.createDirectory(dest);
// create modular JAR
for (String mn : modules) {
Path root = mods.resolve(mn);
try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE,
(p, attr) -> { return attr.isRegularFile(); })) {
JdepsUtil.createJar(dest.resolve(mn + ".jar"), root, stream);
}
}
}
/**
* Create JAR files with no module-info.class
*/
@ -141,18 +163,16 @@ public class GenModuleInfo {
}
/**
* Compiles all modules used by the test
*/
@BeforeTest
public void compileAll() throws Exception {
public static void compileAndCreateJars() throws Exception {
CompilerUtils.cleanDir(MODS_DIR);
CompilerUtils.cleanDir(LIBS_DIR);
CompilerUtils.cleanDir(DEST_DIR);
CompilerUtils.cleanDir(NEW_MODS_DIR);
compileModules(MODS_DIR);
// create modular JARs except test
createModularJARs(MODS_DIR, MLIBS_DIR, MODULES.stream().filter(mn -> !mn.equals("test"))
.toArray(String[]::new));
// create non-modular JARs
createJARFiles(MODS_DIR, LIBS_DIR);
}
@ -166,35 +186,67 @@ public class GenModuleInfo {
@Test
public void test() throws IOException {
Files.createDirectory(DEST_DIR);
Path dest = DEST_DIR.resolve("case1");
Path classes = NEW_MODS_DIR.resolve("case1");
Files.createDirectories(dest);
Files.createDirectories(classes);
Stream<String> files = MODULES.stream()
.map(mn -> LIBS_DIR.resolve(mn + ".jar"))
.map(Path::toString);
Stream<String> options = Stream.concat(
Stream.of("--generate-module-info", DEST_DIR.toString()), files);
Stream.of("--generate-module-info", dest.toString()), files);
JdepsRunner.run(options.toArray(String[]::new));
// check file exists
MODULES.stream()
.map(mn -> DEST_DIR.resolve(mn).resolve("module-info.java"))
.map(mn -> dest.resolve(mn).resolve("module-info.java"))
.forEach(f -> assertTrue(Files.exists(f)));
// copy classes to a temporary directory
// and then compile new module-info.java
copyClasses(MODS_DIR, NEW_MODS_DIR);
compileNewGenModuleInfo(DEST_DIR, NEW_MODS_DIR);
copyClasses(MODS_DIR, classes);
compileNewGenModuleInfo(dest, classes);
for (String mn : MODULES) {
Path p1 = NEW_MODS_DIR.resolve(mn).resolve(MODULE_INFO);
Path p2 = MODS_DIR.resolve(mn).resolve(MODULE_INFO);
verify(mn, classes, MODS_DIR);
}
}
try (InputStream in1 = Files.newInputStream(p1);
InputStream in2 = Files.newInputStream(p2)) {
verify(ModuleDescriptor.read(in1),
ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
}
@Test
public void withModulePath() throws IOException {
Path dest = DEST_DIR.resolve("case2");
Path classes = NEW_MODS_DIR.resolve("case2");
Files.createDirectories(dest);
Files.createDirectories(classes);
JdepsRunner.run("--module-path", MLIBS_DIR.toString(),
"--generate-module-info", dest.toString(),
LIBS_DIR.resolve("test.jar").toString());
String name = "test";
Path gensrc = dest.resolve(name).resolve("module-info.java");
assertTrue(Files.exists(gensrc));
// copy classes to a temporary directory
// and then compile new module-info.java
copyClasses(MODS_DIR.resolve(name), classes.resolve(name));
assertTrue(CompilerUtils.compileModule(dest, classes, name, "-p", MLIBS_DIR.toString()));
verify(name, classes, MODS_DIR);
}
/**
* Verify the dependences from the given module-info.class files
*/
public void verify(String mn, Path mdir1, Path mdir2) throws IOException {
Path p1 = mdir1.resolve(mn).resolve(MODULE_INFO);
Path p2 = mdir2.resolve(mn).resolve(MODULE_INFO);
try (InputStream in1 = Files.newInputStream(p1);
InputStream in2 = Files.newInputStream(p2)) {
verify(ModuleDescriptor.read(in1),
ModuleDescriptor.read(in2, () -> packages(mdir2.resolve(mn))));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2017, 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
@ -54,41 +54,39 @@ public class GenOpenModule extends GenModuleInfo {
private static final Path DEST_DIR = Paths.get("moduleinfosrc");
private static final Path NEW_MODS_DIR = Paths.get("new_mods");
/**
* Compiles all modules used by the test
*/
@BeforeTest
public void compileAll() throws Exception {
compileModules(MODS_DIR);
createJARFiles(MODS_DIR, LIBS_DIR);
public void setup() throws Exception {
compileAndCreateJars();
}
@Test
public void test() throws IOException {
Path dest = DEST_DIR.resolve("open");
Path classes = NEW_MODS_DIR.resolve("open");
Files.createDirectories(dest);
Files.createDirectories(classes);
Stream<String> files = MODULES.stream()
.map(mn -> LIBS_DIR.resolve(mn + ".jar"))
.map(Path::toString);
Stream<String> options = Stream.concat(
Stream.of("--generate-open-module", DEST_DIR.toString()), files);
Stream.of("--generate-open-module", dest.toString()), files);
JdepsRunner.run(options.toArray(String[]::new));
// check file exists
MODULES.stream()
.map(mn -> DEST_DIR.resolve(mn).resolve("module-info.java"))
.map(mn -> dest.resolve(mn).resolve("module-info.java"))
.forEach(f -> assertTrue(Files.exists(f)));
// copy classes to a temporary directory
// and then compile new module-info.java
copyClasses(MODS_DIR, NEW_MODS_DIR);
compileNewGenModuleInfo(DEST_DIR, NEW_MODS_DIR);
copyClasses(MODS_DIR, classes);
compileNewGenModuleInfo(dest, classes);
for (String mn : MODULES) {
Path p1 = NEW_MODS_DIR.resolve(mn).resolve(MODULE_INFO);
Path p1 = classes.resolve(mn).resolve(MODULE_INFO);
Path p2 = MODS_DIR.resolve(mn).resolve(MODULE_INFO);
try (InputStream in1 = Files.newInputStream(p1);
InputStream in2 = Files.newInputStream(p2)) {
verify(ModuleDescriptor.read(in1),

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2017, 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 jdk.test;
import java.sql.Driver;
import p1.Goo;
import p2.Bar;
import p3.Foo;
public class Main {
public static void main(String... args) {
Goo goo = toGoo(new Bar());
Driver driver = new Foo().getDriver();
}
public static Goo toGoo(Bar bar) {
return bar.toGoo();
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2017, 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.
*/
module test {
requires java.sql;
requires transitive mI;
requires transitive mII;
requires mIII;
}