9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package build.tools.module; 27 28 import jdk.internal.jimage.Archive; 29 import jdk.internal.jimage.ImageFile; 30 import jdk.internal.jimage.ImageModules; 31 32 import java.io.BufferedReader; 33 import java.io.File; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.InputStreamReader; 37 import java.io.PrintWriter; 38 import java.io.UncheckedIOException; 39 import java.nio.ByteOrder; 40 import java.nio.file.Files; 41 import java.nio.file.InvalidPathException; 42 import java.nio.file.Path; 43 import java.nio.file.Paths; 44 import java.nio.file.attribute.PosixFilePermission; 45 import java.util.ArrayList; 46 import java.util.Collection; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.LinkedList; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Optional; 53 import java.util.Set; 54 import java.util.stream.Collectors; 55 56 /** 57 * A tool for building a runtime image. 58 * 59 * java build.tools.module.ImageBuilder <options> --output <path> top/modules.xml,... 60 * Possible options are: 61 * --cmds Location of native commands 62 * --configs Location of config files 63 * --help Print this usage message 64 * --classes Location of module classes files 65 * --libs Location of native libraries 66 * --mods Comma separated list of module names 67 * --output Location of the output path 68 * --endian Byte order of the target runtime; {little,big} 69 */ 70 class ImageBuilder { 71 static class BadArgs extends Exception { 72 private static final long serialVersionUID = 0L; 73 BadArgs(String format, Object... args) { 74 super(String.format(format, args)); 75 this.format = format; 76 this.args = args; 77 } 78 BadArgs showUsage(boolean b) { 79 showUsage = b; 80 return this; 81 } 82 final String format; 83 final Object[] args; 84 boolean showUsage; 85 } 86 87 static abstract class Option { 88 final boolean hasArg; 89 final String[] aliases; 90 Option(boolean hasArg, String... aliases) { 91 this.hasArg = hasArg; 92 this.aliases = aliases; 93 } 94 boolean isHidden() { 95 return false; 96 } 97 boolean matches(String opt) { 98 for (String a : aliases) { 99 if (a.equals(opt)) { 100 return true; 101 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) { 102 return true; 103 } 104 } 105 return false; 106 } 107 boolean ignoreRest() { 108 return false; 109 } 110 abstract void process(ImageBuilder task, String opt, String arg) throws BadArgs; 111 abstract String description(); 112 } 113 114 private static Path CWD = Paths.get(""); 115 116 private static List<Path> splitPath(String arg, String separator) 117 throws BadArgs 118 { 119 List<Path> paths = new ArrayList<>(); 120 for (String p: arg.split(separator)) { 121 if (p.length() > 0) { 122 try { 123 Path path = CWD.resolve(p); 124 if (Files.notExists(path)) 125 throw new BadArgs("path not found: %s", path); 126 paths.add(path); 127 } catch (InvalidPathException x) { 128 throw new BadArgs("path not valid: %s", p); 129 } 130 } 131 } 132 return paths; 133 } 134 135 static Option[] recognizedOptions = { 136 new Option(true, "--cmds") { 137 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 138 task.options.cmds = splitPath(arg, File.pathSeparator); 139 } 140 String description() { return "Location of native commands"; } 141 }, 142 new Option(true, "--configs") { 143 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 144 task.options.configs = splitPath(arg, File.pathSeparator); 145 } 146 String description() { return "Location of config files"; } 147 }, 148 new Option(false, "--help") { 149 void process(ImageBuilder task, String opt, String arg) { 150 task.options.help = true; 151 } 152 String description() { return "Print this usage message"; } 153 }, 154 new Option(true, "--classes") { 155 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 156 task.options.classes = splitPath(arg, File.pathSeparator); 157 } 158 String description() { return "Location of module classes files"; } 159 }, 160 new Option(true, "--libs") { 161 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 162 task.options.libs = splitPath(arg, File.pathSeparator); 163 } 164 String description() { return "Location of native libraries"; } 165 }, 166 new Option(true, "--mods") { 167 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 168 for (String mn : arg.split(",")) { 169 if (mn.isEmpty()) 170 throw new BadArgs("Module not found", mn); 171 task.options.mods.add(mn); 172 } 173 } 174 String description() { return "Comma separated list of module names"; } 175 }, 176 new Option(true, "--output") { 177 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 178 Path path = Paths.get(arg); 179 task.options.output = path; 180 } 181 String description() { return "Location of the output path"; } 182 }, 183 new Option(true, "--endian") { 184 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 185 if (arg.equals("little")) 186 task.options.endian = ByteOrder.LITTLE_ENDIAN; 187 else if (arg.equals("big")) 188 task.options.endian = ByteOrder.BIG_ENDIAN; 189 else 190 throw new BadArgs("Unknown byte order " + arg); 191 } 192 String description() { return "Byte order of the target runtime; {little,big}"; } 193 } 194 }; 195 196 private final Options options = new Options(); 197 198 private PrintWriter log; 199 void setLog(PrintWriter out) { 200 log = out; 201 } 202 203 Set<Module> moduleGraph = new java.util.HashSet<>(); 204 205 /** Module list files */ 206 private static final String BOOT_MODULES = "boot.modules"; 207 private static final String EXT_MODULES = "ext.modules"; 208 209 /** 210 * Result codes. 211 */ 212 static final int EXIT_OK = 0, // Completed with no errors. 213 EXIT_ERROR = 1, // Completed but reported errors. 353 Collection<String> modules = resolve(options.mods); 354 log.print(modules.stream().collect(Collectors.joining(" "))); 355 ImageFileHelper imageHelper = new ImageFileHelper(modules); 356 imageHelper.createModularImage(options.output); 357 358 // jspawnhelper, might be in lib or lib/ARCH 359 Path jspawnhelper = Paths.get("jspawnhelper"); 360 Path lib = options.output.resolve("lib"); 361 Optional<Path> helper = Files.walk(lib, 2) 362 .filter(f -> f.getFileName().equals(jspawnhelper)) 363 .findFirst(); 364 if (helper.isPresent()) 365 helper.get().toFile().setExecutable(true, false); 366 } 367 368 private class ImageFileHelper { 369 final Collection<String> modules; 370 final Set<String> bootModules; 371 final Set<String> extModules; 372 final Set<String> appModules; 373 final ImageModules imf; 374 375 ImageFileHelper(Collection<String> modules) throws IOException { 376 this.modules = modules; 377 this.bootModules = modulesFor(BOOT_MODULES).stream() 378 .filter(modules::contains) 379 .collect(Collectors.toSet()); 380 this.extModules = modulesFor(EXT_MODULES).stream() 381 .filter(modules::contains) 382 .collect(Collectors.toSet()); 383 this.appModules = modules.stream() 384 .filter(m -> !bootModules.contains(m) && !extModules.contains(m)) 385 .collect(Collectors.toSet()); 386 387 this.imf = new ImageModules(bootModules, extModules, appModules); 388 } 389 390 void createModularImage(Path output) throws IOException { 391 Set<Archive> archives = modules.stream() 392 .map(this::toModuleArchive) 393 .collect(Collectors.toSet()); 394 ImageFile.create(output, archives, imf, options.endian); 395 } 396 397 ModuleArchive toModuleArchive(String mn) { 398 return new ModuleArchive(mn, 399 moduleToPath(mn, options.classes, false/*true*/), 400 moduleToPath(mn, options.cmds, false), 401 moduleToPath(mn, options.libs, false), 402 moduleToPath(mn, options.configs, false)); 403 } 404 405 private Path moduleToPath(String name, List<Path> paths, boolean expect) { 406 Set<Path> foundPaths = new HashSet<>(); 407 if (paths != null) { 408 for (Path p : paths) { 409 Path rp = p.resolve(name); 410 if (Files.exists(rp)) 411 foundPaths.add(rp); 412 } 413 } 414 if (foundPaths.size() > 1) | 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package build.tools.module; 27 28 import jdk.internal.jimage.Archive; 29 30 import java.io.BufferedReader; 31 import java.io.File; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.InputStreamReader; 35 import java.io.PrintWriter; 36 import java.nio.ByteOrder; 37 import java.nio.file.Files; 38 import java.nio.file.InvalidPathException; 39 import java.nio.file.Path; 40 import java.nio.file.Paths; 41 import java.util.ArrayList; 42 import java.util.Collection; 43 import java.util.HashMap; 44 import java.util.HashSet; 45 import java.util.LinkedList; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Optional; 49 import java.util.Set; 50 import java.util.stream.Collectors; 51 import jdk.internal.jimage.ImageFileCreator; 52 53 /** 54 * A tool for building a runtime image. 55 * 56 * java build.tools.module.ImageBuilder <options> --output <path> top/modules.xml,... 57 * Possible options are: 58 * --cmds Location of native commands 59 * --configs Location of config files 60 * --help Print this usage message 61 * --classes Location of module classes files 62 * --libs Location of native libraries 63 * --mods Comma separated list of module names 64 * --output Location of the output path 65 * --endian Byte order of the target runtime; {little,big} 66 */ 67 class ImageBuilder { 68 static class BadArgs extends Exception { 69 private static final long serialVersionUID = 0L; 70 BadArgs(String format, Object... args) { 71 super(String.format(format, args)); 72 this.format = format; 73 this.args = args; 74 } 75 BadArgs showUsage(boolean b) { 76 showUsage = b; 77 return this; 78 } 79 final String format; 80 final Object[] args; 81 boolean showUsage; 82 } 83 84 static class Option { 85 86 interface Processing { 87 88 void process(ImageBuilder task, String opt, String arg) throws BadArgs; 89 } 90 91 final boolean hasArg; 92 final String[] aliases; 93 final String description; 94 final Processing processing; 95 96 Option(boolean hasArg, String description, Processing processing, 97 String... aliases) { 98 this.hasArg = hasArg; 99 this.description = description; 100 this.processing = processing; 101 this.aliases = aliases; 102 } 103 boolean isHidden() { 104 return false; 105 } 106 boolean matches(String opt) { 107 for (String a : aliases) { 108 if (a.equals(opt)) { 109 return true; 110 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) { 111 return true; 112 } 113 } 114 return false; 115 } 116 boolean ignoreRest() { 117 return false; 118 } 119 void process(ImageBuilder task, String opt, String arg) throws BadArgs { 120 processing.process(task, opt, arg); 121 } 122 String description() { 123 return description; 124 } 125 } 126 127 private static Path CWD = Paths.get(""); 128 129 private static List<Path> splitPath(String arg, String separator) 130 throws BadArgs 131 { 132 List<Path> paths = new ArrayList<>(); 133 for (String p: arg.split(separator)) { 134 if (p.length() > 0) { 135 try { 136 Path path = CWD.resolve(p); 137 if (Files.notExists(path)) 138 throw new BadArgs("path not found: %s", path); 139 paths.add(path); 140 } catch (InvalidPathException x) { 141 throw new BadArgs("path not valid: %s", p); 142 } 143 } 144 } 145 return paths; 146 } 147 148 static Option[] recognizedOptions = { 149 new Option(true, "Location of native commands", (task, opt, arg) -> { 150 task.options.cmds = splitPath(arg, File.pathSeparator); 151 }, "--cmds"), 152 new Option(true, "Location of config files", (task, opt, arg) -> { 153 task.options.configs = splitPath(arg, File.pathSeparator); 154 }, "--configs"), 155 new Option(false, "Print this usage message", (task, opt, arg) -> { 156 task.options.help = true; 157 }, "--help"), 158 new Option(true, "Location of module classes files", (task, opt, arg) -> { 159 task.options.classes = splitPath(arg, File.pathSeparator); 160 }, "--classes"), 161 new Option(true, "Location of native libraries", (task, opt, arg) -> { 162 task.options.libs = splitPath(arg, File.pathSeparator); 163 }, "--libs"), 164 new Option(true, "Comma separated list of module names", 165 (task, opt, arg) -> { 166 for (String mn : arg.split(",")) { 167 if (mn.isEmpty()) { 168 throw new BadArgs("Module not found", mn); 169 } 170 task.options.mods.add(mn); 171 } 172 }, "--mods"), 173 new Option(true, "Location of the output path", (task, opt, arg) -> { 174 Path path = Paths.get(arg); 175 task.options.output = path; 176 }, "--output"), 177 new Option(true, "Byte order of the target runtime; {little,big}", 178 (task, opt, arg) -> { 179 if (arg.equals("little")) { 180 task.options.endian = ByteOrder.LITTLE_ENDIAN; 181 } else if (arg.equals("big")) { 182 task.options.endian = ByteOrder.BIG_ENDIAN; 183 } else { 184 throw new BadArgs("Unknown byte order " + arg); 185 } 186 }, "--endian") 187 }; 188 189 private final Options options = new Options(); 190 191 private PrintWriter log; 192 void setLog(PrintWriter out) { 193 log = out; 194 } 195 196 Set<Module> moduleGraph = new java.util.HashSet<>(); 197 198 /** Module list files */ 199 private static final String BOOT_MODULES = "boot.modules"; 200 private static final String EXT_MODULES = "ext.modules"; 201 202 /** 203 * Result codes. 204 */ 205 static final int EXIT_OK = 0, // Completed with no errors. 206 EXIT_ERROR = 1, // Completed but reported errors. 346 Collection<String> modules = resolve(options.mods); 347 log.print(modules.stream().collect(Collectors.joining(" "))); 348 ImageFileHelper imageHelper = new ImageFileHelper(modules); 349 imageHelper.createModularImage(options.output); 350 351 // jspawnhelper, might be in lib or lib/ARCH 352 Path jspawnhelper = Paths.get("jspawnhelper"); 353 Path lib = options.output.resolve("lib"); 354 Optional<Path> helper = Files.walk(lib, 2) 355 .filter(f -> f.getFileName().equals(jspawnhelper)) 356 .findFirst(); 357 if (helper.isPresent()) 358 helper.get().toFile().setExecutable(true, false); 359 } 360 361 private class ImageFileHelper { 362 final Collection<String> modules; 363 final Set<String> bootModules; 364 final Set<String> extModules; 365 final Set<String> appModules; 366 367 ImageFileHelper(Collection<String> modules) throws IOException { 368 this.modules = modules; 369 this.bootModules = modulesFor(BOOT_MODULES).stream() 370 .filter(modules::contains) 371 .collect(Collectors.toSet()); 372 this.extModules = modulesFor(EXT_MODULES).stream() 373 .filter(modules::contains) 374 .collect(Collectors.toSet()); 375 this.appModules = modules.stream() 376 .filter(m -> m.length() != 0 && 377 !bootModules.contains(m) && 378 !extModules.contains(m)) 379 .collect(Collectors.toSet()); 380 } 381 382 void createModularImage(Path output) throws IOException { 383 Set<Archive> bootArchives = bootModules.stream() 384 .map(this::toModuleArchive) 385 .collect(Collectors.toSet()); 386 Set<Archive> extArchives = extModules.stream() 387 .map(this::toModuleArchive) 388 .collect(Collectors.toSet()); 389 Set<Archive> appArchives = appModules.stream() 390 .map(this::toModuleArchive) 391 .collect(Collectors.toSet()); 392 ImageFileCreator.create(output, "bootmodules", bootArchives, options.endian); 393 ImageFileCreator.create(output, "extmodules", extArchives, options.endian); 394 ImageFileCreator.create(output, "appmodules", appArchives, options.endian); 395 } 396 397 ModuleArchive toModuleArchive(String mn) { 398 return new ModuleArchive(mn, 399 moduleToPath(mn, options.classes, false/*true*/), 400 moduleToPath(mn, options.cmds, false), 401 moduleToPath(mn, options.libs, false), 402 moduleToPath(mn, options.configs, false)); 403 } 404 405 private Path moduleToPath(String name, List<Path> paths, boolean expect) { 406 Set<Path> foundPaths = new HashSet<>(); 407 if (paths != null) { 408 for (Path p : paths) { 409 Path rp = p.resolve(name); 410 if (Files.exists(rp)) 411 foundPaths.add(rp); 412 } 413 } 414 if (foundPaths.size() > 1) |