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 }
|