1 /* 2 * Copyright (c) 2009, 2011, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 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 org.openjdk.jigsaw.cli; 27 28 import java.lang.module.*; 29 import java.io.*; 30 import java.security.SignatureException; 31 import java.util.*; 32 33 import static java.lang.System.out; 34 import static java.lang.System.err; 35 36 import org.openjdk.jigsaw.*; 37 import org.openjdk.jigsaw.SimpleLibrary.StorageOption; 38 import org.openjdk.internal.joptsimple.*; 39 40 /* Interface: 41 42 jpkg [-v] [-L <library>] [-r <resource-dir>] [-i include-dir] \ 43 [-m <module_dir>] [-d <output_dir>] [-c <command>] [-n <name>] \ 44 [-e <e-mail@address>] [-s <short description>] [-l <long description>] \ 45 [-x <extra metadata>] [deb|jmod] <module_name>* 46 47 -v : verbose output 48 -L : library the modules are installed to 49 -i : directory with files to include as part of the package 50 -m : directory with modules to package 51 -d : destination directory to put the package in 52 -c : command name for launcher invoking main class 53 -n : maintainer name 54 -e : maintainer e-mail address 55 -s : short description 56 -l : long description 57 -x : additional metadata - for Debian packages, a file whose 58 contents gets appended to DEBIAN/control 59 --fast : use fastest, rather then best compression 60 */ 61 62 public class Packager { 63 64 private static JigsawModuleSystem jms 65 = JigsawModuleSystem.instance(); 66 67 private static boolean jigsawDevMode 68 = System.getenv("JIGSAW_DEV_MODE") != null; 69 70 /** Temp dir for modules to be pre-installed into */ 71 private static File tmp_dst; 72 73 /** Directory where the classes to package are stored on disk. */ 74 private File classes = new File(System.getProperty("user.dir")); 75 76 /** Default destination dir is current directory */ 77 private File destination = new File(System.getProperty("user.dir")); 78 79 /** The default location to install modules to */ 80 private File library = new File("/usr/lib/java/modules"); 81 82 /** The JRE to use in control scripts */ 83 private File javaHome = new File(System.getProperty("java.home")); 84 85 private boolean verbose; 86 87 private boolean fast; 88 89 /** Command launcher for main class */ 90 private String bincmd; 91 92 /** Directory with optional files to include in package */ 93 private File includes; 94 95 /** Name of the maintainer or creator of the package */ 96 private String maintainer_name; 97 98 /** Default name of the maintainer or creator of the package */ 99 private String default_maintainer_name = System.getProperty("user.name"); 100 101 /** E-mail address of the maintainer or creator of the package */ 102 private String maintainer_email; 103 104 /** Default E-mail address of the maintainer or creator of the package */ 105 private String default_maintainer_email = "<generated@by.jpkg>"; 106 107 /** Short description of the package */ 108 private String short_description; 109 110 /** Default short description of the package */ 111 private static String default_short_description = "Generated by jpkg"; 112 113 /** Long description of the package */ 114 private String long_description; 115 116 /** Default long description of the package */ 117 private static String default_long_description 118 = " This package was automatically generated from the corresponding Jigsaw module.\n" 119 + " Information on Jigsaw is available at http://openjdk.java.net/projects/jigsaw."; 120 121 /** Packaging-system dependent additional metadata */ 122 private File extra_metadata; 123 124 private File natlibs; 125 private File natcmds; 126 private File config_dir; 127 128 // Installed size 129 private Integer installedSize = null; 130 131 // Platform boot module 132 private static final String BOOT_MODULE = "jdk.base"; 133 134 private static void createTempWorkDir() 135 throws Command.Exception 136 { 137 try { 138 tmp_dst = File.createTempFile("jigsaw",null); 139 Files.delete(tmp_dst); 140 Files.mkdirs(tmp_dst, "jigsaw temp directory"); 141 } 142 catch (IOException x) { 143 throw new Command.Exception(x); 144 } 145 } 146 147 148 class Jmod extends Command<SimpleLibrary> { 149 private String getModuleVersion(String modulename) 150 throws Command.Exception 151 { 152 Manifest mf = Manifest.create(modulename, classes); 153 ModuleInfo info = getModuleInfo(mf); 154 return info.id().version().toString(); 155 } 156 157 protected void go(SimpleLibrary lib) 158 throws Command.Exception 159 { 160 while (hasArg()) { 161 File outputfile = null; 162 try { 163 String modulename = takeArg(); 164 String version = getModuleVersion(modulename); 165 String outputfilename = modulename + '@'+ version + ".jmod"; 166 if (verbose) 167 System.out.println("Creating module file " 168 + outputfilename + " for " 169 + modulename); 170 outputfile = new File(destination, outputfilename); 171 ModuleFileWriter writer 172 = new ModuleFileWriter(outputfile, (fast || jigsawDevMode)); 173 writer.writeModule(classes, natlibs, natcmds, config_dir); 174 } 175 catch (IOException x) { 176 if (outputfile != null && !outputfile.delete()) { 177 Throwable t 178 = new IOException(outputfile + 179 ": Cannot delete").initCause(x); 180 throw new Command.Exception((IOException)t); 181 } 182 throw new Command.Exception(x); 183 } 184 } 185 finishArgs(); 186 } 187 } 188 189 static private ModuleInfo getModuleInfo(Manifest mf) 190 throws Command.Exception 191 { 192 try { 193 final String MINFO_CLASS = "module-info.class"; 194 File classdir = mf.classes().get(0); 195 File mif = new File(classdir , MINFO_CLASS); 196 if (!mif.exists()) 197 mif = new File(classdir, 198 mf.module() + File.separator + MINFO_CLASS); 199 byte[] bs = Files.load(mif); 200 return jms.parseModuleInfo(bs); 201 } catch (IOException x) { 202 throw new Command.Exception(x); 203 } 204 } 205 206 class Deb extends Command<SimpleLibrary> { 207 private File tmp_module_dst; 208 private File tmp_metadata_dst; 209 210 private void createMetaDataDir() 211 throws Command.Exception 212 { 213 tmp_metadata_dst = new File(tmp_dst, "DEBIAN"); 214 if (!tmp_metadata_dst.mkdirs()) 215 throw new Command.Exception("Couldn't create meta data directory " 216 + tmp_metadata_dst); 217 } 218 219 private void createModuleLibraryWorkDir() 220 throws Command.Exception 221 { 222 tmp_module_dst = new File(tmp_dst, library.toString()); 223 224 if (!tmp_module_dst.mkdirs()) 225 throw new Command.Exception("Couldn't create module destination directory " 226 + tmp_module_dst); 227 228 // Delete the modules dir to make SimpleLibrary happy, 229 // it wants to create the jigsaw metadata and the directory 230 // along with it. 231 if (!tmp_module_dst.delete()) 232 throw new Command.Exception("Can't delete " + tmp_module_dst); 233 } 234 235 private void preinstallModule(Manifest manifest) 236 throws Command.Exception 237 { 238 try { 239 240 createModuleLibraryWorkDir(); 241 Set<StorageOption> opts = Collections.emptySet(); 242 if (BOOT_MODULE.equals(manifest.module())) { 243 // Create a module library to the boot module package 244 SimpleLibrary.create(tmp_module_dst, opts).installFromManifests(Collections.singleton(manifest)); 245 } else { 246 // We need to create a throwaway SimpleLibrary to work with it, 247 // As there is no static preInstall method 248 File scratchlib_dst = new File(tmp_dst, "scratchlib"); 249 SimpleLibrary.create(scratchlib_dst, opts).preInstall(manifest, tmp_module_dst); 250 Files.deleteTree(scratchlib_dst); 251 } 252 } catch (IOException | ConfigurationException x) { 253 throw new Command.Exception(x); 254 } 255 } 256 257 private String translateVersion(String v) { 258 // Debian version format: [epoch:]upstream_version[-debian_revision] 259 // upstream_version may contain only alphanumerics and '.', '+', '-', '~' 260 // There is no epoch, ':' not allowed. 261 // 262 if (!v.matches("[A-Za-z0-9\\+-~\\.]+")) 263 throw new AssertionError("Invalid debian version format: " + v); 264 return v; 265 } 266 267 private String computeDependencies(ModuleInfo info) 268 { 269 StringBuilder deps = new StringBuilder(); 270 271 for (ViewDependence d : info.requiresModules()) { 272 if (d.modifiers().contains(ViewDependence.Modifier.OPTIONAL)) 273 continue; // skip optional dependency 274 275 deps.append(", ") 276 .append(d.query().name()) 277 .append(' ') 278 .append(d.query().versionQuery() != null ? 279 "(" + translateVersion(d.query().versionQuery().toString()) + ")" : 280 ""); 281 } 282 283 return deps.length() > 0 ? 284 deps.substring(2) : 285 ""; 286 } 287 288 private String computeProvides(ModuleInfo info) 289 { 290 StringBuilder deps = new StringBuilder(); 291 292 for (ModuleId id : info.defaultView().aliases()) 293 deps.append(", ") 294 .append(id.name()); 295 296 return deps.length() > 0 ? 297 deps.substring(2) : 298 ""; 299 } 300 301 /** Long descriptions in Debian control files must start the line with a space. */ 302 private String formatDescription(String description) 303 { 304 return " " + description.replace("\n", "\n "); 305 } 306 307 /** 308 * The ModuleInfo metadata gets translated into the package in the following way: 309 * 310 * package name is module's name 311 * package version is module's version 312 * package dependecies are module's required dependencies 313 * 314 * @param manifest The module's manifest 315 */ 316 private void writeMetaData(Manifest manifest) 317 throws Command.Exception 318 { 319 boolean bootmodule = BOOT_MODULE.equals(manifest.module()); 320 createMetaDataDir(); 321 ModuleInfo info = getModuleInfo(manifest); 322 323 // Create the control file, and fill in dependency and provides info 324 try (PrintStream control 325 = new PrintStream(new File(tmp_metadata_dst, "control"))) { 326 control.format("Package: %s%n" 327 + "Version: %s%n" 328 + "Section: misc%n" 329 + "Priority: optional%n" 330 + "Architecture: " 331 + System.getProperty("os.arch") + "%n", 332 info.id().name(), 333 translateVersion(info.id().version().toString())); 334 335 // If either maintainer name or e-mail is declared as parameter, use it 336 if (null != maintainer_name || null != maintainer_email) { 337 control.format("Maintainer: %s %s%n", 338 maintainer_name == null? default_maintainer_name : maintainer_name, 339 maintainer_email == null? default_maintainer_email : maintainer_email); 340 } 341 // Otherwise, if there is no extra metadata, use defaults 342 else if (null == extra_metadata) { 343 control.format("Maintainer: %s %s%n", 344 default_maintainer_name, 345 default_maintainer_email); 346 } 347 348 // If either short or long description is declared as parameter, use it 349 if (null != short_description || null != long_description) { 350 control.format("Description: %s%n" 351 + "%s%n", 352 short_description == null? default_short_description : short_description, 353 long_description == null? default_long_description : formatDescription(long_description)); 354 } 355 // Otherwise, if there is no extra metadata, use defaults 356 else if (null == extra_metadata) { 357 control.format("Description: %s%n" 358 + "%s%n", 359 default_short_description, 360 default_long_description); 361 } 362 363 if (!bootmodule && !info.requiresModules().isEmpty()) 364 control.format("Depends: %s\n", computeDependencies(info)); 365 if (!info.defaultView().aliases().isEmpty()) 366 control.format("Provides: %s\n", computeProvides(info)); 367 if (null != extra_metadata) 368 control.format("%s\n", new String(Files.load(extra_metadata))); 369 if (installedSize != null) 370 control.format("Installed-Size: %d\n", installedSize); 371 372 // All jpkg generated packages (except boot) depend on a boot 373 // package being installed at time of installation to be able to run jmod. 374 if (!bootmodule) 375 control.format("Pre-Depends: %s\n", BOOT_MODULE); 376 377 378 // Generate the launcher script, if a main class exists 379 if (!bootmodule && info.defaultView().mainClass() != null) { 380 // If no command name is given, use module name 381 if (null == bincmd) 382 bincmd = info.id().name(); 383 384 String BINDIR = "/usr/bin"; 385 File bin = new File(tmp_dst + BINDIR); 386 if (!bin.mkdirs()) 387 throw new IOException("Couldn't create " + tmp_dst + BINDIR); 388 389 File cmd = new File(bin, bincmd); 390 try (PrintStream launcher = new PrintStream(cmd)) { 391 String java_launcher = System.getProperty("java.home") 392 + "/bin/java"; 393 if (! (new File(java_launcher)).exists()) 394 throw new IOException("Couldn't find java launcher " 395 + "at " + java_launcher); 396 397 launcher.format("#!/bin/sh\n" + 398 "set -e\n" + 399 "exec %s -ea -L %s -m %s \"$@\"\n", 400 java_launcher, library, 401 info.id().name()); 402 } 403 cmd.setExecutable(true, false); 404 } 405 406 String installedJMod = javaHome.getPath() + "/bin/jmod"; 407 408 // Before a package is installed, 409 // check if the jigsaw module library needs to be created first 410 if (!bootmodule) { 411 File preinst = new File(tmp_metadata_dst, "preinst"); 412 try (PrintStream pis = new PrintStream(preinst)) { 413 pis.format("#!/bin/sh\n" + 414 "set -e\n" + 415 "if [ ! -f %1$s/%%jigsaw-library ]\n" + 416 "then\n" + 417 " %2$s -L %1$s create\n" + 418 "fi\n", 419 library, installedJMod); 420 } 421 preinst.setExecutable(true, false); 422 423 // After a package is installed, 424 // reconfigure the jigsaw module library for the module 425 File postinst = new File(tmp_metadata_dst, "postinst"); 426 try (PrintStream pis = new PrintStream(postinst)) { 427 pis.format("#!/bin/sh\n" + 428 "set -e\n" + 429 "if [ -f %s/%s.pack ] ; then\n" + 430 " for i in %s/*.pack ; do\n" + 431 " %s/bin/unpack200 -r $i %s/tmp.jar\n" + 432 " unzip -o -q -d / %s/tmp.jar\n" + 433 " rm %s/tmp.jar\n" + 434 " done\n" + 435 "fi\n" + 436 "%s -L %s config %s\n", 437 library, manifest.module(), 438 library, 439 javaHome, library, 440 library, 441 library, 442 installedJMod, library, info.id()); 443 } 444 postinst.setExecutable(true, false); 445 } 446 447 // Before a package is removed, 448 // remove the generated jigsaw module configuration 449 File prerm = new File(tmp_metadata_dst, "prerm"); 450 try (PrintStream pis = new PrintStream(prerm)) { 451 pis.format("#!/bin/sh\n" + 452 "set -e\n" + 453 // Delete unpacked class files. 454 "if [ -e %1$s/%2$s/%3$s ]\n" + 455 "then\n" + 456 " rm -rf %1$s/%2$s/%3$s\n" + 457 "fi\n" + 458 // Delete module library directory if it's empty. 459 "find %1$s/%2$s/ -maxdepth 0 -type d -empty -delete\n", 460 library, info.id().name(), info.id().version()); 461 } 462 prerm.setExecutable(true, false); 463 } catch (IOException x) { 464 throw new Command.Exception(x); 465 } 466 } 467 468 private void buildPackage() 469 throws Command.Exception 470 { 471 String dashz = "-z" + ((fast || jigsawDevMode) ? 1 : 9); 472 try { 473 Process build 474 = (new ProcessBuilder("fakeroot", "dpkg-deb", dashz, "-Zlzma", "--build", 475 tmp_dst.toString(), destination.toString())).start(); 476 477 try (BufferedReader br = new BufferedReader( 478 new InputStreamReader(build.getErrorStream()))) { 479 480 if (0 != build.waitFor()) 481 throw new Command.Exception("Failed to create package " 482 + br.readLine()); 483 } 484 } catch (IOException | InterruptedException x) { 485 throw new Command.Exception(x); 486 } 487 } 488 489 private void cleanup() 490 throws Command.Exception 491 { 492 try { 493 Files.deleteTree(tmp_dst); 494 } catch (IOException x) { 495 throw new Command.Exception(x); 496 } 497 } 498 499 private File getPackFile(Manifest manifest) 500 { 501 return new File(tmp_module_dst, manifest.module() + ".pack"); 502 } 503 504 private void packModule(Manifest manifest) 505 throws Command.Exception 506 { 507 // Can't pack the boot module since unpack200 tool may not exist 508 if (BOOT_MODULE.equals(manifest.module())) 509 return; 510 511 try { 512 File tmp_jar = new File(tmp_dst, "module.jar"); 513 514 // Create temporary jar file with module classes and ressources 515 // Store entries, as we'll just need it for pack200 to read from 516 // and then delete it. No point in wasting time. 517 Process jar 518 = (new ProcessBuilder("jar", "c0f", tmp_jar.toString(), 519 "-C", tmp_dst.toString(), 520 library.toString())).start(); 521 try (BufferedReader br = new BufferedReader( 522 new InputStreamReader(jar.getErrorStream()))) { 523 if (0 != jar.waitFor()) 524 throw new Command.Exception("Failed to jar module " 525 + br.readLine()); 526 } 527 528 // Remove redundant META-INF directory from jar file, 529 // so that it doesn't pollute the filesystem hierarchy 530 // when we unpack the files again. 531 Process zip 532 = (new ProcessBuilder("zip", tmp_jar.toString(), 533 "-d", "META-INF/*")).start(); 534 try (BufferedReader br = new BufferedReader( 535 new InputStreamReader(zip.getErrorStream()))) { 536 if (0 != zip.waitFor()) 537 throw new Command.Exception("Failed to remove META-INF " 538 + "directory from jar " 539 + "module " 540 + br.readLine()); 541 } 542 // Compress the jar file with pack200. 543 String dashE = "-E" + ((fast || jigsawDevMode) ? "0" : "9"); 544 Process pack200 545 = (new ProcessBuilder("pack200", dashE, "-S-1", "--no-gzip", 546 getPackFile(manifest).toString(), 547 tmp_jar.toString())).start(); 548 try (BufferedReader br = new BufferedReader( 549 new InputStreamReader(pack200.getErrorStream()))) { 550 if (0 != pack200.waitFor()) 551 throw new Command.Exception("Failed to pack200 module " 552 + br.readLine()); 553 } 554 555 if (! tmp_jar.delete()) 556 throw new Command.Exception("Failed to delete temporary file " + tmp_jar); 557 Files.deleteTree(new File(tmp_module_dst, manifest.module().toString())); 558 } catch (IOException | InterruptedException x) { 559 throw new Command.Exception(x); 560 } 561 } 562 563 protected void go(SimpleLibrary lib) 564 throws Command.Exception 565 { 566 List<Manifest> mfs = new ArrayList<>(); 567 while (hasArg()) { 568 mfs.add(Manifest.create(takeArg(), classes)); 569 } 570 finishArgs(); 571 572 for (Manifest manifest : mfs) { 573 574 if (verbose) 575 System.out.println("Creating binary Debian package for " + manifest.module()); 576 577 createTempWorkDir(); 578 if (null != includes) { 579 try { 580 Files.copyTree(includes, tmp_dst); 581 } catch (IOException x) { 582 throw new Command.Exception(x); 583 } 584 } 585 preinstallModule(manifest); 586 packModule(manifest); 587 writeMetaData(manifest); 588 buildPackage(); 589 cleanup(); 590 } 591 } 592 } 593 594 private static Map<String,Class<? extends Command<SimpleLibrary>>> commands 595 = new HashMap<>(); 596 597 static { 598 commands.put("deb", Deb.class); 599 commands.put("jmod", Jmod.class); 600 } 601 602 private OptionParser parser; 603 604 private static OptionSpec<File> resourcePath; // ## 605 606 private void usage() { 607 out.format("%n"); 608 out.format("usage: jpkg [-v] [-L <library>] [-r <resource-dir>] [-i <include-dir>] [-m <module-dir>] [-d <output-dir>] [-c <command>] [-n <name>] [-e <e-mail@address>] [-s <short description>] [-l <long description>] [-x <extra metadata>] [deb|jmod] <module-name>%n"); 609 out.format("%n"); 610 try { 611 parser.printHelpOn(out); 612 } catch (IOException x) { 613 throw new AssertionError(x); 614 } 615 out.format("%n"); 616 } 617 618 public static void run(String[] args) 619 throws OptionException, Command.Exception { 620 621 new Packager().exec(args); 622 } 623 624 private void exec(String[] args) 625 throws OptionException, Command.Exception { 626 627 parser = new OptionParser(); 628 629 OptionSpec<File> libPath 630 = (parser.acceptsAll(Arrays.asList("L", "library"), 631 "Module-library location" 632 + " (default $JAVA_MODULES)") 633 .withRequiredArg() 634 .describedAs("path") 635 .ofType(File.class)); 636 637 OptionSpec<File> launcherPath 638 = (parser.acceptsAll(Arrays.asList("c", "command"), 639 "Launcher command name") 640 .withRequiredArg() 641 .describedAs("path") 642 .ofType(File.class)); 643 644 parser.acceptsAll(Arrays.asList("v", "verbose"), 645 "Enable verbose output"); 646 parser.acceptsAll(Arrays.asList("h", "?", "help"), 647 "Show this help message"); 648 OptionSpec<File> destinationPath 649 = (parser.acceptsAll(Arrays.asList("d", "dest-dir"), 650 "Destination directory for packages") 651 .withRequiredArg() 652 .describedAs("path") 653 .ofType(File.class)); 654 655 OptionSpec<File> modulePath 656 = (parser.acceptsAll(Arrays.asList("m", "module-dir"), 657 "Source directory for modules") 658 .withRequiredArg() 659 .describedAs("path") 660 .ofType(File.class)); 661 662 OptionSpec<File> includePath 663 = (parser.acceptsAll(Arrays.asList("i", "include"), 664 "Directory of files to be included") 665 .withRequiredArg() 666 .describedAs("path") 667 .ofType(File.class)); 668 669 OptionSpec<String> maintainerName 670 = (parser.acceptsAll(Arrays.asList("n", "name"), 671 "Package maintainer's name") 672 .withRequiredArg() 673 .describedAs("name") 674 .ofType(String.class)); 675 676 OptionSpec<String> maintainerEmail 677 = (parser.acceptsAll(Arrays.asList("e", "email"), 678 "Package maintainer's e-mail address") 679 .withRequiredArg() 680 .describedAs("e-mail@address") 681 .ofType(String.class)); 682 683 OptionSpec<String> shortDescription 684 = (parser.acceptsAll(Arrays.asList("s", "short"), 685 "Short description of the package") 686 .withRequiredArg() 687 .describedAs("description") 688 .ofType(String.class)); 689 690 OptionSpec<String> longDescription 691 = (parser.acceptsAll(Arrays.asList("l", "long"), 692 "Long description of the package") 693 .withRequiredArg() 694 .describedAs("description") 695 .ofType(String.class)); 696 697 parser.acceptsAll(Arrays.asList("fast"), 698 "Use fastest compression"); 699 700 OptionSpec<Integer> isize 701 = (parser.acceptsAll(Arrays.asList("installed-size"), 702 "Installed size in kilobytes") 703 .withRequiredArg() 704 .describedAs("size") 705 .ofType(Integer.class)); 706 707 OptionSpec<File> javaHomePath 708 = (parser.acceptsAll(Arrays.asList("java-home"), 709 "Alternate $JAVA_HOME location") 710 .withRequiredArg() 711 .describedAs("dir") 712 .ofType(File.class)); 713 714 OptionSpec<File> extraMetadata 715 = (parser.acceptsAll(Arrays.asList("x", "extra"), 716 "File or directory with additional metadata," 717 + " depending on the packaging system") 718 .withRequiredArg() 719 .describedAs("dir") 720 .ofType(File.class)); 721 722 OptionSpec<File> nativeLibs 723 = (parser.accepts("natlib", "Directory with native libs") 724 .withRequiredArg() 725 .describedAs("dir") 726 .ofType(File.class)); 727 728 OptionSpec<File> nativeCmds 729 = (parser.accepts("natcmd", "Directory with native launchers") 730 .withRequiredArg() 731 .describedAs("dir") 732 .ofType(File.class)); 733 734 OptionSpec<File> config 735 = (parser.accepts("config", "Directory with configuration") 736 .withRequiredArg() 737 .describedAs("dir") 738 .ofType(File.class)); 739 740 if (args.length == 0) { 741 usage(); 742 return; 743 } 744 745 OptionSet opts = parser.parse(args); 746 if (opts.has("h")) { 747 usage(); 748 return; 749 } 750 if (opts.has("v")) 751 verbose = true; 752 if (opts.has("fast")) 753 fast = true; 754 List<String> words = opts.nonOptionArguments(); 755 if (words.isEmpty()) { 756 usage(); 757 return; 758 } 759 String verb = words.get(0); 760 Class<? extends Command<SimpleLibrary>> cmd = commands.get(verb); 761 if (cmd == null) 762 throw new Command.Exception("%s: unknown command", verb); 763 if (opts.has(launcherPath)) 764 bincmd = opts.valueOf(launcherPath).toString(); 765 if (opts.has(destinationPath)) 766 destination = opts.valueOf(destinationPath); 767 if (opts.has(modulePath)) { 768 classes = opts.valueOf(modulePath); 769 checkPathArgument(classes, "Module"); 770 } 771 if (opts.has(libPath)) { 772 library = opts.valueOf(libPath); 773 } else { 774 String jm = System.getenv("JAVA_MODULES"); 775 if (jm != null) 776 library = new File(jm); 777 } 778 if (opts.has(includePath)) 779 includes = opts.valueOf(includePath); 780 if (opts.has(javaHomePath)) 781 javaHome = opts.valueOf(javaHomePath); 782 if (opts.has(maintainerName)) 783 maintainer_name = opts.valueOf(maintainerName); 784 if (opts.has(maintainerEmail)) { 785 maintainer_email = opts.valueOf(maintainerEmail); 786 // Add missing e-mail quotes if necessary 787 maintainer_email 788 = (maintainer_email.startsWith("<") ? "" : "<") 789 + maintainer_email 790 + (maintainer_email.endsWith(">") ? "" : ">") ; 791 } 792 if (opts.has(shortDescription)) 793 short_description = opts.valueOf(shortDescription); 794 if (opts.has(longDescription)) 795 long_description = opts.valueOf(longDescription); 796 if (opts.has(extraMetadata)) 797 extra_metadata = opts.valueOf(extraMetadata); 798 if (opts.has(nativeLibs)) { 799 natlibs = opts.valueOf(nativeLibs); 800 checkPathArgument(natlibs, "Native library"); 801 } 802 if (opts.has(nativeCmds)) { 803 natcmds = opts.valueOf(nativeCmds); 804 checkPathArgument(natcmds, "Native command"); 805 } 806 if (opts.has(config)) { 807 config_dir = opts.valueOf(config); 808 checkPathArgument(config_dir, "Config"); 809 } 810 if (opts.has(isize)) 811 installedSize = opts.valueOf(isize); 812 813 if (cmd == Deb.class) 814 (new Deb()).run(null, opts); 815 else if (cmd == Jmod.class) 816 (new Jmod()).run(null, opts); 817 } 818 819 /** 820 * Helper method to check if a path exists before using it further. 821 * 822 * @param path to check 823 * @param type of path being checked 824 * 825 * @throws Command.Exception if path doesn't exist 826 */ 827 private static final void checkIfPathExists(File path, String type) 828 throws Command.Exception { 829 830 if (!path.exists()) 831 throw new Command.Exception("%s path doesn't exist: %s", 832 type, path); 833 } 834 835 /** 836 * Helper method to check if a path is readable before using it further. 837 * 838 * @param path to check 839 * @param type of path being checked 840 * 841 * @throws Command.Exception if path isn't readable 842 */ 843 private static final void checkIfPathIsReadable(File path, String type) 844 throws Command.Exception { 845 846 if (!path.canRead()) 847 throw new Command.Exception("%s path isn't readable: %s", 848 type, path); 849 } 850 851 /** 852 * Helper method to check if a path is a directory before using it further. 853 * 854 * @param path to check 855 * @param type of path being checked 856 * 857 * @throws Command.Exception if path is not a directory 858 */ 859 private static final void checkIfPathIsDirectory(File path, String type) 860 throws Command.Exception { 861 862 if (!path.isDirectory()) 863 throw new Command.Exception("%s path is not a directory: %s", 864 type, path); 865 } 866 867 /** 868 * Helper method to check if a path argument is valid. 869 * 870 * @param path to check 871 * @param type of path being checked 872 * 873 * @throws Command.Exception if path is not a directory 874 */ 875 private static final void checkPathArgument(File path, String type) 876 throws Command.Exception { 877 878 checkIfPathExists(path, type); 879 checkIfPathIsReadable(path, type); 880 checkIfPathIsDirectory(path, type); 881 } 882 883 private Packager() { } 884 885 public static void main(String[] args) throws Exception { 886 try { 887 run(args); 888 } catch (OptionException | Command.Exception x) { 889 err.println(x.getMessage()); 890 System.exit(1); 891 } 892 } 893 894 } --- EOF ---