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 }