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 jdk.tools.jmod;
27
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.io.PrintStream;
35 import java.io.PrintWriter;
36 import java.io.UncheckedIOException;
37 import java.lang.module.Configuration;
38 import java.lang.module.ModuleReader;
39 import java.lang.module.ModuleReference;
40 import java.lang.module.ModuleFinder;
41 import java.lang.module.ModuleDescriptor;
42 import java.lang.module.ModuleDescriptor.Exports;
43 import java.lang.module.ModuleDescriptor.Opens;
44 import java.lang.module.ModuleDescriptor.Provides;
45 import java.lang.module.ModuleDescriptor.Requires;
46 import java.lang.module.ModuleDescriptor.Version;
47 import java.lang.module.ResolutionException;
48 import java.lang.module.ResolvedModule;
49 import java.net.URI;
50 import java.nio.file.FileSystems;
51 import java.nio.file.FileVisitOption;
52 import java.nio.file.FileVisitResult;
53 import java.nio.file.Files;
54 import java.nio.file.InvalidPathException;
67 import java.util.Deque;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.List;
71 import java.util.Locale;
72 import java.util.Map;
73 import java.util.MissingResourceException;
74 import java.util.Optional;
75 import java.util.ResourceBundle;
76 import java.util.Set;
77 import java.util.function.Consumer;
78 import java.util.function.Function;
79 import java.util.function.Predicate;
80 import java.util.function.Supplier;
81 import java.util.jar.JarEntry;
82 import java.util.jar.JarFile;
83 import java.util.jar.JarOutputStream;
84 import java.util.stream.Collectors;
85 import java.util.regex.Pattern;
86 import java.util.regex.PatternSyntaxException;
87 import java.util.stream.Stream;
88 import java.util.zip.ZipEntry;
89 import java.util.zip.ZipException;
90 import java.util.zip.ZipFile;
91
92 import jdk.internal.jmod.JmodFile;
93 import jdk.internal.jmod.JmodFile.Section;
94 import jdk.internal.joptsimple.BuiltinHelpFormatter;
95 import jdk.internal.joptsimple.NonOptionArgumentSpec;
96 import jdk.internal.joptsimple.OptionDescriptor;
97 import jdk.internal.joptsimple.OptionException;
98 import jdk.internal.joptsimple.OptionParser;
99 import jdk.internal.joptsimple.OptionSet;
100 import jdk.internal.joptsimple.OptionSpec;
101 import jdk.internal.joptsimple.ValueConverter;
102 import jdk.internal.loader.ResourceHelper;
103 import jdk.internal.misc.JavaLangModuleAccess;
104 import jdk.internal.misc.SharedSecrets;
105 import jdk.internal.module.ModuleHashes;
106 import jdk.internal.module.ModuleInfoExtender;
107 import jdk.tools.jlink.internal.Utils;
152 EXIT_ABNORMAL = 4;// terminated abnormally
153
154 enum Mode {
155 CREATE,
156 LIST,
157 DESCRIBE,
158 HASH
159 };
160
161 static class Options {
162 Mode mode;
163 Path jmodFile;
164 boolean help;
165 boolean version;
166 List<Path> classpath;
167 List<Path> cmds;
168 List<Path> configs;
169 List<Path> libs;
170 List<Path> headerFiles;
171 List<Path> manPages;
172 ModuleFinder moduleFinder;
173 Version moduleVersion;
174 String mainClass;
175 String osName;
176 String osArch;
177 String osVersion;
178 Pattern modulesToHash;
179 boolean dryrun;
180 List<PathMatcher> excludes;
181 }
182
183 public int run(String[] args) {
184
185 try {
186 handleOptions(args);
187 if (options == null) {
188 showUsageSummary();
189 return EXIT_CMDERR;
190 }
191 if (options.help) {
342 } catch (Exception e) {
343 if (Files.exists(tempTarget)) {
344 try {
345 Files.delete(tempTarget);
346 } catch (IOException ioe) {
347 e.addSuppressed(ioe);
348 }
349 }
350 throw e;
351 }
352 return true;
353 }
354
355 private class JmodFileWriter {
356 final List<Path> cmds = options.cmds;
357 final List<Path> libs = options.libs;
358 final List<Path> configs = options.configs;
359 final List<Path> classpath = options.classpath;
360 final List<Path> headerFiles = options.headerFiles;
361 final List<Path> manPages = options.manPages;
362
363 final Version moduleVersion = options.moduleVersion;
364 final String mainClass = options.mainClass;
365 final String osName = options.osName;
366 final String osArch = options.osArch;
367 final String osVersion = options.osVersion;
368 final List<PathMatcher> excludes = options.excludes;
369 final Hasher hasher = hasher();
370
371 JmodFileWriter() { }
372
373 /**
374 * Writes the jmod to the given output stream.
375 */
376 void write(JmodOutputStream out) throws IOException {
377 // module-info.class
378 writeModuleInfo(out, findPackages(classpath));
379
380 // classes
381 processClasses(out, classpath);
382
383 processSection(out, Section.NATIVE_CMDS, cmds);
384 processSection(out, Section.NATIVE_LIBS, libs);
385 processSection(out, Section.CONFIG, configs);
386 processSection(out, Section.HEADER_FILES, headerFiles);
387 processSection(out, Section.MAN_PAGES, manPages);
388
389 }
390
391 /**
392 * Returns a supplier of an input stream to the module-info.class
393 * on the class path of directories and JAR files.
394 */
395 Supplier<InputStream> newModuleInfoSupplier() throws IOException {
396 ByteArrayOutputStream baos = new ByteArrayOutputStream();
397 for (Path e: classpath) {
398 if (Files.isDirectory(e)) {
399 Path mi = e.resolve(MODULE_INFO);
400 if (Files.isRegularFile(mi)) {
401 Files.copy(mi, baos);
402 break;
403 }
404 } else if (Files.isRegularFile(e) && e.toString().endsWith(".jar")) {
405 try (JarFile jf = new JarFile(e.toFile())) {
406 ZipEntry entry = jf.getEntry(MODULE_INFO);
407 if (entry != null) {
627 return;
628
629 for (Path p : classpaths) {
630 if (Files.isDirectory(p)) {
631 processSection(out, Section.CLASSES, p);
632 } else if (Files.isRegularFile(p) && p.toString().endsWith(".jar")) {
633 try (JarFile jf = new JarFile(p.toFile())) {
634 JarEntryConsumer jec = new JarEntryConsumer(out, jf);
635 jf.stream().filter(jec).forEach(jec);
636 }
637 }
638 }
639 }
640
641 void processSection(JmodOutputStream out, Section section, List<Path> paths)
642 throws IOException
643 {
644 if (paths == null)
645 return;
646
647 for (Path p : paths)
648 processSection(out, section, p);
649 }
650
651 void processSection(JmodOutputStream out, Section section, Path top)
652 throws IOException
653 {
654 Files.walkFileTree(top, Set.of(FileVisitOption.FOLLOW_LINKS),
655 Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
656 @Override
657 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
658 throws IOException
659 {
660 Path relPath = top.relativize(file);
661 if (relPath.toString().equals(MODULE_INFO)
662 && !Section.CLASSES.equals(section))
663 warning("warn.ignore.entry", MODULE_INFO, section);
664
665 if (!relPath.toString().equals(MODULE_INFO)
666 && !matches(relPath, excludes)) {
667 try (InputStream in = Files.newInputStream(file)) {
668 out.writeEntry(in, section, relPath.toString());
669 } catch (IOException x) {
670 if (x.getMessage().contains("duplicate entry")) {
671 warning("warn.ignore.duplicate.entry", relPath.toString(), section);
672 return FileVisitResult.CONTINUE;
673 }
674 throw x;
675 }
676 }
677 return FileVisitResult.CONTINUE;
678 }
679 });
680 }
681
682 boolean matches(Path path, List<PathMatcher> matchers) {
683 if (matchers != null) {
684 for (PathMatcher pm : matchers) {
685 if (pm.matches(path))
686 return true;
687 }
688 }
689 return false;
690 }
691
1215 = parser.accepts("hash-modules", getMessage("main.opt.hash-modules"))
1216 .withRequiredArg()
1217 .withValuesConvertedBy(new PatternConverter());
1218
1219 OptionSpec<Void> help
1220 = parser.acceptsAll(Set.of("h", "help"), getMessage("main.opt.help"))
1221 .forHelp();
1222
1223 OptionSpec<Path> headerFiles
1224 = parser.accepts("header-files", getMessage("main.opt.header-files"))
1225 .withRequiredArg()
1226 .withValuesSeparatedBy(File.pathSeparatorChar)
1227 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1228
1229 OptionSpec<Path> libs
1230 = parser.accepts("libs", getMessage("main.opt.libs"))
1231 .withRequiredArg()
1232 .withValuesSeparatedBy(File.pathSeparatorChar)
1233 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1234
1235 OptionSpec<String> mainClass
1236 = parser.accepts("main-class", getMessage("main.opt.main-class"))
1237 .withRequiredArg()
1238 .describedAs(getMessage("main.opt.main-class.arg"));
1239
1240 OptionSpec<Path> manPages
1241 = parser.accepts("man-pages", getMessage("main.opt.man-pages"))
1242 .withRequiredArg()
1243 .withValuesSeparatedBy(File.pathSeparatorChar)
1244 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1245
1246 OptionSpec<Path> modulePath
1247 = parser.acceptsAll(Set.of("p", "module-path"),
1248 getMessage("main.opt.module-path"))
1249 .withRequiredArg()
1250 .withValuesSeparatedBy(File.pathSeparatorChar)
1251 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1252
1253 OptionSpec<Version> moduleVersion
1254 = parser.accepts("module-version", getMessage("main.opt.module-version"))
1296 } catch (IllegalArgumentException e) {
1297 throw new CommandException("err.invalid.mode", verb).showUsage(true);
1298 }
1299
1300 if (opts.has(classPath))
1301 options.classpath = opts.valuesOf(classPath);
1302 if (opts.has(cmds))
1303 options.cmds = opts.valuesOf(cmds);
1304 if (opts.has(config))
1305 options.configs = opts.valuesOf(config);
1306 if (opts.has(dryrun))
1307 options.dryrun = true;
1308 if (opts.has(excludes))
1309 options.excludes = opts.valuesOf(excludes);
1310 if (opts.has(libs))
1311 options.libs = opts.valuesOf(libs);
1312 if (opts.has(headerFiles))
1313 options.headerFiles = opts.valuesOf(headerFiles);
1314 if (opts.has(manPages))
1315 options.manPages = opts.valuesOf(manPages);
1316 if (opts.has(modulePath)) {
1317 Path[] dirs = opts.valuesOf(modulePath).toArray(new Path[0]);
1318 options.moduleFinder = JLMA.newModulePath(Runtime.version(), true, dirs);
1319 }
1320 if (opts.has(moduleVersion))
1321 options.moduleVersion = opts.valueOf(moduleVersion);
1322 if (opts.has(mainClass))
1323 options.mainClass = opts.valueOf(mainClass);
1324 if (opts.has(osName))
1325 options.osName = opts.valueOf(osName);
1326 if (opts.has(osArch))
1327 options.osArch = opts.valueOf(osArch);
1328 if (opts.has(osVersion))
1329 options.osVersion = opts.valueOf(osVersion);
1330 if (opts.has(hashModules)) {
1331 options.modulesToHash = opts.valueOf(hashModules);
1332 // if storing hashes then the module path is required
1333 if (options.moduleFinder == null)
1334 throw new CommandException("err.modulepath.must.be.specified")
1335 .showUsage(true);
|
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 jdk.tools.jmod;
27
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.io.PrintWriter;
35 import java.io.UncheckedIOException;
36 import java.lang.module.Configuration;
37 import java.lang.module.ModuleReader;
38 import java.lang.module.ModuleReference;
39 import java.lang.module.ModuleFinder;
40 import java.lang.module.ModuleDescriptor;
41 import java.lang.module.ModuleDescriptor.Exports;
42 import java.lang.module.ModuleDescriptor.Opens;
43 import java.lang.module.ModuleDescriptor.Provides;
44 import java.lang.module.ModuleDescriptor.Requires;
45 import java.lang.module.ModuleDescriptor.Version;
46 import java.lang.module.ResolutionException;
47 import java.lang.module.ResolvedModule;
48 import java.net.URI;
49 import java.nio.file.FileSystems;
50 import java.nio.file.FileVisitOption;
51 import java.nio.file.FileVisitResult;
52 import java.nio.file.Files;
53 import java.nio.file.InvalidPathException;
66 import java.util.Deque;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.List;
70 import java.util.Locale;
71 import java.util.Map;
72 import java.util.MissingResourceException;
73 import java.util.Optional;
74 import java.util.ResourceBundle;
75 import java.util.Set;
76 import java.util.function.Consumer;
77 import java.util.function.Function;
78 import java.util.function.Predicate;
79 import java.util.function.Supplier;
80 import java.util.jar.JarEntry;
81 import java.util.jar.JarFile;
82 import java.util.jar.JarOutputStream;
83 import java.util.stream.Collectors;
84 import java.util.regex.Pattern;
85 import java.util.regex.PatternSyntaxException;
86 import java.util.zip.ZipEntry;
87 import java.util.zip.ZipException;
88 import java.util.zip.ZipFile;
89
90 import jdk.internal.jmod.JmodFile;
91 import jdk.internal.jmod.JmodFile.Section;
92 import jdk.internal.joptsimple.BuiltinHelpFormatter;
93 import jdk.internal.joptsimple.NonOptionArgumentSpec;
94 import jdk.internal.joptsimple.OptionDescriptor;
95 import jdk.internal.joptsimple.OptionException;
96 import jdk.internal.joptsimple.OptionParser;
97 import jdk.internal.joptsimple.OptionSet;
98 import jdk.internal.joptsimple.OptionSpec;
99 import jdk.internal.joptsimple.ValueConverter;
100 import jdk.internal.loader.ResourceHelper;
101 import jdk.internal.misc.JavaLangModuleAccess;
102 import jdk.internal.misc.SharedSecrets;
103 import jdk.internal.module.ModuleHashes;
104 import jdk.internal.module.ModuleInfoExtender;
105 import jdk.tools.jlink.internal.Utils;
150 EXIT_ABNORMAL = 4;// terminated abnormally
151
152 enum Mode {
153 CREATE,
154 LIST,
155 DESCRIBE,
156 HASH
157 };
158
159 static class Options {
160 Mode mode;
161 Path jmodFile;
162 boolean help;
163 boolean version;
164 List<Path> classpath;
165 List<Path> cmds;
166 List<Path> configs;
167 List<Path> libs;
168 List<Path> headerFiles;
169 List<Path> manPages;
170 List<Path> legalNotices;;
171 ModuleFinder moduleFinder;
172 Version moduleVersion;
173 String mainClass;
174 String osName;
175 String osArch;
176 String osVersion;
177 Pattern modulesToHash;
178 boolean dryrun;
179 List<PathMatcher> excludes;
180 }
181
182 public int run(String[] args) {
183
184 try {
185 handleOptions(args);
186 if (options == null) {
187 showUsageSummary();
188 return EXIT_CMDERR;
189 }
190 if (options.help) {
341 } catch (Exception e) {
342 if (Files.exists(tempTarget)) {
343 try {
344 Files.delete(tempTarget);
345 } catch (IOException ioe) {
346 e.addSuppressed(ioe);
347 }
348 }
349 throw e;
350 }
351 return true;
352 }
353
354 private class JmodFileWriter {
355 final List<Path> cmds = options.cmds;
356 final List<Path> libs = options.libs;
357 final List<Path> configs = options.configs;
358 final List<Path> classpath = options.classpath;
359 final List<Path> headerFiles = options.headerFiles;
360 final List<Path> manPages = options.manPages;
361 final List<Path> legalNotices = options.legalNotices;
362
363 final Version moduleVersion = options.moduleVersion;
364 final String mainClass = options.mainClass;
365 final String osName = options.osName;
366 final String osArch = options.osArch;
367 final String osVersion = options.osVersion;
368 final List<PathMatcher> excludes = options.excludes;
369 final Hasher hasher = hasher();
370
371 JmodFileWriter() { }
372
373 /**
374 * Writes the jmod to the given output stream.
375 */
376 void write(JmodOutputStream out) throws IOException {
377 // module-info.class
378 writeModuleInfo(out, findPackages(classpath));
379
380 // classes
381 processClasses(out, classpath);
382
383 processSection(out, Section.CONFIG, configs);
384 processSection(out, Section.HEADER_FILES, headerFiles);
385 processSection(out, Section.LEGAL_NOTICES, legalNotices);
386 processSection(out, Section.MAN_PAGES, manPages);
387 processSection(out, Section.NATIVE_CMDS, cmds);
388 processSection(out, Section.NATIVE_LIBS, libs);
389
390 }
391
392 /**
393 * Returns a supplier of an input stream to the module-info.class
394 * on the class path of directories and JAR files.
395 */
396 Supplier<InputStream> newModuleInfoSupplier() throws IOException {
397 ByteArrayOutputStream baos = new ByteArrayOutputStream();
398 for (Path e: classpath) {
399 if (Files.isDirectory(e)) {
400 Path mi = e.resolve(MODULE_INFO);
401 if (Files.isRegularFile(mi)) {
402 Files.copy(mi, baos);
403 break;
404 }
405 } else if (Files.isRegularFile(e) && e.toString().endsWith(".jar")) {
406 try (JarFile jf = new JarFile(e.toFile())) {
407 ZipEntry entry = jf.getEntry(MODULE_INFO);
408 if (entry != null) {
628 return;
629
630 for (Path p : classpaths) {
631 if (Files.isDirectory(p)) {
632 processSection(out, Section.CLASSES, p);
633 } else if (Files.isRegularFile(p) && p.toString().endsWith(".jar")) {
634 try (JarFile jf = new JarFile(p.toFile())) {
635 JarEntryConsumer jec = new JarEntryConsumer(out, jf);
636 jf.stream().filter(jec).forEach(jec);
637 }
638 }
639 }
640 }
641
642 void processSection(JmodOutputStream out, Section section, List<Path> paths)
643 throws IOException
644 {
645 if (paths == null)
646 return;
647
648 for (Path p : paths) {
649 processSection(out, section, p);
650 }
651 }
652
653 void processSection(JmodOutputStream out, Section section, Path path)
654 throws IOException
655 {
656 Files.walkFileTree(path, Set.of(FileVisitOption.FOLLOW_LINKS),
657 Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
658 @Override
659 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
660 throws IOException
661 {
662 Path relPath = path.relativize(file);
663 if (relPath.toString().equals(MODULE_INFO)
664 && !Section.CLASSES.equals(section))
665 warning("warn.ignore.entry", MODULE_INFO, section);
666
667 if (!relPath.toString().equals(MODULE_INFO)
668 && !matches(relPath, excludes)) {
669 try (InputStream in = Files.newInputStream(file)) {
670 out.writeEntry(in, section, relPath.toString());
671 } catch (IOException x) {
672 if (x.getMessage().contains("duplicate entry")) {
673 warning("warn.ignore.duplicate.entry",
674 relPath.toString(), section);
675 return FileVisitResult.CONTINUE;
676 }
677 throw x;
678 }
679 }
680 return FileVisitResult.CONTINUE;
681 }
682 });
683 }
684
685 boolean matches(Path path, List<PathMatcher> matchers) {
686 if (matchers != null) {
687 for (PathMatcher pm : matchers) {
688 if (pm.matches(path))
689 return true;
690 }
691 }
692 return false;
693 }
694
1218 = parser.accepts("hash-modules", getMessage("main.opt.hash-modules"))
1219 .withRequiredArg()
1220 .withValuesConvertedBy(new PatternConverter());
1221
1222 OptionSpec<Void> help
1223 = parser.acceptsAll(Set.of("h", "help"), getMessage("main.opt.help"))
1224 .forHelp();
1225
1226 OptionSpec<Path> headerFiles
1227 = parser.accepts("header-files", getMessage("main.opt.header-files"))
1228 .withRequiredArg()
1229 .withValuesSeparatedBy(File.pathSeparatorChar)
1230 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1231
1232 OptionSpec<Path> libs
1233 = parser.accepts("libs", getMessage("main.opt.libs"))
1234 .withRequiredArg()
1235 .withValuesSeparatedBy(File.pathSeparatorChar)
1236 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1237
1238 OptionSpec<Path> legalNotices
1239 = parser.accepts("legal-notices", getMessage("main.opt.legal-notices"))
1240 .withRequiredArg()
1241 .withValuesSeparatedBy(File.pathSeparatorChar)
1242 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1243
1244
1245 OptionSpec<String> mainClass
1246 = parser.accepts("main-class", getMessage("main.opt.main-class"))
1247 .withRequiredArg()
1248 .describedAs(getMessage("main.opt.main-class.arg"));
1249
1250 OptionSpec<Path> manPages
1251 = parser.accepts("man-pages", getMessage("main.opt.man-pages"))
1252 .withRequiredArg()
1253 .withValuesSeparatedBy(File.pathSeparatorChar)
1254 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1255
1256 OptionSpec<Path> modulePath
1257 = parser.acceptsAll(Set.of("p", "module-path"),
1258 getMessage("main.opt.module-path"))
1259 .withRequiredArg()
1260 .withValuesSeparatedBy(File.pathSeparatorChar)
1261 .withValuesConvertedBy(DirPathConverter.INSTANCE);
1262
1263 OptionSpec<Version> moduleVersion
1264 = parser.accepts("module-version", getMessage("main.opt.module-version"))
1306 } catch (IllegalArgumentException e) {
1307 throw new CommandException("err.invalid.mode", verb).showUsage(true);
1308 }
1309
1310 if (opts.has(classPath))
1311 options.classpath = opts.valuesOf(classPath);
1312 if (opts.has(cmds))
1313 options.cmds = opts.valuesOf(cmds);
1314 if (opts.has(config))
1315 options.configs = opts.valuesOf(config);
1316 if (opts.has(dryrun))
1317 options.dryrun = true;
1318 if (opts.has(excludes))
1319 options.excludes = opts.valuesOf(excludes);
1320 if (opts.has(libs))
1321 options.libs = opts.valuesOf(libs);
1322 if (opts.has(headerFiles))
1323 options.headerFiles = opts.valuesOf(headerFiles);
1324 if (opts.has(manPages))
1325 options.manPages = opts.valuesOf(manPages);
1326 if (opts.has(legalNotices))
1327 options.legalNotices = opts.valuesOf(legalNotices);
1328 if (opts.has(modulePath)) {
1329 Path[] dirs = opts.valuesOf(modulePath).toArray(new Path[0]);
1330 options.moduleFinder = JLMA.newModulePath(Runtime.version(), true, dirs);
1331 }
1332 if (opts.has(moduleVersion))
1333 options.moduleVersion = opts.valueOf(moduleVersion);
1334 if (opts.has(mainClass))
1335 options.mainClass = opts.valueOf(mainClass);
1336 if (opts.has(osName))
1337 options.osName = opts.valueOf(osName);
1338 if (opts.has(osArch))
1339 options.osArch = opts.valueOf(osArch);
1340 if (opts.has(osVersion))
1341 options.osVersion = opts.valueOf(osVersion);
1342 if (opts.has(hashModules)) {
1343 options.modulesToHash = opts.valueOf(hashModules);
1344 // if storing hashes then the module path is required
1345 if (options.moduleFinder == null)
1346 throw new CommandException("err.modulepath.must.be.specified")
1347 .showUsage(true);
|