59 import java.lang.module.ModuleFinder; 60 import java.lang.module.ModuleReference; 61 62 final class JLinkBundlerHelper { 63 64 private static final ResourceBundle I18N = ResourceBundle.getBundle( 65 "jdk.jpackage.internal.resources.MainResources"); 66 private static final String JRE_MODULES_FILENAME = 67 "jdk/jpackage/internal/resources/jre.list"; 68 private static final String SERVER_JRE_MODULES_FILENAME = 69 "jdk/jpackage/internal/resources/jre.module.list"; 70 71 static final ToolProvider JLINK_TOOL = 72 ToolProvider.findFirst("jlink").orElseThrow(); 73 74 private JLinkBundlerHelper() {} 75 76 @SuppressWarnings("unchecked") 77 static final BundlerParamInfo<Integer> DEBUG = 78 new StandardBundlerParam<>( 79 "", 80 "", 81 "-J-Xdebug", 82 Integer.class, 83 p -> null, 84 (s, p) -> { 85 return Integer.valueOf(s); 86 }); 87 88 static String listOfPathToString(List<Path> value) { 89 String result = ""; 90 91 for (Path path : value) { 92 if (result.length() > 0) { 93 result += File.pathSeparator; 94 } 95 96 result += path.toString(); 97 } 98 99 return result; 100 } 173 174 return result; 175 } 176 177 private static Set<String> getValidModules(List<Path> modulePath, 178 Set<String> addModules, Set<String> limitModules) { 179 ModuleHelper moduleHelper = new ModuleHelper( 180 modulePath, addModules, limitModules); 181 return removeInvalidModules(modulePath, moduleHelper.modules()); 182 } 183 184 static void execute(Map<String, ? super Object> params, 185 AbstractAppImageBuilder imageBuilder) 186 throws IOException, Exception { 187 List<Path> modulePath = 188 StandardBundlerParam.MODULE_PATH.fetchFrom(params); 189 Set<String> addModules = 190 StandardBundlerParam.ADD_MODULES.fetchFrom(params); 191 Set<String> limitModules = 192 StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); 193 boolean stripNativeCommands = 194 StandardBundlerParam.STRIP_NATIVE_COMMANDS.fetchFrom(params); 195 Path outputDir = imageBuilder.getRoot(); 196 String excludeFileList = imageBuilder.getExcludeFileList(); 197 File mainJar = getMainJar(params); 198 ModFile.ModType mainJarType = ModFile.ModType.Unknown; 199 200 if (mainJar != null) { 201 mainJarType = new ModFile(mainJar).getModType(); 202 } else if (StandardBundlerParam.MODULE.fetchFrom(params) == null) { 203 // user specified only main class, all jars will be on the classpath 204 mainJarType = ModFile.ModType.UnnamedJar; 205 } 206 207 // Modules 208 String mainModule = getMainModule(params); 209 if (mainJarType == ModFile.ModType.UnnamedJar) { 210 // The default for an unnamed jar is ALL_DEFAULT 211 addModules.add(ModuleHelper.ALL_DEFAULT); 212 } else if (mainJarType == ModFile.ModType.Unknown || 213 mainJarType == ModFile.ModType.ModularJar) { 214 if (mainModule == null) { 215 addModules.add(ModuleHelper.ALL_DEFAULT); 216 } 217 } 218 219 Set<String> validModules = 220 getValidModules(modulePath, addModules, limitModules); 221 if (mainModule != null) { 222 validModules.add(mainModule); 223 } 224 225 Log.verbose(MessageFormat.format( 226 I18N.getString("message.modules"), validModules.toString())); 227 228 runJLink(outputDir, modulePath, validModules, limitModules, 229 excludeFileList, stripNativeCommands, 230 new HashMap<String,String>()); 231 232 imageBuilder.prepareApplicationFiles(); 233 } 234 235 236 static void generateJre(Map<String, ? super Object> params, 237 AbstractAppImageBuilder imageBuilder) 238 throws IOException, Exception { 239 List<Path> modulePath = 240 StandardBundlerParam.MODULE_PATH.fetchFrom(params); 241 Set<String> addModules = 242 StandardBundlerParam.ADD_MODULES.fetchFrom(params); 243 Set<String> limitModules = 244 StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); 245 boolean stripNativeCommands = 246 StandardBundlerParam.STRIP_NATIVE_COMMANDS.fetchFrom(params); 247 Path outputDir = imageBuilder.getRoot(); 248 addModules.add(ModuleHelper.ALL_MODULE_PATH); 249 Set<String> redistModules = getValidModules(modulePath, 250 addModules, limitModules); 251 addModules.addAll(redistModules); 252 253 Log.verbose(MessageFormat.format( 254 I18N.getString("message.modules"), addModules.toString())); 255 256 runJLink(outputDir, modulePath, addModules, limitModules, 257 null, stripNativeCommands, new HashMap<String,String>()); 258 259 imageBuilder.prepareJreFiles(); 260 } 261 262 // Returns the path to the JDK modules in the user defined module path. 263 static Path findPathOfModule( List<Path> modulePath, String moduleName) { 264 265 for (Path path : modulePath) { 266 Path moduleNamePath = path.resolve(moduleName); 267 268 if (Files.exists(moduleNamePath)) { 269 return path; 270 } 271 } 272 273 return null; 274 } 275 276 /* 277 * Returns the set of modules that would be visible by default for 278 * a non-modular-aware application consisting of the given elements. 279 */ 280 private static Set<String> getDefaultModules( 281 Path[] paths, String[] addModules) { 391 return modules; 392 } 393 394 private static Set<String> getModuleNamesFromPath(List<Path> Value) { 395 Set<String> result = new LinkedHashSet<String>(); 396 ModuleManager mm = new ModuleManager(Value); 397 List<ModFile> modFiles = mm.getModules( 398 EnumSet.of(ModuleManager.SearchType.ModularJar, 399 ModuleManager.SearchType.Jmod, 400 ModuleManager.SearchType.ExplodedModule)); 401 402 for (ModFile modFile : modFiles) { 403 result.add(modFile.getModName()); 404 } 405 return result; 406 } 407 } 408 409 private static void runJLink(Path output, List<Path> modulePath, 410 Set<String> modules, Set<String> limitModules, String excludes, 411 boolean strip, HashMap<String, String> user) throws IOException { 412 413 // This is just to ensure jlink is given a non-existant directory 414 // The passed in output path should be non-existant or empty directory 415 IOUtils.deleteRecursive(output.toFile()); 416 417 ArrayList<String> args = new ArrayList<String>(); 418 args.add("--output"); 419 args.add(output.toString()); 420 if (modulePath != null && !modulePath.isEmpty()) { 421 args.add("--module-path"); 422 args.add(getPathList(modulePath)); 423 } 424 if (modules != null && !modules.isEmpty()) { 425 args.add("--add-modules"); 426 args.add(getStringList(modules)); 427 } 428 if (limitModules != null && !limitModules.isEmpty()) { 429 args.add("--limit-modules"); 430 args.add(getStringList(limitModules)); 431 } 432 if (excludes != null) { 433 args.add("--exclude-files"); 434 args.add(excludes); 435 } 436 if (strip) { 437 args.add("--strip-native-commands"); 438 } 439 for (Map.Entry<String, String> entry : user.entrySet()) { 440 args.add(entry.getKey()); 441 args.add(entry.getValue()); 442 } 443 args.add("--strip-debug"); 444 args.add("--no-header-files"); 445 args.add("--bind-services"); 446 447 StringWriter writer = new StringWriter(); 448 PrintWriter pw = new PrintWriter(writer); 449 450 Log.verbose("jlink arguments: " + args); 451 int retVal = JLINK_TOOL.run(pw, pw, args.toArray(new String[0])); 452 String jlinkOut = writer.toString(); 453 454 if (retVal != 0) { 455 throw new IOException("jlink failed with: " + jlinkOut); 456 } else if (jlinkOut.length() > 0) { 457 Log.verbose("jlink output: " + jlinkOut); 458 } 459 } 460 461 private static String getPathList(List<Path> pathList) { 462 String ret = null; 463 for (Path p : pathList) { 464 String s = Matcher.quoteReplacement(p.toString()); 465 if (ret == null) { | 59 import java.lang.module.ModuleFinder; 60 import java.lang.module.ModuleReference; 61 62 final class JLinkBundlerHelper { 63 64 private static final ResourceBundle I18N = ResourceBundle.getBundle( 65 "jdk.jpackage.internal.resources.MainResources"); 66 private static final String JRE_MODULES_FILENAME = 67 "jdk/jpackage/internal/resources/jre.list"; 68 private static final String SERVER_JRE_MODULES_FILENAME = 69 "jdk/jpackage/internal/resources/jre.module.list"; 70 71 static final ToolProvider JLINK_TOOL = 72 ToolProvider.findFirst("jlink").orElseThrow(); 73 74 private JLinkBundlerHelper() {} 75 76 @SuppressWarnings("unchecked") 77 static final BundlerParamInfo<Integer> DEBUG = 78 new StandardBundlerParam<>( 79 "-J-Xdebug", 80 Integer.class, 81 p -> null, 82 (s, p) -> { 83 return Integer.valueOf(s); 84 }); 85 86 static String listOfPathToString(List<Path> value) { 87 String result = ""; 88 89 for (Path path : value) { 90 if (result.length() > 0) { 91 result += File.pathSeparator; 92 } 93 94 result += path.toString(); 95 } 96 97 return result; 98 } 171 172 return result; 173 } 174 175 private static Set<String> getValidModules(List<Path> modulePath, 176 Set<String> addModules, Set<String> limitModules) { 177 ModuleHelper moduleHelper = new ModuleHelper( 178 modulePath, addModules, limitModules); 179 return removeInvalidModules(modulePath, moduleHelper.modules()); 180 } 181 182 static void execute(Map<String, ? super Object> params, 183 AbstractAppImageBuilder imageBuilder) 184 throws IOException, Exception { 185 List<Path> modulePath = 186 StandardBundlerParam.MODULE_PATH.fetchFrom(params); 187 Set<String> addModules = 188 StandardBundlerParam.ADD_MODULES.fetchFrom(params); 189 Set<String> limitModules = 190 StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); 191 Path outputDir = imageBuilder.getRoot(); 192 String excludeFileList = imageBuilder.getExcludeFileList(); 193 File mainJar = getMainJar(params); 194 ModFile.ModType mainJarType = ModFile.ModType.Unknown; 195 196 if (mainJar != null) { 197 mainJarType = new ModFile(mainJar).getModType(); 198 } else if (StandardBundlerParam.MODULE.fetchFrom(params) == null) { 199 // user specified only main class, all jars will be on the classpath 200 mainJarType = ModFile.ModType.UnnamedJar; 201 } 202 203 // Modules 204 String mainModule = getMainModule(params); 205 if (mainModule == null) { 206 if (mainJarType == ModFile.ModType.UnnamedJar) { 207 if (addModules.isEmpty()) { 208 // The default for an unnamed jar is ALL_DEFAULT 209 addModules.add(ModuleHelper.ALL_DEFAULT); 210 } 211 } else if (mainJarType == ModFile.ModType.Unknown || 212 mainJarType == ModFile.ModType.ModularJar) { 213 addModules.add(ModuleHelper.ALL_DEFAULT); 214 } 215 } 216 217 Set<String> validModules = 218 getValidModules(modulePath, addModules, limitModules); 219 220 if (mainModule != null) { 221 validModules.add(mainModule); 222 } 223 224 Log.verbose(MessageFormat.format( 225 I18N.getString("message.modules"), validModules.toString())); 226 227 runJLink(outputDir, modulePath, validModules, limitModules, 228 excludeFileList, new HashMap<String,String>()); 229 230 imageBuilder.prepareApplicationFiles(); 231 } 232 233 234 // Returns the path to the JDK modules in the user defined module path. 235 static Path findPathOfModule( List<Path> modulePath, String moduleName) { 236 237 for (Path path : modulePath) { 238 Path moduleNamePath = path.resolve(moduleName); 239 240 if (Files.exists(moduleNamePath)) { 241 return path; 242 } 243 } 244 245 return null; 246 } 247 248 /* 249 * Returns the set of modules that would be visible by default for 250 * a non-modular-aware application consisting of the given elements. 251 */ 252 private static Set<String> getDefaultModules( 253 Path[] paths, String[] addModules) { 363 return modules; 364 } 365 366 private static Set<String> getModuleNamesFromPath(List<Path> Value) { 367 Set<String> result = new LinkedHashSet<String>(); 368 ModuleManager mm = new ModuleManager(Value); 369 List<ModFile> modFiles = mm.getModules( 370 EnumSet.of(ModuleManager.SearchType.ModularJar, 371 ModuleManager.SearchType.Jmod, 372 ModuleManager.SearchType.ExplodedModule)); 373 374 for (ModFile modFile : modFiles) { 375 result.add(modFile.getModName()); 376 } 377 return result; 378 } 379 } 380 381 private static void runJLink(Path output, List<Path> modulePath, 382 Set<String> modules, Set<String> limitModules, String excludes, 383 HashMap<String, String> user) throws IOException { 384 385 // This is just to ensure jlink is given a non-existant directory 386 // The passed in output path should be non-existant or empty directory 387 IOUtils.deleteRecursive(output.toFile()); 388 389 ArrayList<String> args = new ArrayList<String>(); 390 args.add("--output"); 391 args.add(output.toString()); 392 if (modulePath != null && !modulePath.isEmpty()) { 393 args.add("--module-path"); 394 args.add(getPathList(modulePath)); 395 } 396 if (modules != null && !modules.isEmpty()) { 397 args.add("--add-modules"); 398 args.add(getStringList(modules)); 399 } 400 if (limitModules != null && !limitModules.isEmpty()) { 401 args.add("--limit-modules"); 402 args.add(getStringList(limitModules)); 403 } 404 if (excludes != null) { 405 args.add("--exclude-files"); 406 args.add(excludes); 407 } 408 if (user != null && !user.isEmpty()) { 409 for (Map.Entry<String, String> entry : user.entrySet()) { 410 args.add(entry.getKey()); 411 args.add(entry.getValue()); 412 } 413 } else { 414 args.add("--bind-services"); 415 args.add("--strip-native-commands"); 416 args.add("--strip-debug"); 417 args.add("--no-man-pages"); 418 args.add("--no-header-files"); 419 } 420 421 StringWriter writer = new StringWriter(); 422 PrintWriter pw = new PrintWriter(writer); 423 424 Log.verbose("jlink arguments: " + args); 425 int retVal = JLINK_TOOL.run(pw, pw, args.toArray(new String[0])); 426 String jlinkOut = writer.toString(); 427 428 if (retVal != 0) { 429 throw new IOException("jlink failed with: " + jlinkOut); 430 } else if (jlinkOut.length() > 0) { 431 Log.verbose("jlink output: " + jlinkOut); 432 } 433 } 434 435 private static String getPathList(List<Path> pathList) { 436 String ret = null; 437 for (Path p : pathList) { 438 String s = Matcher.quoteReplacement(p.toString()); 439 if (ret == null) { |