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