1 /* 2 * Copyright (c) 2015, 2018, 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 newHelper()68 public static Helper newHelper() throws IOException { 69 Path jdkHome = Paths.get(System.getProperty("test.jdk")); 70 if (!Files.exists(jdkHome.resolve("jmods"))) { 71 // Skip test if the jmods directory is missing (e.g. exploded image) 72 System.err.println("Test not run, NO jmods directory"); 73 return null; 74 } 75 return new Helper(jdkHome); 76 } 77 Helper(Path jdkHome)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 generateDefaultModules()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 defaultModulePath()138 public String defaultModulePath() { 139 return defaultModulePath(true); 140 } 141 defaultModulePath(boolean includeStdMods)142 public String defaultModulePath(boolean includeStdMods) { 143 return (includeStdMods? stdjmods.toAbsolutePath().toString() : "") + File.pathSeparator 144 + jmods.toAbsolutePath().toString() + File.pathSeparator 145 + jars.toAbsolutePath().toString() + File.pathSeparator 146 + explodedmodsclasses.toAbsolutePath().toString(); 147 } 148 generateModuleCompiledClasses( Path src, Path classes, String moduleName, String... dependencies)149 public Path generateModuleCompiledClasses( 150 Path src, Path classes, String moduleName, String... dependencies) throws IOException { 151 return generateModuleCompiledClasses(src, classes, moduleName, getDefaultClasses(moduleName), dependencies); 152 } 153 generateModuleCompiledClasses( Path src, Path classes, String moduleName, List<String> classNames, String... dependencies)154 public Path generateModuleCompiledClasses( 155 Path src, Path classes, String moduleName, 156 List<String> classNames, String... dependencies) throws IOException { 157 if (classNames == null) { 158 classNames = getDefaultClasses(moduleName); 159 } 160 putAppClasses(moduleName, classNames); 161 moduleDependencies.put(moduleName, Arrays.asList(dependencies)); 162 String modulePath = defaultModulePath(); 163 JImageGenerator.generateSourcesFromTemplate(src, moduleName, classNames.toArray(new String[classNames.size()])); 164 List<String> packages = classNames.stream() 165 .map(JImageGenerator::getPackageName) 166 .distinct() 167 .collect(Collectors.toList()); 168 Path srcMod = src.resolve(moduleName); 169 JImageGenerator.generateModuleInfo(srcMod, packages, dependencies); 170 Path destination = classes.resolve(moduleName); 171 if (!JImageGenerator.compile(srcMod, destination, "--module-path", modulePath, "-g")) { 172 throw new AssertionError("Compilation failure"); 173 } 174 return destination; 175 } 176 generateDefaultJModule(String moduleName, String... dependencies)177 public Result generateDefaultJModule(String moduleName, String... dependencies) throws IOException { 178 return generateDefaultJModule(moduleName, getDefaultClasses(moduleName), dependencies); 179 } 180 generateDefaultJModule(String moduleName, List<String> classNames, String... dependencies)181 public Result generateDefaultJModule(String moduleName, List<String> classNames, 182 String... dependencies) throws IOException { 183 generateModuleCompiledClasses(jmodssrc, jmodsclasses, moduleName, classNames, dependencies); 184 generateGarbage(jmodsclasses.resolve(moduleName)); 185 186 Path jmodFile = jmods.resolve(moduleName + ".jmod"); 187 JModTask task = JImageGenerator.getJModTask() 188 .jmod(jmodFile) 189 .addJmods(stdjmods) 190 .addJmods(jmods.toAbsolutePath()) 191 .addJars(jars.toAbsolutePath()) 192 .addClassPath(jmodsclasses.resolve(moduleName)); 193 if (!classNames.isEmpty()) { 194 task.mainClass(classNames.get(0)); 195 } 196 return task.create(); 197 } 198 generateDefaultJarModule(String moduleName, String... dependencies)199 public Result generateDefaultJarModule(String moduleName, String... dependencies) throws IOException { 200 return generateDefaultJarModule(moduleName, getDefaultClasses(moduleName), dependencies); 201 } 202 generateDefaultJarModule(String moduleName, List<String> classNames, String... dependencies)203 public Result generateDefaultJarModule(String moduleName, List<String> classNames, 204 String... dependencies) throws IOException { 205 generateModuleCompiledClasses(jarssrc, jarsclasses, moduleName, classNames, dependencies); 206 generateGarbage(jarsclasses.resolve(moduleName)); 207 208 Path jarFile = jars.resolve(moduleName + ".jar"); 209 JImageGenerator.createJarFile(jarFile, jarsclasses.resolve(moduleName)); 210 return new Result(0, "", jarFile); 211 } 212 generateDefaultExplodedModule(String moduleName, String... dependencies)213 public Result generateDefaultExplodedModule(String moduleName, String... dependencies) throws IOException { 214 return generateDefaultExplodedModule(moduleName, getDefaultClasses(moduleName), dependencies); 215 } 216 generateDefaultExplodedModule(String moduleName, List<String> classNames, String... dependencies)217 public Result generateDefaultExplodedModule(String moduleName, List<String> classNames, 218 String... dependencies) throws IOException { 219 generateModuleCompiledClasses(explodedmodssrc, explodedmodsclasses, 220 moduleName, classNames, dependencies); 221 222 Path dir = explodedmods.resolve("classes").resolve(moduleName); 223 return new Result(0, "", dir); 224 } 225 generateGarbage(Path compiled)226 private void generateGarbage(Path compiled) throws IOException { 227 Path metaInf = compiled.resolve("META-INF").resolve("services"); 228 Files.createDirectories(metaInf); 229 Path provider = metaInf.resolve("MyProvider"); 230 Files.createFile(provider); 231 Files.createFile(compiled.resolve("toto.jcov")); 232 } 233 createNewFile(Path root, String pathName, String extension)234 public static Path createNewFile(Path root, String pathName, String extension) { 235 Path out = root.resolve(pathName + extension); 236 int i = 1; 237 while (Files.exists(out)) { 238 out = root.resolve(pathName + "-" + (++i) + extension); 239 } 240 return out; 241 } 242 generateDefaultImage(String module)243 public Result generateDefaultImage(String module) { 244 return generateDefaultImage(new String[0], module); 245 } 246 generateDefaultImage(String[] options, String module)247 public Result generateDefaultImage(String[] options, String module) { 248 Path output = createNewFile(images, module, ".image"); 249 JLinkTask jLinkTask = JImageGenerator.getJLinkTask() 250 .modulePath(defaultModulePath()) 251 .output(output) 252 .addMods(module) 253 .limitMods(module); 254 for (String option : options) { 255 jLinkTask.option(option); 256 } 257 return jLinkTask.call(); 258 } 259 postProcessImage(Path root, String[] options)260 public Result postProcessImage(Path root, String[] options) { 261 JLinkTask jLinkTask = JImageGenerator.getJLinkTask() 262 .existing(root); 263 for (String option : options) { 264 jLinkTask.option(option); 265 } 266 return jLinkTask.callPostProcess(); 267 } 268 getDefaultClasses(String module)269 private List<String> getDefaultClasses(String module) { 270 return Arrays.asList(module + ".Main", module + ".com.foo.bar.X"); 271 } 272 putAppClasses(String module, List<String> classes)273 private void putAppClasses(String module, List<String> classes) { 274 List<String> appClasses = toLocation(module, classes).stream().collect(Collectors.toList()); 275 appClasses.add(toLocation(module, "module-info")); 276 moduleClassDependencies.put(module, appClasses); 277 } 278 toLocation(String module, String className)279 private static String toLocation(String module, String className) { 280 return "/" + module + "/" + className.replaceAll("\\.", "/") + ".class"; 281 } 282 toLocation(String module, List<String> classNames)283 public static List<String> toLocation(String module, List<String> classNames) { 284 return classNames.stream() 285 .map(clazz -> toLocation(module, clazz)) 286 .collect(Collectors.toList()); 287 } 288 checkImage(Path imageDir, String module, String[] paths, String[] files)289 public void checkImage(Path imageDir, String module, String[] paths, String[] files) throws IOException { 290 checkImage(imageDir, module, paths, files, null); 291 } 292 checkImage(Path imageDir, String module, String[] paths, String[] files, String[] expectedFiles)293 public void checkImage(Path imageDir, String module, String[] paths, String[] files, String[] expectedFiles) throws IOException { 294 List<String> unexpectedPaths = new ArrayList<>(); 295 if (paths != null) { 296 Collections.addAll(unexpectedPaths, paths); 297 } 298 List<String> unexpectedFiles = new ArrayList<>(); 299 if (files != null) { 300 Collections.addAll(unexpectedFiles, files); 301 } 302 303 JImageValidator validator = new JImageValidator(module, gatherExpectedLocations(module), 304 imageDir.toFile(), 305 unexpectedPaths, 306 unexpectedFiles, 307 expectedFiles); 308 System.out.println("*** Validate Image " + module); 309 validator.validate(); 310 long moduleExecutionTime = validator.getModuleLauncherExecutionTime(); 311 if (moduleExecutionTime != 0) { 312 System.out.println("Module launcher execution time " + moduleExecutionTime); 313 } 314 System.out.println("Java launcher execution time " 315 + validator.getJavaLauncherExecutionTime()); 316 System.out.println("***"); 317 } 318 gatherExpectedLocations(String module)319 private List<String> gatherExpectedLocations(String module) throws IOException { 320 List<String> expectedLocations = new ArrayList<>(); 321 expectedLocations.addAll(bootClasses); 322 List<String> modules = moduleDependencies.get(module); 323 for (String dep : modules) { 324 Path path = fs.getPath("/modules/" + dep); 325 if (Files.exists(path)) { 326 List<String> locations = Files.find(path, Integer.MAX_VALUE, 327 (p, attrs) -> Files.isRegularFile(p) && p.toString().endsWith(".class") 328 && !p.toString().endsWith("module-info.class")) 329 .map(p -> p.toString().substring("/modules".length())) 330 .collect(Collectors.toList()); 331 expectedLocations.addAll(locations); 332 } 333 } 334 335 List<String> appClasses = moduleClassDependencies.get(module); 336 if (appClasses != null) { 337 expectedLocations.addAll(appClasses); 338 } 339 return expectedLocations; 340 } 341 getDebugSymbolsExtension()342 public static String getDebugSymbolsExtension() { 343 return ".diz"; 344 } 345 createNewImageDir(String moduleName)346 public Path createNewImageDir(String moduleName) { 347 return createNewFile(getImageDir(), moduleName, ".image"); 348 } 349 createNewExtractedDir(String name)350 public Path createNewExtractedDir(String name) { 351 return createNewFile(getExtractedDir(), name, ".extracted"); 352 } 353 createNewRecreatedDir(String name)354 public Path createNewRecreatedDir(String name) { 355 return createNewFile(getRecreatedDir(), name, ".jimage"); 356 } 357 createNewJmodFile(String moduleName)358 public Path createNewJmodFile(String moduleName) { 359 return createNewFile(getJmodDir(), moduleName, ".jmod"); 360 } 361 createNewJarFile(String moduleName)362 public Path createNewJarFile(String moduleName) { 363 return createNewFile(getJarDir(), moduleName, ".jar"); 364 } 365 getJmodSrcDir()366 public Path getJmodSrcDir() { 367 return jmodssrc; 368 } 369 getJarSrcDir()370 public Path getJarSrcDir() { 371 return jarssrc; 372 } 373 getJmodClassesDir()374 public Path getJmodClassesDir() { 375 return jmodsclasses; 376 } 377 getJarClassesDir()378 public Path getJarClassesDir() { 379 return jarsclasses; 380 } 381 getJmodDir()382 public Path getJmodDir() { 383 return jmods; 384 } 385 getExplodedModsDir()386 public Path getExplodedModsDir() { 387 return explodedmods; 388 } 389 getJarDir()390 public Path getJarDir() { 391 return jars; 392 } 393 getImageDir()394 public Path getImageDir() { 395 return images; 396 } 397 getStdJmodsDir()398 public Path getStdJmodsDir() { 399 return stdjmods; 400 } 401 getExtractedDir()402 public Path getExtractedDir() { 403 return extracted; 404 } 405 getRecreatedDir()406 public Path getRecreatedDir() { 407 return recreated; 408 } 409 } 410