changeset 14169:521434b30dce

Add option that prints module descriptor to jmod, and various options cleanup
author chegar
date Thu, 01 Oct 2015 14:42:10 +0100
parents 2b6cb5de27d5
children 7187c680d9d0
files src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties test/jdk/jigsaw/launcher/basic/BasicTest.java test/jdk/jigsaw/module/ModuleReader/ModuleReaderTest.java test/jdk/jigsaw/tools/jmod/JmodNegativeTest.java test/jdk/jigsaw/tools/jmod/JmodTest.java
diffstat 6 files changed, 280 insertions(+), 92 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Thu Oct 01 14:40:45 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Thu Oct 01 14:42:10 2015 +0100
@@ -25,9 +25,11 @@
 
 package jdk.tools.jmod;
 
+import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -40,6 +42,7 @@
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Version;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.URI;
 import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
@@ -76,6 +79,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
 import jdk.internal.joptsimple.BuiltinHelpFormatter;
@@ -92,6 +96,8 @@
 import jdk.internal.module.Hasher.DependencyHashes;
 import jdk.internal.module.ModuleInfoExtender;
 
+import static java.util.stream.Collectors.joining;
+
 /**
  * Implementation for the jmod tool.
  */
@@ -150,13 +156,14 @@
                      EXIT_SYSERR = 3, // System error or resource exhaustion.
                      EXIT_ABNORMAL = 4;// terminated abnormally
 
-    enum Task {
+    enum Mode {
         CREATE,
-        LIST
+        LIST,
+        PRINT_DESCRIPTOR
     };
 
     static class Options {
-        Task task;
+        Mode mode;
         Path jmodFile;
         boolean help;
         boolean version;
@@ -189,15 +196,18 @@
             }
 
             boolean ok;
-            switch (options.task) {
+            switch (options.mode) {
                 case CREATE:
                     ok = create();
                     break;
                 case LIST:
                     ok = list();
                     break;
+                case PRINT_DESCRIPTOR:
+                    ok = printModuleDescriptor();
+                    break;
                 default:
-                    throw new AssertionError("Unknown task: " + options.task.name());
+                    throw new AssertionError("Unknown mode: " + options.mode.name());
             }
 
             return ok ? EXIT_OK : EXIT_ERROR;
@@ -274,6 +284,114 @@
         return modPaths;
     }
 
+    private boolean printModuleDescriptor() throws IOException {
+        ZipFile zip = null;
+        try {
+            try {
+                zip = new ZipFile(options.jmodFile.toFile());
+            } catch (IOException x) {
+                throw new IOException("error opening jmod file", x);
+            }
+
+            try (FileInputStream fis = new FileInputStream(options.jmodFile.toFile())) {
+                boolean found = printModuleDescriptor(fis);
+                if (!found)
+                    throw new CommandException("err.module.descriptor.not.found");
+                return found;
+            }
+        } finally {
+            if (zip != null)
+                zip.close();
+        }
+    }
+
+    static <T> String toString(Set<T> set,
+                               CharSequence prefix,
+                               CharSequence suffix ) {
+        if (set.isEmpty()) { return ""; }
+        return set.stream().map(e -> e.toString())
+                  .collect(joining(", ", prefix, suffix));
+    }
+
+    private boolean printModuleDescriptor(FileInputStream fis)
+        throws IOException
+    {
+        final String mi = Section.CLASSES.jmodDir() + "/" + MODULE_INFO;
+        try (BufferedInputStream bis = new BufferedInputStream(fis);
+             ZipInputStream zis = new ZipInputStream(bis)) {
+
+            ZipEntry e;
+            while ((e = zis.getNextEntry()) != null) {
+                if (e.getName().equals(mi)) {
+                    ModuleDescriptor md = ModuleDescriptor.read(zis);
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("\nName:\n  " + md.toNameAndVersion());
+
+                    Set<Requires> requires = md.requires();
+                    if (!requires.isEmpty()) {
+                        sb.append("\nRequires:");
+                        requires.forEach(r ->
+                                sb.append("\n  ").append(r.name())
+                                  .append(toString(r.modifiers(), " [ ", " ]")));
+                    }
+
+                    Set<String> s = md.uses();
+                    if (!s.isEmpty()) {
+                        sb.append("\nUses: ");
+                        s.forEach(sv -> sb.append("\n  ").append(sv));
+                    }
+
+                    Set<ModuleDescriptor.Exports> exports = md.exports();
+                    if (!exports.isEmpty()) {
+                        sb.append("\nExports:");
+                        exports.forEach(sv -> sb.append("\n  ").append(sv));
+                    }
+
+                    Map<String, ModuleDescriptor.Provides> provides = md.provides();
+                    if (!provides.isEmpty()) {
+                        sb.append("\nProvides: ");
+                        provides.values().forEach(p ->
+                                sb.append("\n  ").append(p.service())
+                                  .append(" with ")
+                                  .append(toString(p.providers(), "", "")));
+                    }
+
+                    Optional<String> mc = md.mainClass();
+                    if (mc.isPresent())
+                        sb.append("\nMain class:\n  " + mc.get());
+
+                    s = md.conceals();
+                    if (!s.isEmpty()) {
+                        sb.append("\nConceals:");
+                        s.forEach(p -> sb.append("\n  ").append(p));
+                    }
+
+                    try {
+                        Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
+                        m.setAccessible(true);
+                        @SuppressWarnings("unchecked")
+                        Optional<Hasher.DependencyHashes> optHashes =
+                                (Optional<Hasher.DependencyHashes>) m.invoke(md);
+
+                        if (optHashes.isPresent()) {
+                            Hasher.DependencyHashes hashes = optHashes.get();
+                            sb.append("\nHashes:");
+                            sb.append("\n  Algorithm: " + hashes.algorithm());
+                            hashes.names().stream().forEach(mod ->
+                                    sb.append("\n  ").append(mod)
+                                      .append(": ").append(hashes.hashFor(mod)));
+                        }
+                    } catch (ReflectiveOperationException x) {
+                        throw new InternalError(x);
+                    }
+                    out.print(sb.toString());
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private boolean create() throws IOException {
         JmodFileWriter jmod = new JmodFileWriter();
 
@@ -768,16 +886,32 @@
             String content = super.format(all);
             String[] lines = content.split("\n");
             StringBuilder builder = new StringBuilder();
+
+            String modes = ".*--create.*|.*--list.*|.*--print-module-descriptor.*";
+            boolean first = true;
+            for (String line : lines) {
+                if (line.matches(modes)) {
+                    if (first) {
+                        builder.append("\n").append(" Main operation modes:\n");
+                        first = false;
+                    }
+                    builder.append("  ").append(line).append("\n");
+                }
+            }
+            builder.append("\n");
+
             String cmdfile = null;
             for (String line : lines) {
                 if (line.startsWith("--@")) {
                     cmdfile = line.replace("--" + CMD_FILENAME, CMD_FILENAME + "  ");
-                } else {
-                    builder.append(line).append("\n");
+                } else if (line.startsWith("Option") || line.startsWith("------")) {
+                    builder.append(" ").append(line).append("\n");
+                } else if (!line.matches(modes)){
+                    builder.append("  ").append(line).append("\n");
                 }
             }
             if (cmdfile != null) {
-                builder.append(cmdfile).append("\n");
+                builder.append("  ").append(cmdfile).append("\n");
             }
             return builder.toString();
         }
@@ -788,6 +922,21 @@
     private void handleOptions(String[] args) {
         parser.formatHelpWith(new JmodHelpFormatter());
 
+        // Main operation modes
+        OptionSpec<Void> create
+                = parser.acceptsAll(Arrays.asList("c", "create"),
+                                    getMessage("main.opt.mode.create"));
+
+        OptionSpec<Void> list
+                = parser.acceptsAll(Arrays.asList("t", "list"),
+                                    getMessage("main.opt.mode.list"));
+
+        OptionSpec<Void> printDescriptor
+                = parser.acceptsAll(Arrays.asList("p", "print-module-descriptor"),
+                                    getMessage("main.opt.mode.pmd"));
+
+        // options
+
         OptionSpec<Path> classPath
                 = parser.accepts("class-path", getMessage("main.opt.class-path"))
                         .withRequiredArg()
@@ -847,17 +996,38 @@
                 = parser.accepts("version", getMessage("main.opt.version"));
 
         NonOptionArgumentSpec<String> nonOptions
-                = parser.nonOptions(getMessage("main.non.opt.args"));
+                = parser.nonOptions();
 
         try {
             OptionSet opts = parser.parse(args);
 
-            if (opts.specs().isEmpty() && opts.valuesOf(nonOptions).isEmpty())
-                return;  // usage summary will be shown
+            if (opts.has(help) || opts.has(version)) {
+                options = new Options();
+                options.help = opts.has(help);
+                options.version = opts.has(version);
+                return;  // informational message will be shown
+            }
+
+            if (opts.specs().isEmpty() ||
+                !(opts.has(create) || opts.has(list) || opts.has(printDescriptor)))
+                throw new CommandException("err.bad.main.mode").showUsage(true);
 
             options = new Options();
-            options.help = opts.has(help);
-            options.version = opts.has(version);
+
+            if (opts.has(create)) {
+                if (opts.has(list) || opts.has(printDescriptor))
+                    throw new CommandException("err.multiple.main.modes").showUsage(true);
+                options.mode = Mode.CREATE;
+            } else if (opts.has(list)) {
+                if (opts.has(create) || opts.has(printDescriptor))
+                    throw new CommandException("err.multiple.main.modes").showUsage(true);
+                options.mode = Mode.LIST;
+            } else if (opts.has(printDescriptor)) {
+                if (opts.has(create) || opts.has(list))
+                    throw new CommandException("err.multiple.main.modes").showUsage(true);
+                options.mode = Mode.PRINT_DESCRIPTOR;
+            }
+
             if (opts.has(classPath))
                 options.classpath = opts.valuesOf(classPath);
             if (opts.has(cmds))
@@ -885,34 +1055,23 @@
                     throw new CommandException("err.modulepath.must.be.specified").showUsage(true);
             }
 
-            if (options.help || options.version)
-                return;  // informational message will be shown
-
             List<String> words = opts.valuesOf(nonOptions);
             if (words.isEmpty())
-                throw new CommandException("err.missing.task").showUsage(true);
-
-            String verb = words.get(0);
-            try {
-                options.task = Enum.valueOf(Task.class, verb.toUpperCase());
-            } catch (IllegalArgumentException e) {
-                throw new CommandException("err.invalid.task", verb).showUsage(true);
-            }
-
-            if (words.size() == 1)
                 throw new CommandException("err.jmod.must.be.specified").showUsage(true);
-            Path path = Paths.get(words.get(1));
-            if (options.task.equals(Task.CREATE) && Files.exists(path))
+            Path path = Paths.get(words.get(0));
+            if (options.mode.equals(Mode.CREATE) && Files.exists(path))
                 throw new CommandException("err.file.already.exists", path);
-            else if (options.task.equals(Task.LIST) && Files.notExists(path))
+            else if ((options.mode.equals(Mode.LIST) ||
+                          options.mode.equals(Mode.PRINT_DESCRIPTOR))
+                      && Files.notExists(path))
                 throw new CommandException("err.jmod.not.found", path);
             options.jmodFile = path;
 
-            if (words.size() > 2)
+            if (words.size() > 1)
                 throw new CommandException("err.unknown.option",
-                        words.subList(2, words.size())).showUsage(true);
+                        words.subList(1, words.size())).showUsage(true);
 
-            if (options.task.equals(Task.CREATE) && options.classpath == null)
+            if (options.mode.equals(Mode.CREATE) && options.classpath == null)
                 throw new CommandException("err.classpath.must.be.specified").showUsage(true);
             if (options.mainClass != null && !isValidJavaIdentifier(options.mainClass))
                 throw new CommandException("err.invalid.main-class", options.mainClass);
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties	Thu Oct 01 14:40:45 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties	Thu Oct 01 14:42:10 2015 +0100
@@ -1,17 +1,16 @@
 main.usage.summary=\
-Usage: {0} <create|list> <options> jmod-file\n\
+Usage: {0} [OPTIONS] jmod-file\n\
 use --help for a list of possible options
 
 main.usage=\
-Usage: {0} <create|list> <options> jmod-file
+Usage: {0} [OPTIONS] jmod-file
 
 error.prefix=Error:
 warn.prefix=Warning:
 
-main.non.opt.args=\
-\create    - Creates a new jmod archive\n\
-\list      - Prints the names of all the entries in the jmod\n\
-\jmod-file - The jmod archive to operate on
+main.opt.mode.create=Creates a new jmod archive
+main.opt.mode.list=Prints the names of all the entries
+main.opt.mode.pmd=Prints the module descriptor
 
 main.opt.help=Print this usage message
 main.opt.version=Version information
@@ -27,10 +26,10 @@
 main.opt.hash-dependencies=Compute and record hashes of dependencies matched by the pattern
 main.opt.cmdfile=Read options from the specified file
 
+err.bad.main.mode=One of options -ctp must be specified.
+err.multiple.main.modes=You may not specify more than one of '-ctp' option
 err.classpath.must.be.specified=--class-path must be specified
 err.jmod.must.be.specified=jmod-file must be specified
-err.missing.task=one of create or list must be specified
-err.invalid.task=task must be create|list: {0}
 err.invalid.version=invalid module version {0}
 err.output.must.be.specified:--output must be specified
 err.mods.must.be.specified:--mods must be specified
@@ -50,4 +49,5 @@
 err.internal.error=internal error: {0} {1} {2}
 err.invalid.arg.for.option=invalid argument for option: {0}
 err.option.after.class=option must be specified before classes: {0}
+err.module.descriptor.not.found=Module descriptor not found
 warn.invalid.arg=Invalid classname or pathname not exist: {0}
--- a/test/jdk/jigsaw/launcher/basic/BasicTest.java	Thu Oct 01 14:40:45 2015 +0100
+++ b/test/jdk/jigsaw/launcher/basic/BasicTest.java	Thu Oct 01 14:42:10 2015 +0100
@@ -133,7 +133,7 @@
         String cp = MODS_DIR.resolve(TEST_MODULE).toString();
         String jmod = dir.resolve("m.jmod").toString();
         String[] args = {
-            "create",
+            "--create",
             "--class-path", cp,
             "--main-class", MAIN_CLASS,
             jmod
--- a/test/jdk/jigsaw/module/ModuleReader/ModuleReaderTest.java	Thu Oct 01 14:40:45 2015 +0100
+++ b/test/jdk/jigsaw/module/ModuleReader/ModuleReaderTest.java	Thu Oct 01 14:42:10 2015 +0100
@@ -115,10 +115,10 @@
     public void testJMod() throws Exception {
         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
 
-        // jmod create --class-path mods/${TESTMODULE}  mlib/${TESTMODULE}.jmod
+        // jmod --create --class-path mods/${TESTMODULE}  mlib/${TESTMODULE}.jmod
         String cp = MODS_DIR.resolve(TEST_MODULE).toString();
         String jmod = dir.resolve("m.jmod").toString();
-        String[] args = { "create", "--class-path", cp, jmod };
+        String[] args = { "--create", "--class-path", cp, jmod };
         jdk.tools.jmod.JmodTask task = new jdk.tools.jmod.JmodTask();
         assertEquals(task.run(args), 0);
 
--- a/test/jdk/jigsaw/tools/jmod/JmodNegativeTest.java	Thu Oct 01 14:40:45 2015 +0100
+++ b/test/jdk/jigsaw/tools/jmod/JmodNegativeTest.java	Thu Oct 01 14:42:10 2015 +0100
@@ -75,7 +75,7 @@
         jmod("")
             .assertFailure()
             .resultChecker(r ->
-                assertContains(r.output, "Error: task must be create|list:")
+                assertContains(r.output, "Error: One of options -ctp must be specified.")
             );
     }
 
@@ -84,7 +84,7 @@
         jmod("badAction")
             .assertFailure()
             .resultChecker(r ->
-                assertContains(r.output, "Error: task must be create|list:")
+                assertContains(r.output, "Error: One of options -ctp must be specified.")
             );
 
         jmod("--badOption")
@@ -99,7 +99,7 @@
         Path jmod = MODS_DIR.resolve("doesNotExist.jmod");
         FileUtils.deleteFileIfExistsWithRetry(jmod);
 
-        jmod("create",
+        jmod("--create",
              jmod.toString(),
              "AAA")
             .assertFailure()
@@ -110,7 +110,7 @@
 
     @Test
     public void testCreateNoArgs() {
-        jmod("create")
+        jmod("--create")
             .assertFailure()
             .resultChecker(r ->
                 assertContains(r.output, "Error: jmod-file must be specified")
@@ -119,7 +119,7 @@
 
     @Test
     public void testListNoArgs() {
-        jmod("list")
+        jmod("--list")
             .assertFailure()
             .resultChecker(r ->
                 assertContains(r.output, "Error: jmod-file must be specified")
@@ -131,7 +131,7 @@
         Path jmod = MODS_DIR.resolve("doesNotExist.jmod");
         FileUtils.deleteFileIfExistsWithRetry(jmod);
 
-        jmod("list",
+        jmod("--list",
              jmod.toString())
             .assertFailure()
             .resultChecker(r ->
@@ -146,7 +146,7 @@
         if (Files.notExists(jmod))
             Files.createDirectory(jmod);
 
-        jmod("list",
+        jmod("--list",
              jmod.toString())
             .assertFailure()
             .resultChecker(r ->
@@ -160,7 +160,7 @@
         if (Files.notExists(jmod))
             Files.createFile(jmod);
 
-        jmod("list",
+        jmod("--list",
              jmod.toString())
             .assertFailure()
             .resultChecker(r ->
@@ -170,7 +170,7 @@
 
     @Test
     public void testHashDependenciesModulePathNotSpecified() {
-        jmod("create",
+        jmod("--create",
              "--hash-dependencies", "anyPattern.*",
              "output.jmod")
             .assertFailure()
@@ -186,7 +186,7 @@
         if (Files.notExists(jmod))
             Files.createFile(jmod);
 
-        jmod("create",
+        jmod("--create",
              "--class-path", Paths.get(".").toString(), // anything that exists
              jmod.toString())
             .assertFailure()
@@ -201,7 +201,7 @@
         if (Files.notExists(jmod))
             Files.createDirectory(jmod);
 
-        jmod("create",
+        jmod("--create",
              "--class-path", Paths.get(".").toString(), // anything that exists
              jmod.toString())
             .assertFailure()
@@ -217,7 +217,7 @@
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
         for (String version : new String[] { "", "NOT_A_VALID_VERSION" }) {
-            jmod("create",
+            jmod("--create",
                  "--class-path", cp,
                  "--module-version", version,
                  jmod.toString())
@@ -234,7 +234,7 @@
         FileUtils.deleteFileIfExistsWithRetry(jmod);
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
-        jmod("create",
+        jmod("--create",
               "--class-path", cp + pathSeparator + cp,
               jmod.toString())
             .assertFailure()
@@ -251,7 +251,7 @@
         FileUtils.deleteFileIfExistsWithRetry(jar);
         Files.createFile(jar);
 
-        jmod("create",
+        jmod("--create",
              "--class-path", jar.toString(),
              jmod.toString())
             .assertFailure()
@@ -271,7 +271,7 @@
             // empty
         }
 
-        jmod("create",
+        jmod("--create",
              "--class-path", jar.toString(),
              jmod.toString())
             .assertFailure()
@@ -288,7 +288,7 @@
         FileUtils.deleteFileIfExistsWithRetry(jar);
         Files.createDirectory(jar);
 
-        jmod("create",
+        jmod("--create",
              "--class-path", jar.toString(),
              jmod.toString())
             .assertFailure()
@@ -306,7 +306,7 @@
         Files.createDirectory(cp);
         Files.createFile(cp.resolve("nada.txt"));
 
-        jmod("create",
+        jmod("--create",
              "--class-path", cp.toString(),
              jmod.toString())
             .assertFailure()
@@ -325,7 +325,7 @@
         Files.createDirectory(emptyDir);
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
-        jmod("create",
+        jmod("--create",
              "--class-path", cp,
              "--hash-dependencies", ".*",
              "--modulepath", emptyDir.toString(),
@@ -347,7 +347,7 @@
         try {
             String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
-            jmod("create",
+            jmod("--create",
                  "--class-path", cp,
                  "--hash-dependencies", ".*",
                  "--modulepath", MODS_DIR.toString(),
@@ -369,7 +369,7 @@
         FileUtils.deleteFileIfExistsWithRetry(file);
         Files.createFile(file);
 
-        jmod("create",
+        jmod("--create",
              "--hash-dependencies", ".*",
              "--modulepath", file.toString(),
              jmod.toString())
@@ -386,23 +386,23 @@
         FileUtils.deleteFileIfExistsWithRetry(Paths.get("doesNotExist"));
 
         List<Supplier<JmodResult>> tasks = Arrays.asList(
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--hash-dependencies", "anyPattern",
                            "--modulepath", "doesNotExist",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--class-path", "doesNotExist",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--class-path", "doesNotExist.jar",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--cmds", "doesNotExist",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--config", "doesNotExist",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--libs", "doesNotExist",
                            "output.jmod") );
 
@@ -434,23 +434,23 @@
         Files.createDirectory(emptyDir);
 
         List<Supplier<JmodResult>> tasks = Arrays.asList(
-            () -> jmod("create",
+            () -> jmod("--create",
                        "--hash-dependencies", "anyPattern",
                        "--modulepath","empty" + pathSeparator + "doesNotExist",
                        "output.jmod"),
-            () -> jmod("create",
+            () -> jmod("--create",
                        "--class-path", "empty" + pathSeparator + "doesNotExist",
                        "output.jmod"),
-            () -> jmod("create",
+            () -> jmod("--create",
                        "--class-path", "empty" + pathSeparator + "doesNotExist.jar",
                        "output.jmod"),
-            () -> jmod("create",
+            () -> jmod("--create",
                        "--cmds", "empty" + pathSeparator + "doesNotExist",
                        "output.jmod"),
-            () -> jmod("create",
+            () -> jmod("--create",
                        "--config", "empty" + pathSeparator + "doesNotExist",
                        "output.jmod"),
-            () -> jmod("create",
+            () -> jmod("--create",
                        "--libs", "empty" + pathSeparator + "doesNotExist",
                        "output.jmod") );
 
@@ -481,19 +481,19 @@
             Files.createFile(aFile);
 
         List<Supplier<JmodResult>> tasks = Arrays.asList(
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--class-path", "aFile.txt",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--modulepath", "aFile.txt",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--cmds", "aFile.txt",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--config", "aFile.txt",
                            "output.jmod"),
-                () -> jmod("create",
+                () -> jmod("--create",
                            "--libs", "aFile.txt",
                            "output.jmod") );
 
--- a/test/jdk/jigsaw/tools/jmod/JmodTest.java	Thu Oct 01 14:40:45 2015 +0100
+++ b/test/jdk/jigsaw/tools/jmod/JmodTest.java	Thu Oct 01 14:42:10 2015 +0100
@@ -36,6 +36,7 @@
 import java.nio.file.*;
 import java.util.*;
 import java.util.function.Consumer;
+import java.util.regex.Pattern;
 import java.util.stream.Stream;
 import jdk.testlibrary.FileUtils;
 import org.testng.annotations.BeforeTest;
@@ -78,12 +79,12 @@
     @Test
     public void testList() throws IOException {
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
-        jmod("create",
+        jmod("--create",
              "--class-path", cp,
              MODS_DIR.resolve("foo.jmod").toString())
             .assertSuccess();
 
-        jmod("list",
+        jmod("--list",
               MODS_DIR.resolve("foo.jmod").toString())
             .assertSuccess()
             .resultChecker(r -> {
@@ -100,7 +101,7 @@
         FileUtils.deleteFileIfExistsWithRetry(jmod);
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
-        jmod("create",
+        jmod("--create",
              "--class-path", cp,
              "--main-class", "jdk.test.foo.Foo",
              jmod.toString())
@@ -118,7 +119,7 @@
         FileUtils.deleteFileIfExistsWithRetry(jmod);
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
-        jmod("create",
+        jmod("--create",
              "--class-path", cp,
              "--module-version", "5.4.3",
              jmod.toString())
@@ -137,7 +138,7 @@
         Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
         Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
 
-        jmod("create",
+        jmod("--create",
              "--class-path", cp.toString(),
              "--config", cf.toString(),
              jmod.toString())
@@ -159,7 +160,7 @@
         Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
         Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
 
-        jmod("create",
+        jmod("--create",
              "--cmds", bp.toString(),
              "--class-path", cp.toString(),
              jmod.toString())
@@ -181,7 +182,7 @@
         Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
         Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
 
-        jmod("create",
+        jmod("--create",
              "--libs=", lp.toString(),
              "--class-path", cp.toString(),
              jmod.toString())
@@ -205,7 +206,7 @@
         Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
         Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
 
-        jmod("create",
+        jmod("--create",
              "--conf", cf.toString(),
              "--cmds=", bp.toString(),
              "--libs=", lp.toString(),
@@ -232,7 +233,7 @@
         Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
         Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
 
-        jmod("create",
+        jmod("--create",
              "--libs=", lp.toString(),
              "--class-path", cp.toString(),
              "--exclude", "**internal**",
@@ -255,6 +256,34 @@
     }
 
     @Test
+    public void printModuleDescriptorFoo() throws IOException {
+        String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
+        jmod("--create",
+             "--class-path", cp,
+              MODS_DIR.resolve("printModuleDescriptorFoo.jmod").toString())
+             .assertSuccess();
+
+        for (String opt : new String[]  {"--print-module-descriptor", "-p" }) {
+            jmod(opt,
+                 MODS_DIR.resolve("printModuleDescriptorFoo.jmod").toString())
+                 .assertSuccess()
+                 .resultChecker(r -> {
+                     // Expect similar output: "Name:foo,  Requires: java.base
+                     // Exports: jdk.test.foo,  Conceals: jdk.test.foo.internal"
+                     Pattern p = Pattern.compile("\\s+Name:\\s+foo\\s+Requires:\\s+java.base");
+                     assertTrue(p.matcher(r.output).find(),
+                               "Expecting to find \"Name: foo, Requires: java.base\"" +
+                                    "in output, but did not: [" + r.output + "]");
+                     p = Pattern.compile(
+                            "Exports:\\s+jdk.test.foo\\s+Conceals:\\s+jdk.test.foo.internal");
+                     assertTrue(p.matcher(r.output).find(),
+                               "Expecting to find \"Exports: ..., Conceals: ...\"" +
+                                    "in output, but did not: [" + r.output + "]");
+                 });
+        }
+    }
+
+    @Test
     public void testVersion() {
         jmod("--version")
             .assertSuccess()
@@ -284,7 +313,7 @@
         Files.createFile(tmp);
         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 
-        jmod("create",
+        jmod("--create",
              "--class-path", cp,
              jmod.toString())
             .assertSuccess()
@@ -304,7 +333,7 @@
             assertTrue(true);
         else
             assertTrue(false,"Expected to find [" + subString + "], in output ["
-                           + output + "]");
+                           + output + "]" + "\n");
     }
 
     static ModuleDescriptor getModuleDescriptor(Path jmod) {
@@ -331,7 +360,7 @@
     }
 
     static Set<String> getJmodContent(Path jmod) {
-        JmodResult r = jmod("list", jmod.toString()).assertSuccess();
+        JmodResult r = jmod("--list", jmod.toString()).assertSuccess();
         return Stream.of(r.output.split("\r?\n")).collect(toSet());
     }