1 /*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package tests;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.net.URI;
28 import java.nio.file.FileSystem;
29 import java.nio.file.FileSystems;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.stream.Collectors;
40
41 import tests.JImageGenerator.JLinkTask;
42 import tests.JImageGenerator.JModTask;
43
44 /**
45 * JLink tests helper.
46 */
47 public class Helper {
48
49 private final Path explodedmodssrc;
50 private final Path jmodssrc;
51 private final Path jarssrc;
52 private final Path explodedmodsclasses;
53 private final Path jmodsclasses;
54 private final Path jarsclasses;
55 private final Path jmods;
56 private final Path jars;
57 private final Path images;
58 private final Path explodedmods;
59 private final Path stdjmods;
60 private final Path extracted;
61 private final Path recreated;
62
63 private final Map<String, List<String>> moduleClassDependencies = new HashMap<>();
64 private final Map<String, List<String>> moduleDependencies = new HashMap<>();
65 private final List<String> bootClasses;
66 private final FileSystem fs;
67
68 public static Helper newHelper() throws IOException {
69 Path jdkHome = Paths.get(System.getProperty("test.jdk"));
70 // JPRT not yet ready for jmods
71 if (!Files.exists(jdkHome.resolve("jmods"))) {
72 System.err.println("Test not run, NO jmods directory");
73 return null;
74 }
75 return new Helper(jdkHome);
76 }
77
78 private Helper(Path jdkHome) throws IOException {
79 this.stdjmods = jdkHome.resolve("jmods").normalize();
80 if (!Files.exists(stdjmods)) {
81 throw new IOException("Standard jMods do not exist.");
82 }
83 this.fs = FileSystems.getFileSystem(URI.create("jrt:/"));
84
85 Path javabase = fs.getPath("/modules/java.base");
86 this.bootClasses = Files.find(javabase, Integer.MAX_VALUE,
87 (file, attrs) -> file.toString().endsWith(".class"))
88 .map(Object::toString)
89 .map(s -> s.substring("/modules".length()))
90 .collect(Collectors.toList());
91
92 if (bootClasses.isEmpty()) {
93 throw new AssertionError("No boot class to check against");
94 }
95
96 this.jmods = Paths.get("jmods").toAbsolutePath();
97 Files.createDirectories(jmods);
98 this.jars = Paths.get("jars").toAbsolutePath();
99 Files.createDirectories(jars);
100 this.explodedmods = Paths.get("explodedmods").toAbsolutePath();
101 Files.createDirectories(explodedmods);
102 this.explodedmodssrc = explodedmods.resolve("src");
103 Files.createDirectories(explodedmodssrc);
104 this.jarssrc = jars.resolve("src");
105 Files.createDirectories(jarssrc);
106 this.jmodssrc = jmods.resolve("src");
107 Files.createDirectories(jmodssrc);
108 this.explodedmodsclasses = explodedmods.resolve("classes");
109 Files.createDirectories(explodedmodsclasses);
110 this.jmodsclasses = jmods.resolve("classes");
111 Files.createDirectories(jmodsclasses);
112 this.jarsclasses = jars.resolve("classes");
113 Files.createDirectories(jarsclasses);
114 this.images = Paths.get("images").toAbsolutePath();
115 Files.createDirectories(images);
116 this.extracted = Paths.get("extracted").toAbsolutePath();
117 Files.createDirectories(extracted);
118 this.recreated = Paths.get("recreated").toAbsolutePath();
119 Files.createDirectories(recreated);
120 }
121
122 public void generateDefaultModules() throws IOException {
123 generateDefaultJModule("leaf1");
124 generateDefaultJModule("leaf2");
125 generateDefaultJModule("leaf3");
126
127 generateDefaultJarModule("leaf4");
128 generateDefaultJarModule("leaf5");
129
130 generateDefaultExplodedModule("leaf6");
131 generateDefaultExplodedModule("leaf7");
132
133 generateDefaultJarModule("composite1", "leaf1", "leaf2", "leaf4", "leaf6");
134 generateDefaultJModule("composite2", "composite1", "leaf3", "leaf5", "leaf7",
135 "java.management");
136 }
137
138 public String defaultModulePath() {
139 return stdjmods.toAbsolutePath().toString() + File.pathSeparator
140 + jmods.toAbsolutePath().toString() + File.pathSeparator
141 + jars.toAbsolutePath().toString() + File.pathSeparator
142 + explodedmodsclasses.toAbsolutePath().toString();
143 }
144
145 public Path generateModuleCompiledClasses(
146 Path src, Path classes, String moduleName, String... dependencies) throws IOException {
147 return generateModuleCompiledClasses(src, classes, moduleName, getDefaultClasses(moduleName), dependencies);
148 }
149
150 public Path generateModuleCompiledClasses(
151 Path src, Path classes, String moduleName,
152 List<String> classNames, String... dependencies) throws IOException {
153 if (classNames == null) {
154 classNames = getDefaultClasses(moduleName);
155 }
156 putAppClasses(moduleName, classNames);
157 moduleDependencies.put(moduleName, Arrays.asList(dependencies));
158 String modulePath = defaultModulePath();
159 JImageGenerator.generateSourcesFromTemplate(src, moduleName, classNames.toArray(new String[classNames.size()]));
160 List<String> packages = classNames.stream()
161 .map(JImageGenerator::getPackageName)
162 .distinct()
163 .collect(Collectors.toList());
164 Path srcMod = src.resolve(moduleName);
165 JImageGenerator.generateModuleInfo(srcMod, packages, dependencies);
166 Path destination = classes.resolve(moduleName);
167 if (!JImageGenerator.compile(srcMod, destination, "--module-path", modulePath, "-g")) {
168 throw new AssertionError("Compilation failure");
169 }
170 return destination;
171 }
172
173 public Result generateDefaultJModule(String moduleName, String... dependencies) throws IOException {
174 return generateDefaultJModule(moduleName, getDefaultClasses(moduleName), dependencies);
175 }
176
177 public Result generateDefaultJModule(String moduleName, List<String> classNames,
178 String... dependencies) throws IOException {
179 generateModuleCompiledClasses(jmodssrc, jmodsclasses, moduleName, classNames, dependencies);
180 generateGarbage(jmodsclasses.resolve(moduleName));
181
182 Path jmodFile = jmods.resolve(moduleName + ".jmod");
183 JModTask task = JImageGenerator.getJModTask()
184 .jmod(jmodFile)
185 .addJmods(stdjmods)
186 .addJmods(jmods.toAbsolutePath())
187 .addJars(jars.toAbsolutePath())
188 .addClassPath(jmodsclasses.resolve(moduleName));
189 if (!classNames.isEmpty()) {
190 task.mainClass(classNames.get(0));
191 }
192 return task.create();
193 }
194
195 public Result generateDefaultJarModule(String moduleName, String... dependencies) throws IOException {
196 return generateDefaultJarModule(moduleName, getDefaultClasses(moduleName), dependencies);
197 }
198
199 public Result generateDefaultJarModule(String moduleName, List<String> classNames,
200 String... dependencies) throws IOException {
201 generateModuleCompiledClasses(jarssrc, jarsclasses, moduleName, classNames, dependencies);
202 generateGarbage(jarsclasses.resolve(moduleName));
203
204 Path jarFile = jars.resolve(moduleName + ".jar");
205 JImageGenerator.createJarFile(jarFile, jarsclasses.resolve(moduleName));
206 return new Result(0, "", jarFile);
207 }
208
209 public Result generateDefaultExplodedModule(String moduleName, String... dependencies) throws IOException {
210 return generateDefaultExplodedModule(moduleName, getDefaultClasses(moduleName), dependencies);
211 }
212
213 public Result generateDefaultExplodedModule(String moduleName, List<String> classNames,
214 String... dependencies) throws IOException {
215 generateModuleCompiledClasses(explodedmodssrc, explodedmodsclasses,
216 moduleName, classNames, dependencies);
217
218 Path dir = explodedmods.resolve(moduleName);
219 return new Result(0, "", dir);
220 }
221
222 private void generateGarbage(Path compiled) throws IOException {
223 Path metaInf = compiled.resolve("META-INF").resolve("services");
224 Files.createDirectories(metaInf);
225 Path provider = metaInf.resolve("MyProvider");
226 Files.createFile(provider);
227 Files.createFile(compiled.resolve("toto.jcov"));
228 }
229
230 public static Path createNewFile(Path root, String pathName, String extension) {
231 Path out = root.resolve(pathName + extension);
232 int i = 1;
233 while (Files.exists(out)) {
234 out = root.resolve(pathName + "-" + (++i) + extension);
235 }
236 return out;
237 }
238
239 public Result generateDefaultImage(String module) {
240 return generateDefaultImage(new String[0], module);
241 }
242
243 public Result generateDefaultImage(String[] options, String module) {
244 Path output = createNewFile(images, module, ".image");
245 JLinkTask jLinkTask = JImageGenerator.getJLinkTask()
246 .modulePath(defaultModulePath())
247 .output(output)
248 .addMods(module)
249 .limitMods(module);
250 for (String option : options) {
251 jLinkTask.option(option);
252 }
253 return jLinkTask.call();
254 }
255
256 public Result postProcessImage(Path root, String[] options) {
257 JLinkTask jLinkTask = JImageGenerator.getJLinkTask()
258 .existing(root);
259 for (String option : options) {
260 jLinkTask.option(option);
261 }
262 return jLinkTask.callPostProcess();
263 }
264
265 private List<String> getDefaultClasses(String module) {
266 return Arrays.asList(module + ".Main", module + ".com.foo.bar.X");
267 }
268
269 private void putAppClasses(String module, List<String> classes) {
270 List<String> appClasses = toLocation(module, classes).stream().collect(Collectors.toList());
271 appClasses.add(toLocation(module, "module-info"));
272 moduleClassDependencies.put(module, appClasses);
273 }
274
275 private static String toLocation(String module, String className) {
276 return "/" + module + "/" + className.replaceAll("\\.", "/") + ".class";
277 }
278
279 public static List<String> toLocation(String module, List<String> classNames) {
280 return classNames.stream()
281 .map(clazz -> toLocation(module, clazz))
282 .collect(Collectors.toList());
283 }
284
285 public void checkImage(Path imageDir, String module, String[] paths, String[] files) throws IOException {
286 checkImage(imageDir, module, paths, files, null);
287 }
288
289 public void checkImage(Path imageDir, String module, String[] paths, String[] files, String[] expectedFiles) throws IOException {
290 List<String> unexpectedPaths = new ArrayList<>();
291 if (paths != null) {
292 Collections.addAll(unexpectedPaths, paths);
293 }
294 List<String> unexpectedFiles = new ArrayList<>();
295 if (files != null) {
296 Collections.addAll(unexpectedFiles, files);
297 }
298
299 JImageValidator validator = new JImageValidator(module, gatherExpectedLocations(module),
300 imageDir.toFile(),
301 unexpectedPaths,
302 unexpectedFiles,
303 expectedFiles);
304 System.out.println("*** Validate Image " + module);
305 validator.validate();
306 long moduleExecutionTime = validator.getModuleLauncherExecutionTime();
307 if (moduleExecutionTime != 0) {
308 System.out.println("Module launcher execution time " + moduleExecutionTime);
309 }
310 System.out.println("Java launcher execution time "
311 + validator.getJavaLauncherExecutionTime());
312 System.out.println("***");
313 }
314
315 private List<String> gatherExpectedLocations(String module) throws IOException {
316 List<String> expectedLocations = new ArrayList<>();
317 expectedLocations.addAll(bootClasses);
318 List<String> modules = moduleDependencies.get(module);
319 for (String dep : modules) {
320 Path path = fs.getPath("/modules/" + dep);
321 if (Files.exists(path)) {
322 List<String> locations = Files.find(path, Integer.MAX_VALUE,
323 (p, attrs) -> Files.isRegularFile(p) && p.toString().endsWith(".class")
324 && !p.toString().endsWith("module-info.class"))
325 .map(p -> p.toString().substring("/modules".length()))
326 .collect(Collectors.toList());
327 expectedLocations.addAll(locations);
328 }
329 }
330
331 List<String> appClasses = moduleClassDependencies.get(module);
332 if (appClasses != null) {
333 expectedLocations.addAll(appClasses);
334 }
335 return expectedLocations;
336 }
337
338 public static String getDebugSymbolsExtension() {
339 return ".diz";
340 }
341
342 public Path createNewImageDir(String moduleName) {
343 return createNewFile(getImageDir(), moduleName, ".image");
344 }
345
346 public Path createNewExtractedDir(String name) {
347 return createNewFile(getExtractedDir(), name, ".extracted");
348 }
349
350 public Path createNewRecreatedDir(String name) {
351 return createNewFile(getRecreatedDir(), name, ".jimage");
352 }
353
354 public Path createNewJmodFile(String moduleName) {
355 return createNewFile(getJmodDir(), moduleName, ".jmod");
356 }
357
358 public Path createNewJarFile(String moduleName) {
359 return createNewFile(getJarDir(), moduleName, ".jar");
360 }
361
362 public Path getJmodSrcDir() {
363 return jmodssrc;
364 }
365
366 public Path getJarSrcDir() {
367 return jarssrc;
368 }
369
370 public Path getJmodClassesDir() {
371 return jmodsclasses;
372 }
373
374 public Path getJarClassesDir() {
375 return jarsclasses;
376 }
377
378 public Path getJmodDir() {
379 return jmods;
380 }
381
382 public Path getExplodedModsDir() {
383 return explodedmods;
384 }
385
386 public Path getJarDir() {
387 return jars;
388 }
389
390 public Path getImageDir() {
391 return images;
392 }
393
394 public Path getStdJmodsDir() {
395 return stdjmods;
396 }
397
398 public Path getExtractedDir() {
399 return extracted;
400 }
401
402 public Path getRecreatedDir() {
403 return recreated;
404 }
405 }
--- EOF ---