8168149: Examine the behavior of jmod command-line options - repeating vs last one wins

Reviewed-by: mchung
This commit is contained in:
Chris Hegarty 2017-01-12 11:41:51 +00:00
parent 4ec1878401
commit 186fec9a8a
2 changed files with 145 additions and 56 deletions

View File

@ -1088,34 +1088,58 @@ public class JmodTask {
}
}
static class ClassPathConverter implements ValueConverter<Path> {
static final ValueConverter<Path> INSTANCE = new ClassPathConverter();
/**
* An abstract converter that given a string representing a list of paths,
* separated by the File.pathSeparator, returns a List of java.nio.Path's.
* Specific subclasses should do whatever validation is required on the
* individual path elements, if any.
*/
static abstract class AbstractPathConverter implements ValueConverter<List<Path>> {
@Override
public List<Path> convert(String value) {
List<Path> paths = new ArrayList<>();
String[] pathElements = value.split(File.pathSeparator);
for (String pathElement : pathElements) {
paths.add(toPath(pathElement));
}
return paths;
}
@SuppressWarnings("unchecked")
@Override
public Class<List<Path>> valueType() {
return (Class<List<Path>>)(Object)List.class;
}
@Override public String valuePattern() { return "path"; }
abstract Path toPath(String path);
}
static class ClassPathConverter extends AbstractPathConverter {
static final ValueConverter<List<Path>> INSTANCE = new ClassPathConverter();
@Override
public Path convert(String value) {
public Path toPath(String value) {
try {
Path path = CWD.resolve(value);
if (Files.notExists(path))
throw new CommandException("err.path.not.found", path);
if (! (Files.isDirectory(path) ||
(Files.isRegularFile(path) && path.toString().endsWith(".jar"))))
if (!(Files.isDirectory(path) ||
(Files.isRegularFile(path) && path.toString().endsWith(".jar"))))
throw new CommandException("err.invalid.class.path.entry", path);
return path;
} catch (InvalidPathException x) {
throw new CommandException("err.path.not.valid", value);
}
}
@Override public Class<Path> valueType() { return Path.class; }
@Override public String valuePattern() { return "path"; }
}
static class DirPathConverter implements ValueConverter<Path> {
static final ValueConverter<Path> INSTANCE = new DirPathConverter();
static class DirPathConverter extends AbstractPathConverter {
static final ValueConverter<List<Path>> INSTANCE = new DirPathConverter();
@Override
public Path convert(String value) {
public Path toPath(String value) {
try {
Path path = CWD.resolve(value);
if (Files.notExists(path))
@ -1127,10 +1151,6 @@ public class JmodTask {
throw new CommandException("err.path.not.valid", value);
}
}
@Override public Class<Path> valueType() { return Path.class; }
@Override public String valuePattern() { return "path"; }
}
static class ExtractDirPathConverter implements ValueConverter<Path> {
@ -1142,12 +1162,6 @@ public class JmodTask {
if (Files.exists(path)) {
if (!Files.isDirectory(path))
throw new CommandException("err.cannot.create.dir", path);
} else {
try {
Files.createDirectories(path);
} catch (IOException ioe) {
throw new CommandException("err.cannot.create.dir", path);
}
}
return path;
} catch (InvalidPathException x) {
@ -1316,22 +1330,19 @@ public class JmodTask {
options = new Options();
parser.formatHelpWith(new JmodHelpFormatter(options));
OptionSpec<Path> classPath
OptionSpec<List<Path>> classPath
= parser.accepts("class-path", getMessage("main.opt.class-path"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(ClassPathConverter.INSTANCE);
OptionSpec<Path> cmds
OptionSpec<List<Path>> cmds
= parser.accepts("cmds", getMessage("main.opt.cmds"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
OptionSpec<Path> config
OptionSpec<List<Path>> config
= parser.accepts("config", getMessage("main.opt.config"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
OptionSpec<Path> dir
@ -1359,22 +1370,19 @@ public class JmodTask {
OptionSpec<Void> helpExtra
= parser.accepts("help-extra", getMessage("main.opt.help-extra"));
OptionSpec<Path> headerFiles
OptionSpec<List<Path>> headerFiles
= parser.accepts("header-files", getMessage("main.opt.header-files"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
OptionSpec<Path> libs
OptionSpec<List<Path>> libs
= parser.accepts("libs", getMessage("main.opt.libs"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
OptionSpec<Path> legalNotices
OptionSpec<List<Path>> legalNotices
= parser.accepts("legal-notices", getMessage("main.opt.legal-notices"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
@ -1383,17 +1391,15 @@ public class JmodTask {
.withRequiredArg()
.describedAs(getMessage("main.opt.main-class.arg"));
OptionSpec<Path> manPages
OptionSpec<List<Path>> manPages
= parser.accepts("man-pages", getMessage("main.opt.man-pages"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
OptionSpec<Path> modulePath
OptionSpec<List<Path>> modulePath
= parser.acceptsAll(Set.of("p", "module-path"),
getMessage("main.opt.module-path"))
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(DirPathConverter.INSTANCE);
OptionSpec<Version> moduleVersion
@ -1452,48 +1458,48 @@ public class JmodTask {
}
if (opts.has(classPath))
options.classpath = opts.valuesOf(classPath);
options.classpath = getLastElement(opts.valuesOf(classPath));
if (opts.has(cmds))
options.cmds = opts.valuesOf(cmds);
options.cmds = getLastElement(opts.valuesOf(cmds));
if (opts.has(config))
options.configs = opts.valuesOf(config);
options.configs = getLastElement(opts.valuesOf(config));
if (opts.has(dir))
options.extractDir = opts.valueOf(dir);
options.extractDir = getLastElement(opts.valuesOf(dir));
if (opts.has(dryrun))
options.dryrun = true;
if (opts.has(excludes))
options.excludes = opts.valuesOf(excludes);
options.excludes = opts.valuesOf(excludes); // excludes is repeatable
if (opts.has(libs))
options.libs = opts.valuesOf(libs);
options.libs = getLastElement(opts.valuesOf(libs));
if (opts.has(headerFiles))
options.headerFiles = opts.valuesOf(headerFiles);
options.headerFiles = getLastElement(opts.valuesOf(headerFiles));
if (opts.has(manPages))
options.manPages = opts.valuesOf(manPages);
options.manPages = getLastElement(opts.valuesOf(manPages));
if (opts.has(legalNotices))
options.legalNotices = opts.valuesOf(legalNotices);
options.legalNotices = getLastElement(opts.valuesOf(legalNotices));
if (opts.has(modulePath)) {
Path[] dirs = opts.valuesOf(modulePath).toArray(new Path[0]);
Path[] dirs = getLastElement(opts.valuesOf(modulePath)).toArray(new Path[0]);
options.moduleFinder = new ModulePath(Runtime.version(), true, dirs);
}
if (opts.has(moduleVersion))
options.moduleVersion = opts.valueOf(moduleVersion);
options.moduleVersion = getLastElement(opts.valuesOf(moduleVersion));
if (opts.has(mainClass))
options.mainClass = opts.valueOf(mainClass);
options.mainClass = getLastElement(opts.valuesOf(mainClass));
if (opts.has(osName))
options.osName = opts.valueOf(osName);
options.osName = getLastElement(opts.valuesOf(osName));
if (opts.has(osArch))
options.osArch = opts.valueOf(osArch);
options.osArch = getLastElement(opts.valuesOf(osArch));
if (opts.has(osVersion))
options.osVersion = opts.valueOf(osVersion);
options.osVersion = getLastElement(opts.valuesOf(osVersion));
if (opts.has(warnIfResolved))
options.moduleResolution = opts.valueOf(warnIfResolved);
options.moduleResolution = getLastElement(opts.valuesOf(warnIfResolved));
if (opts.has(doNotResolveByDefault)) {
if (options.moduleResolution == null)
options.moduleResolution = ModuleResolution.empty();
options.moduleResolution = options.moduleResolution.withDoNotResolveByDefault();
}
if (opts.has(hashModules)) {
options.modulesToHash = opts.valueOf(hashModules);
options.modulesToHash = getLastElement(opts.valuesOf(hashModules));
// if storing hashes then the module path is required
if (options.moduleFinder == null)
throw new CommandException("err.modulepath.must.be.specified")
@ -1531,6 +1537,13 @@ public class JmodTask {
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);
if (options.mode.equals(Mode.EXTRACT) && options.extractDir != null) {
try {
Files.createDirectories(options.extractDir);
} catch (IOException ioe) {
throw new CommandException("err.cannot.create.dir", options.extractDir);
}
}
} catch (OptionException e) {
throw new CommandException(e.getMessage());
}
@ -1558,6 +1571,12 @@ public class JmodTask {
return true;
}
static <E> E getLastElement(List<E> list) {
if (list.size() == 0)
throw new InternalError("Unexpected 0 list size");
return list.get(list.size() - 1);
}
private void reportError(String message) {
out.println(getMessage("error.prefix") + " " + message);
}

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8142968 8166568 8166286 8170618
* @bug 8142968 8166568 8166286 8170618 8168149
* @summary Basic test for jmod
* @library /lib/testlibrary
* @modules jdk.compiler
@ -458,6 +458,76 @@ public class JmodTest {
);
}
@Test
public void testLastOneWins() throws IOException {
Path workDir = Paths.get("lastOneWins");
if (Files.exists(workDir))
FileUtils.deleteFileTreeWithRetry(workDir);
Files.createDirectory(workDir);
Path jmod = MODS_DIR.resolve("lastOneWins.jmod");
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
Path shouldNotBeAdded = workDir.resolve("shouldNotBeAdded");
Files.createDirectory(shouldNotBeAdded);
Files.write(shouldNotBeAdded.resolve("aFile"), "hello".getBytes(UTF_8));
// Pairs of options. For options with required arguments the last one
// should win ( first should be effectively ignored, but may still be
// validated ).
jmod("create",
"--conf", shouldNotBeAdded.toString(),
"--conf", cf.toString(),
"--cmds", shouldNotBeAdded.toString(),
"--cmds", bp.toString(),
"--libs", shouldNotBeAdded.toString(),
"--libs", lp.toString(),
"--class-path", shouldNotBeAdded.toString(),
"--class-path", cp.toString(),
"--main-class", "does.NotExist",
"--main-class", "jdk.test.foo.Foo",
"--module-version", "00001",
"--module-version", "5.4.3",
"--do-not-resolve-by-default",
"--do-not-resolve-by-default",
"--warn-if-resolved=incubating",
"--warn-if-resolved=deprecated",
MODS_DIR.resolve("lastOneWins.jmod").toString())
.assertSuccess()
.resultChecker(r -> {
ModuleDescriptor md = getModuleDescriptor(jmod);
Optional<String> omc = md.mainClass();
assertTrue(omc.isPresent());
assertEquals(omc.get(), "jdk.test.foo.Foo");
Optional<Version> ov = md.version();
assertTrue(ov.isPresent());
assertEquals(ov.get().toString(), "5.4.3");
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);
Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);
Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {
Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),
Stream.concat(s3, s4))
.collect(toSet());
assertJmodContent(jmod, expectedFilenames);
}
});
jmod("extract",
"--dir", "blah",
"--dir", "lastOneWinsExtractDir",
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
assertTrue(Files.exists(Paths.get("lastOneWinsExtractDir")));
assertTrue(Files.notExists(Paths.get("blah")));
});
}
@Test
public void testPackagesAttribute() throws IOException {
Path jmod = MODS_DIR.resolve("foo.jmod");