< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java

Print this page

        

@@ -44,27 +44,30 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
 import java.nio.file.attribute.PosixFilePermission;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
+import java.util.stream.Stream;
 import static java.util.stream.Collectors.*;
 
 import jdk.tools.jlink.internal.BasicImageWriter;
 import jdk.tools.jlink.internal.plugins.FileCopierPlugin.SymImageFile;
 import jdk.tools.jlink.internal.ExecutableImage;
 import jdk.tools.jlink.plugin.ResourcePool;
 import jdk.tools.jlink.plugin.ResourcePoolEntry;
 import jdk.tools.jlink.plugin.ResourcePoolModule;
+import jdk.tools.jlink.plugin.ResourcePoolModuleView;
 import jdk.tools.jlink.plugin.PluginException;
 
 /**
  *
  * Default Image Builder. This builder creates the default runtime image layout.

@@ -139,35 +142,90 @@
         this.root = root;
         this.mdir = root.resolve("lib");
         Files.createDirectories(mdir);
     }
 
-    private void storeFiles(Set<String> modules, Properties release) throws IOException {
+    private void storeFiles(List<String> modules, Properties release) throws IOException {
         if (release != null) {
             addModules(release, modules);
             File r = new File(root.toFile(), "release");
             try (FileOutputStream fo = new FileOutputStream(r)) {
                 release.store(fo, null);
             }
         }
     }
 
-    private void addModules(Properties props, Set<String> modules) throws IOException {
+    private void addModules(Properties props, List<String> modules) throws IOException {
+        int size = modules.size();
         StringBuilder builder = new StringBuilder();
         int i = 0;
         for (String m : modules) {
             builder.append(m);
-            if (i < modules.size() - 1) {
-                builder.append(",");
+            if (i < size - 1) {
+                builder.append(' ');
             }
             i++;
         }
         props.setProperty("MODULES", quote(builder.toString()));
     }
 
+    // utility class to (topological) sort the module names
+    static class ModuleNamesSorter {
+        // don't create me!
+        private ModuleNamesSorter() {}
+
+        // return a stream of dependent module names of the given module name
+        private static Stream<String> dependencies(String modName, ResourcePoolModuleView modView) {
+            Optional<ResourcePoolModule> mod = modView.findModule(modName);
+            if (mod.isPresent()) {
+                return mod.get().descriptor().requires().stream().map(ModuleDescriptor.Requires::name);
+            } else {
+                throw new RuntimeException("Missing module: " + modName);
+            }
+        }
+
+        // states for module visit
+        private static enum State {
+            VISITING, VISITED;
+        }
+
+        // topological sort helper
+        private static void tsort(String modName, Map<String, State> state,
+                    ResourcePoolModuleView modView, List<String> ret) {
+            state.put(modName, State.VISITING);
+            dependencies(modName, modView).forEach(depModName -> {
+                State st = state.get(depModName);
+                if (st == null) {
+                    tsort(depModName, state, modView, ret);
+                } else if (st == State.VISITING) {
+                    throw new RuntimeException("Cyclic dependency: " + depModName);
+                }
+                assert st == State.VISITED;
+            });
+            state.put(modName, State.VISITED);
+            ret.add(modName);
+        }
+
+        // entry point to sort module names by topological sort
+        static List<String> sort(Set<String> rootMods, ResourcePoolModuleView modView) {
+            Map<String, State> state = new HashMap<>();
+            List<String> ret = new ArrayList<>();
+            for (String rootMod : rootMods) {
+                State st = state.get(rootMod);
+                if (st == null) {
+                    tsort(rootMod, state, modView, ret);
+                } else if (st == State.VISITING) {
+                    throw new RuntimeException("Cyclic dependency: " + rootMod);
+                }
+                assert st == State.VISITED;
+            }
+            return ret;
+        }
+    }
+
     @Override
-    public void storeFiles(ResourcePool files) {
+    public void storeFiles(Set<String> rootModules, ResourcePool files) {
         try {
             // populate release properties up-front. targetOsName
             // field is assigned from there and used elsewhere.
             Properties release = releaseProperties(files);
             Path bin = root.resolve("bin");

@@ -207,11 +265,12 @@
                 if (!m.packages().isEmpty()) {
                     modules.add(m.name());
                 }
             });
 
-            storeFiles(modules, release);
+            storeFiles(ModuleNamesSorter.sort(
+                rootModules.isEmpty()? modules : rootModules, files.moduleView()), release);
 
             if (root.getFileSystem().supportedFileAttributeViews()
                     .contains("posix")) {
                 // launchers in the bin directory need execute permission.
                 // On Windows, "bin" also subdirectories containing jvm.dll.
< prev index next >