8217527: jmod hash does not work if --hash-module does not include the target module

Reviewed-by: alanb
This commit is contained in:
Mandy Chung 2020-07-20 11:40:03 -07:00
parent 3a69dfb5c5
commit 35554ea085
4 changed files with 146 additions and 43 deletions

View File

@ -259,19 +259,35 @@ public class JmodTask {
}
}
private boolean hashModules() {
private boolean hashModules() throws IOException {
String moduleName = null;
if (options.jmodFile != null) {
try (JmodFile jf = new JmodFile(options.jmodFile)) {
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
moduleName = attrs.descriptor().name();
} catch (IOException e) {
throw new CommandException("err.module.descriptor.not.found");
}
}
}
Hasher hasher = new Hasher(moduleName, options.moduleFinder);
if (options.dryrun) {
out.println("Dry run:");
}
Hasher hasher = new Hasher(options.moduleFinder);
hasher.computeHashes().forEach((mn, hashes) -> {
Map<String, ModuleHashes> moduleHashes = hasher.computeHashes();
if (moduleHashes.isEmpty()) {
throw new CommandException("err.no.moduleToHash", "\"" + options.modulesToHash + "\"");
}
moduleHashes.forEach((mn, hashes) -> {
if (options.dryrun) {
out.format("%s%n", mn);
hashes.names().stream()
.sorted()
.forEach(name -> out.format(" hashes %s %s %s%n",
name, hashes.algorithm(), toHex(hashes.hashFor(name))));
.sorted()
.forEach(name -> out.format(" hashes %s %s %s%n",
name, hashes.algorithm(), toHex(hashes.hashFor(name))));
} else {
try {
hasher.updateModuleInfo(mn, hashes);
@ -825,19 +841,6 @@ public class JmodTask {
final Set<String> modules;
final String moduleName; // a specific module to record hashes, if set
/**
* This constructor is for jmod hash command.
*
* This Hasher will determine which modules to record hashes, i.e.
* the module in a subgraph of modules to be hashed and that
* has no outgoing edges. It will record in each of these modules,
* say `M`, with the the hashes of modules that depend upon M
* directly or indirectly matching the specified --hash-modules pattern.
*/
Hasher(ModuleFinder finder) {
this(null, finder);
}
/**
* Constructs a Hasher to compute hashes.
*
@ -846,6 +849,13 @@ public class JmodTask {
* specified --hash-modules pattern and record in the ModuleHashes
* attribute in M's module-info.class.
*
* If name is null, this Hasher will determine which modules to
* record hashes, i.e. the module in a subgraph of modules to be
* hashed and that has no outgoing edges. It will record in each
* of these modules, say `M`, with the hashes of modules that
* depend upon M directly or indirectly matching the specified
* --hash-modules pattern.
*
* @param name name of the module to record hashes
* @param finder module finder for the specified --module-path
*/
@ -1432,6 +1442,18 @@ public class JmodTask {
if (options.moduleFinder == null || options.modulesToHash == null)
throw new CommandException("err.modulepath.must.be.specified")
.showUsage(true);
// It's optional to specify jmod-file. If not specified, then
// it will find all the modules that have no outgoing read edges
if (words.size() >= 2) {
Path path = Paths.get(words.get(1));
if (Files.notExists(path))
throw new CommandException("err.jmod.not.found", path);
options.jmodFile = path;
}
if (words.size() > 2)
throw new CommandException("err.unknown.option",
words.subList(2, words.size())).showUsage(true);
} else {
if (words.size() <= 1)
throw new CommandException("err.jmod.must.be.specified").showUsage(true);

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -105,6 +105,7 @@ err.invalid.dryrun.option=--dry-run can only be used with hash mode
err.module.descriptor.not.found=Module descriptor not found
err.missing.export.or.open.packages=Packages that are exported or open in {0} are not present: {1}
err.module.resolution.fail=Resolution failed: {0}
err.no.moduleToHash=No hashes recorded: no module matching {0} found to record hashes
warn.invalid.arg=Invalid classname or pathname not exist: {0}
warn.no.module.hashes=No hashes recorded: no module specified for hashing depends on {0}
warn.ignore.entry=ignoring entry {0}, in section {1}

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -506,6 +506,34 @@ public class JmodNegativeTest {
});
}
@Test
public void testNoMatchingHashModule() throws IOException {
Path lib = Paths.get("hashes");
Files.createDirectories(lib);
// create jmod file with no module depending on it
Path jmod = lib.resolve("foo.jmod");
jmod("create",
"--class-path", EXPLODED_DIR.resolve("foo").resolve("classes").toString(),
jmod.toString());
// jmod hash command should report no module found to record hashes
jmod("hash",
"--module-path", lib.toString(),
"--hash-modules", ".*",
jmod.toString())
.resultChecker(r ->
assertContains(r.output, "No hashes recorded: " +
"no module matching \".*\" found to record hashes")
);
jmod("hash",
"--module-path", lib.toString(),
"--hash-modules", "foo")
.resultChecker(r ->
assertContains(r.output, "No hashes recorded: " +
"no module matching \"foo\" found to record hashes")
);
}
// ---
static boolean compileModule(String name, Path dest) throws IOException {

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8160286 8243666
* @bug 8160286 8243666 8217527
* @summary Test the recording and checking of module hashes
* @library /test/lib
* @modules java.base/jdk.internal.misc
@ -126,29 +126,29 @@ public class HashesTest {
// hash m1 in m2
ht.jmodHashModules("m2", "m1");
ht.checkHashes("m2", "m1");
ht.checkHashes("m2", Set.of("m1"));
// hash m1 in m2
ht.jmodHashModules("m2", ".*");
ht.checkHashes("m2", "m1");
ht.checkHashes("m2", Set.of("m1"));
// create m2.jmod with no hash
ht.makeJmod("m2");
// run jmod hash command to hash m1 in m2 and m3
runJmod(List.of("hash", "--module-path", ht.lib.toString(),
"--hash-modules", ".*"));
ht.checkHashes("m2", "m1");
ht.checkHashes("m3", "m1");
ht.checkHashes("m2", Set.of("m1"));
ht.checkHashes("m3", Set.of("m1"));
// check transitive requires
ht.makeJmod("org.bar");
ht.makeJmod("org.foo");
ht.jmodHashModules("org.bar", "org.*");
ht.checkHashes("org.bar", "org.foo");
ht.checkHashes("org.bar", Set.of("org.foo"));
ht.jmodHashModules( "m3", ".*");
ht.checkHashes("m3", "org.foo", "org.bar", "m1");
ht.checkHashes("m3", Set.of("org.foo", "org.bar", "m1"));
}
@Test
@ -187,8 +187,8 @@ public class HashesTest {
/*
* z1 and y1 are the modules with hashes recorded.
*/
ht.checkHashes("y1", "y2");
ht.checkHashes("z1", "z2", "z3", "y2");
ht.checkHashes("y1", Set.of("y2"));
ht.checkHashes("z1", Set.of("z2", "z3", "y2"));
Stream.concat(ys.stream(), zs.stream())
.filter(mn -> !mn.equals("y1") && !mn.equals("z1"))
.forEach(mn -> assertTrue(ht.hashes(mn) == null));
@ -232,8 +232,8 @@ public class HashesTest {
/*
* j1 and j2 are the modules with hashes recorded.
*/
ht.checkHashes("j2", "j3");
ht.checkHashes("j1", "m1", "m2", "m3", "j3");
ht.checkHashes("j2", Set.of("j3"));
ht.checkHashes("j1", Set.of("m1", "m2", "m3", "j3"));
Stream.concat(jars.stream(), jmods.stream())
.filter(mn -> !mn.equals("j1") && !mn.equals("j2"))
.forEach(mn -> assertTrue(ht.hashes(mn) == null));
@ -259,7 +259,7 @@ public class HashesTest {
ht.lib.toString() + File.pathSeparator + mpath,
"--hash-modules", "java\\.(?!se)|^m.*");
ht.checkHashes("java.compiler", "m2");
ht.checkHashes("java.compiler", Set.of("m2"));
}
@Test
@ -330,11 +330,56 @@ public class HashesTest {
assertEquals(hashes1, hashes2);
}
@Test
public static void testHashModulesPattern() throws IOException {
Path dest = Paths.get("regex");
HashesTest ht = new HashesTest(dest);
// create modules for test cases
ht.makeModule("m1");
ht.makeModule("m2", "m1");
ht.makeModule("m3");
ht.makeModule("m4", "m1", "m3");
List.of("m1", "m2", "m3", "m4").forEach(ht::makeJmod);
// compute hash for the target jmod (m1.jmod) with different regex
// 1) --hash-module "m2"
Path jmod = ht.lib.resolve("m1.jmod");
runJmod("hash",
"--module-path", ht.lib.toString(),
"--hash-modules", "m2", jmod.toString());
assertEquals(ht.moduleHashes().keySet(), Set.of("m1"));
ht.checkHashes("m1", Set.of("m2"));
// 2) --hash-module "m2|m4"
runJmod("hash",
"--module-path", ht.lib.toString(),
"--hash-modules", "m2|m4", jmod.toString());
assertEquals(ht.moduleHashes().keySet(), Set.of("m1"));
ht.checkHashes("m1", Set.of("m2", "m4"));
// 3) --hash-module ".*"
runJmod("hash",
"--module-path", ht.lib.toString(),
"--hash-modules", ".*", jmod.toString());
assertEquals(ht.moduleHashes().keySet(), Set.of("m1"));
ht.checkHashes("m1", Set.of("m2", "m4"));
// target jmod is not specified
// compute hash for all modules in the library
runJmod("hash",
"--module-path", ht.lib.toString(),
"--hash-modules", ".*");
assertEquals(ht.moduleHashes().keySet(), Set.of("m1", "m3"));
ht.checkHashes("m1", Set.of("m2", "m4"));
ht.checkHashes("m3", Set.of("m4"));
}
private static void validateImageJmodsTest(HashesTest ht, Path mpath)
throws IOException
{
// hash is recorded in m1 and not any other packaged modules on module path
ht.checkHashes("m1", "m2");
ht.checkHashes("m1", Set.of("m2"));
assertTrue(ht.hashes("m2") == null);
// should not override any JDK packaged modules
@ -343,9 +388,9 @@ public class HashesTest {
assertTrue(ht.hashes(finder,"jdk.attach") == null);
}
private void checkHashes(String mn, String... hashModules) throws IOException {
private void checkHashes(String mn, Set<String> hashModules) throws IOException {
ModuleHashes hashes = hashes(mn);
assertTrue(hashes.names().equals(Set.of(hashModules)));
assertTrue(hashes.names().equals(hashModules));
}
private ModuleHashes hashes(String name) {
@ -465,22 +510,29 @@ public class HashesTest {
* a ModuleHashes class file attribute.
*/
private Map<String, ModuleHashes> runJmodHash() {
runJmod(List.of("hash",
runJmod("hash",
"--module-path", lib.toString(),
"--hash-modules", ".*"));
HashesTest ht = this;
"--hash-modules", ".*");
return moduleHashes();
}
private Map<String, ModuleHashes> moduleHashes() {
return ModulePath.of(Runtime.version(), true, lib)
.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.filter(mn -> ht.hashes(mn) != null)
.collect(Collectors.toMap(mn -> mn, ht::hashes));
.filter(mn -> hashes(mn) != null)
.collect(Collectors.toMap(mn -> mn, this::hashes));
}
private static void runJmod(List<String> args) {
int rc = JMOD_TOOL.run(System.out, System.out, args.toArray(new String[args.size()]));
System.out.println("jmod " + args.stream().collect(Collectors.joining(" ")));
runJmod(args.toArray(new String[args.size()]));
}
private static void runJmod(String... args) {
int rc = JMOD_TOOL.run(System.out, System.out, args);
System.out.println("jmod " + Arrays.stream(args).collect(Collectors.joining(" ")));
if (rc != 0) {
throw new AssertionError("jmod failed: rc = " + rc);
}