< prev index next >

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

Print this page




  29 import java.io.BufferedWriter;
  30 import java.io.ByteArrayInputStream;
  31 import java.io.DataOutputStream;
  32 import java.io.File;
  33 import java.io.FileInputStream;
  34 import java.io.FileOutputStream;
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.io.UncheckedIOException;
  38 import java.io.OutputStream;
  39 import java.io.OutputStreamWriter;
  40 import java.io.Writer;
  41 import java.lang.module.ModuleDescriptor;
  42 import java.nio.charset.StandardCharsets;
  43 import java.nio.file.FileAlreadyExistsException;
  44 import java.nio.file.Files;
  45 import java.nio.file.Path;
  46 import java.nio.file.Paths;
  47 import java.nio.file.StandardOpenOption;
  48 import java.nio.file.attribute.PosixFilePermission;

  49 import java.util.Collections;
  50 import java.util.HashMap;
  51 import java.util.HashSet;
  52 import java.util.List;
  53 import java.util.Map;
  54 import java.util.Objects;
  55 import java.util.Optional;
  56 import java.util.Properties;
  57 import java.util.Set;

  58 import static java.util.stream.Collectors.*;
  59 
  60 import jdk.tools.jlink.internal.BasicImageWriter;
  61 import jdk.tools.jlink.internal.plugins.FileCopierPlugin.SymImageFile;
  62 import jdk.tools.jlink.internal.ExecutableImage;
  63 import jdk.tools.jlink.plugin.ResourcePool;
  64 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  65 import jdk.tools.jlink.plugin.ResourcePoolModule;

  66 import jdk.tools.jlink.plugin.PluginException;
  67 
  68 /**
  69  *
  70  * Default Image Builder. This builder creates the default runtime image layout.
  71  */
  72 public final class DefaultImageBuilder implements ImageBuilder {
  73 
  74     /**
  75      * The default java executable Image.
  76      */
  77     static final class DefaultExecutableImage implements ExecutableImage {
  78 
  79         private final Path home;
  80         private final List<String> args;
  81         private final Set<String> modules;
  82 
  83         DefaultExecutableImage(Path home, Set<String> modules) {
  84             Objects.requireNonNull(home);
  85             if (!Files.exists(home)) {


 124 
 125     private final Path root;
 126     private final Path mdir;
 127     private final Set<String> modules = new HashSet<>();
 128     private String targetOsName;
 129 
 130     /**
 131      * Default image builder constructor.
 132      *
 133      * @param root The image root directory.
 134      * @throws IOException
 135      */
 136     public DefaultImageBuilder(Path root) throws IOException {
 137         Objects.requireNonNull(root);
 138 
 139         this.root = root;
 140         this.mdir = root.resolve("lib");
 141         Files.createDirectories(mdir);
 142     }
 143 
 144     private void storeFiles(Set<String> modules, Properties release) throws IOException {
 145         if (release != null) {
 146             addModules(release, modules);
 147             File r = new File(root.toFile(), "release");
 148             try (FileOutputStream fo = new FileOutputStream(r)) {
 149                 release.store(fo, null);
 150             }
 151         }
 152     }
 153 
 154     private void addModules(Properties props, Set<String> modules) throws IOException {

 155         StringBuilder builder = new StringBuilder();
 156         int i = 0;
 157         for (String m : modules) {
 158             builder.append(m);
 159             if (i < modules.size() - 1) {
 160                 builder.append(",");
 161             }
 162             i++;
 163         }
 164         props.setProperty("MODULES", quote(builder.toString()));
 165     }
 166 






















































 167     @Override
 168     public void storeFiles(ResourcePool files) {
 169         try {
 170             // populate release properties up-front. targetOsName
 171             // field is assigned from there and used elsewhere.
 172             Properties release = releaseProperties(files);
 173             Path bin = root.resolve("bin");
 174 
 175             // check any duplicated resource files
 176             Map<Path, Set<String>> duplicates = new HashMap<>();
 177             files.entries()
 178                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
 179                 .collect(groupingBy(this::entryToImagePath,
 180                          mapping(ResourcePoolEntry::moduleName, toSet())))
 181                 .entrySet()
 182                 .stream()
 183                 .filter(e -> e.getValue().size() > 1)
 184                 .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
 185 
 186             // write non-classes resource files to the image
 187             files.entries()
 188                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)


 192                     } catch (FileAlreadyExistsException e) {
 193                         // error for duplicated entries
 194                         Path path = entryToImagePath(f);
 195                         UncheckedIOException x =
 196                             new UncheckedIOException(path + " duplicated in " +
 197                                     duplicates.get(path), e);
 198                         x.addSuppressed(e);
 199                         throw x;
 200                     } catch (IOException ioExp) {
 201                         throw new UncheckedIOException(ioExp);
 202                     }
 203                 });
 204 
 205             files.moduleView().modules().forEach(m -> {
 206                 // Only add modules that contain packages
 207                 if (!m.packages().isEmpty()) {
 208                     modules.add(m.name());
 209                 }
 210             });
 211 
 212             storeFiles(modules, release);

 213 
 214             if (root.getFileSystem().supportedFileAttributeViews()
 215                     .contains("posix")) {
 216                 // launchers in the bin directory need execute permission.
 217                 // On Windows, "bin" also subdirectories containing jvm.dll.
 218                 if (Files.isDirectory(bin)) {
 219                     Files.find(bin, 2, (path, attrs) -> {
 220                         return attrs.isRegularFile() && !path.toString().endsWith(".diz");
 221                     }).forEach(this::setExecutable);
 222                 }
 223 
 224                 // jspawnhelper is in lib or lib/<arch>
 225                 Path lib = root.resolve("lib");
 226                 if (Files.isDirectory(lib)) {
 227                     Files.find(lib, 2, (path, attrs) -> {
 228                         return path.getFileName().toString().equals("jspawnhelper")
 229                                 || path.getFileName().toString().equals("jexec");
 230                     }).forEach(this::setExecutable);
 231                 }
 232             }




  29 import java.io.BufferedWriter;
  30 import java.io.ByteArrayInputStream;
  31 import java.io.DataOutputStream;
  32 import java.io.File;
  33 import java.io.FileInputStream;
  34 import java.io.FileOutputStream;
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.io.UncheckedIOException;
  38 import java.io.OutputStream;
  39 import java.io.OutputStreamWriter;
  40 import java.io.Writer;
  41 import java.lang.module.ModuleDescriptor;
  42 import java.nio.charset.StandardCharsets;
  43 import java.nio.file.FileAlreadyExistsException;
  44 import java.nio.file.Files;
  45 import java.nio.file.Path;
  46 import java.nio.file.Paths;
  47 import java.nio.file.StandardOpenOption;
  48 import java.nio.file.attribute.PosixFilePermission;
  49 import java.util.ArrayList;
  50 import java.util.Collections;
  51 import java.util.HashMap;
  52 import java.util.HashSet;
  53 import java.util.List;
  54 import java.util.Map;
  55 import java.util.Objects;
  56 import java.util.Optional;
  57 import java.util.Properties;
  58 import java.util.Set;
  59 import java.util.stream.Stream;
  60 import static java.util.stream.Collectors.*;
  61 
  62 import jdk.tools.jlink.internal.BasicImageWriter;
  63 import jdk.tools.jlink.internal.plugins.FileCopierPlugin.SymImageFile;
  64 import jdk.tools.jlink.internal.ExecutableImage;
  65 import jdk.tools.jlink.plugin.ResourcePool;
  66 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  67 import jdk.tools.jlink.plugin.ResourcePoolModule;
  68 import jdk.tools.jlink.plugin.ResourcePoolModuleView;
  69 import jdk.tools.jlink.plugin.PluginException;
  70 
  71 /**
  72  *
  73  * Default Image Builder. This builder creates the default runtime image layout.
  74  */
  75 public final class DefaultImageBuilder implements ImageBuilder {
  76 
  77     /**
  78      * The default java executable Image.
  79      */
  80     static final class DefaultExecutableImage implements ExecutableImage {
  81 
  82         private final Path home;
  83         private final List<String> args;
  84         private final Set<String> modules;
  85 
  86         DefaultExecutableImage(Path home, Set<String> modules) {
  87             Objects.requireNonNull(home);
  88             if (!Files.exists(home)) {


 127 
 128     private final Path root;
 129     private final Path mdir;
 130     private final Set<String> modules = new HashSet<>();
 131     private String targetOsName;
 132 
 133     /**
 134      * Default image builder constructor.
 135      *
 136      * @param root The image root directory.
 137      * @throws IOException
 138      */
 139     public DefaultImageBuilder(Path root) throws IOException {
 140         Objects.requireNonNull(root);
 141 
 142         this.root = root;
 143         this.mdir = root.resolve("lib");
 144         Files.createDirectories(mdir);
 145     }
 146 
 147     private void storeFiles(List<String> modules, Properties release) throws IOException {
 148         if (release != null) {
 149             addModules(release, modules);
 150             File r = new File(root.toFile(), "release");
 151             try (FileOutputStream fo = new FileOutputStream(r)) {
 152                 release.store(fo, null);
 153             }
 154         }
 155     }
 156 
 157     private void addModules(Properties props, List<String> modules) throws IOException {
 158         int size = modules.size();
 159         StringBuilder builder = new StringBuilder();
 160         int i = 0;
 161         for (String m : modules) {
 162             builder.append(m);
 163             if (i < size - 1) {
 164                 builder.append(' ');
 165             }
 166             i++;
 167         }
 168         props.setProperty("MODULES", quote(builder.toString()));
 169     }
 170 
 171     // utility class to (topological) sort the module names
 172     static class ModuleNamesSorter {
 173         // don't create me!
 174         private ModuleNamesSorter() {}
 175 
 176         // return a stream of dependent module names of the given module name
 177         private static Stream<String> dependencies(String modName, ResourcePoolModuleView modView) {
 178             Optional<ResourcePoolModule> mod = modView.findModule(modName);
 179             if (mod.isPresent()) {
 180                 return mod.get().descriptor().requires().stream().map(ModuleDescriptor.Requires::name);
 181             } else {
 182                 throw new RuntimeException("Missing module: " + modName);
 183             }
 184         }
 185 
 186         // states for module visit
 187         private static enum State {
 188             VISITING, VISITED;
 189         }
 190 
 191         // topological sort helper
 192         private static void tsort(String modName, Map<String, State> state,
 193                     ResourcePoolModuleView modView, List<String> ret) {
 194             state.put(modName, State.VISITING);
 195             dependencies(modName, modView).forEach(depModName -> {
 196                 State st = state.get(depModName);
 197                 if (st == null) {
 198                     tsort(depModName, state, modView, ret);
 199                 } else if (st == State.VISITING) {
 200                     throw new RuntimeException("Cyclic dependency: " + depModName);
 201                 }
 202                 assert st == State.VISITED;
 203             });
 204             state.put(modName, State.VISITED);
 205             ret.add(modName);
 206         }
 207 
 208         // entry point to sort module names by topological sort
 209         static List<String> sort(Set<String> rootMods, ResourcePoolModuleView modView) {
 210             Map<String, State> state = new HashMap<>();
 211             List<String> ret = new ArrayList<>();
 212             for (String rootMod : rootMods) {
 213                 State st = state.get(rootMod);
 214                 if (st == null) {
 215                     tsort(rootMod, state, modView, ret);
 216                 } else if (st == State.VISITING) {
 217                     throw new RuntimeException("Cyclic dependency: " + rootMod);
 218                 }
 219                 assert st == State.VISITED;
 220             }
 221             return ret;
 222         }
 223     }
 224 
 225     @Override
 226     public void storeFiles(Set<String> rootModules, ResourcePool files) {
 227         try {
 228             // populate release properties up-front. targetOsName
 229             // field is assigned from there and used elsewhere.
 230             Properties release = releaseProperties(files);
 231             Path bin = root.resolve("bin");
 232 
 233             // check any duplicated resource files
 234             Map<Path, Set<String>> duplicates = new HashMap<>();
 235             files.entries()
 236                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
 237                 .collect(groupingBy(this::entryToImagePath,
 238                          mapping(ResourcePoolEntry::moduleName, toSet())))
 239                 .entrySet()
 240                 .stream()
 241                 .filter(e -> e.getValue().size() > 1)
 242                 .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
 243 
 244             // write non-classes resource files to the image
 245             files.entries()
 246                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)


 250                     } catch (FileAlreadyExistsException e) {
 251                         // error for duplicated entries
 252                         Path path = entryToImagePath(f);
 253                         UncheckedIOException x =
 254                             new UncheckedIOException(path + " duplicated in " +
 255                                     duplicates.get(path), e);
 256                         x.addSuppressed(e);
 257                         throw x;
 258                     } catch (IOException ioExp) {
 259                         throw new UncheckedIOException(ioExp);
 260                     }
 261                 });
 262 
 263             files.moduleView().modules().forEach(m -> {
 264                 // Only add modules that contain packages
 265                 if (!m.packages().isEmpty()) {
 266                     modules.add(m.name());
 267                 }
 268             });
 269 
 270             storeFiles(ModuleNamesSorter.sort(
 271                 rootModules.isEmpty()? modules : rootModules, files.moduleView()), release);
 272 
 273             if (root.getFileSystem().supportedFileAttributeViews()
 274                     .contains("posix")) {
 275                 // launchers in the bin directory need execute permission.
 276                 // On Windows, "bin" also subdirectories containing jvm.dll.
 277                 if (Files.isDirectory(bin)) {
 278                     Files.find(bin, 2, (path, attrs) -> {
 279                         return attrs.isRegularFile() && !path.toString().endsWith(".diz");
 280                     }).forEach(this::setExecutable);
 281                 }
 282 
 283                 // jspawnhelper is in lib or lib/<arch>
 284                 Path lib = root.resolve("lib");
 285                 if (Files.isDirectory(lib)) {
 286                     Files.find(lib, 2, (path, attrs) -> {
 287                         return path.getFileName().toString().equals("jspawnhelper")
 288                                 || path.getFileName().toString().equals("jexec");
 289                     }).forEach(this::setExecutable);
 290                 }
 291             }


< prev index next >