8193192: jdeps --generate-module-info does not look at module path
authormchung
Tue, 12 Dec 2017 11:31:38 -0800
changeset 48253 82767203606e
parent 48252 77b88d8f8380
child 48254 111104f1e033
8193192: jdeps --generate-module-info does not look at module path Reviewed-by: dfuchs
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java
test/langtools/tools/jdeps/lib/JdepsUtil.java
test/langtools/tools/jdeps/modules/GenModuleInfo.java
test/langtools/tools/jdeps/modules/GenOpenModule.java
test/langtools/tools/jdeps/modules/src/test/jdk/test/Main.java
test/langtools/tools/jdeps/modules/src/test/module-info.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java	Tue Dec 12 11:31:38 2017 -0800
@@ -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 @@
      * 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 @@
         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 @@
      * 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 @@
         }
 
         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 @@
             }
             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);
         }
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java	Tue Dec 12 11:31:38 2017 -0800
@@ -38,8 +38,6 @@
 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 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 @@
                                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 @@
 
         // 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 @@
         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 @@
         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 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 @@
                         .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 @@
 
             // 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 @@
                     .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);
         }
 
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Tue Dec 12 11:31:38 2017 -0800
@@ -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 @@
     }
 
     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 @@
 
             // 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 @@
         }
     }
 
-    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 @@
          * 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 @@
          * 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 @@
         /*
          * Returns true to analyze all modules
          */
-        public boolean allModules() {
-            return true;
+        Set<String> addModules() {
+            return Set.of("ALL-SYSTEM", "ALL-MODULE-PATH");
         }
     }
 
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java	Tue Dec 12 11:31:38 2017 -0800
@@ -32,7 +32,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -138,7 +137,7 @@
     // 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) {
--- a/test/langtools/tools/jdeps/lib/JdepsUtil.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/test/langtools/tools/jdeps/lib/JdepsUtil.java	Tue Dec 12 11:31:38 2017 -0800
@@ -175,7 +175,7 @@
         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);
         }
 
--- a/test/langtools/tools/jdeps/modules/GenModuleInfo.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/test/langtools/tools/jdeps/modules/GenModuleInfo.java	Tue Dec 12 11:31:38 2017 -0800
@@ -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 @@
     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
      */
@@ -81,6 +88,21 @@
     /**
      * 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
+     */
     public static List<Path> createJARFiles(Path mods, Path libs) throws IOException {
         Files.createDirectory(libs);
 
@@ -141,18 +163,16 @@
 
     }
 
-    /**
-     * 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 @@
 
     @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);
+        }
+    }
+
+    @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));
 
-            try (InputStream in1 = Files.newInputStream(p1);
-                 InputStream in2 = Files.newInputStream(p2)) {
-                verify(ModuleDescriptor.read(in1),
-                       ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
-            }
+        // 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))));
         }
     }
 
--- a/test/langtools/tools/jdeps/modules/GenOpenModule.java	Tue Dec 12 11:10:12 2017 -0800
+++ b/test/langtools/tools/jdeps/modules/GenOpenModule.java	Tue Dec 12 11:31:38 2017 -0800
@@ -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 @@
     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),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/jdeps/modules/src/test/jdk/test/Main.java	Tue Dec 12 11:31:38 2017 -0800
@@ -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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/jdeps/modules/src/test/module-info.java	Tue Dec 12 11:31:38 2017 -0800
@@ -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;
+}