Skip to content

Commit

Permalink
8293499: Provide jmod --compress option
Browse files Browse the repository at this point in the history
Reviewed-by: alanb, mchung, jpai, redestad
  • Loading branch information
shipilev committed Sep 19, 2022
1 parent 26e08cf commit 43f7f47
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 7 deletions.
Expand Up @@ -55,16 +55,17 @@ class JmodOutputStream extends OutputStream implements AutoCloseable {
* This method creates (or overrides, if exists) the JMOD file,
* returning the output stream to write to the JMOD file.
*/
static JmodOutputStream newOutputStream(Path file, LocalDateTime date) throws IOException {
static JmodOutputStream newOutputStream(Path file, LocalDateTime date, int compressLevel) throws IOException {
OutputStream out = Files.newOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
return new JmodOutputStream(bos, date);
return new JmodOutputStream(bos, date, compressLevel);
}

private final ZipOutputStream zos;
private final LocalDateTime date;
private JmodOutputStream(OutputStream out, LocalDateTime date) {
private JmodOutputStream(OutputStream out, LocalDateTime date, int compressLevel) {
this.zos = new ZipOutputStream(out);
this.zos.setLevel(compressLevel);
this.date = date;
try {
JmodFile.writeMagicNumber(out);
Expand Down
51 changes: 48 additions & 3 deletions src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
Expand Down Expand Up @@ -60,6 +60,7 @@
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
Expand Down Expand Up @@ -167,6 +168,7 @@ static class Options {
List<PathMatcher> excludes;
Path extractDir;
LocalDateTime date;
int compressLevel;
}

// Valid --date range
Expand Down Expand Up @@ -438,7 +440,7 @@ private boolean create() throws IOException {
Path target = options.jmodFile;
Path tempTarget = jmodTempFilePath(target);
try {
try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget, options.date)) {
try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget, options.date, options.compressLevel)) {
jmod.write(jos);
}
Files.move(tempTarget, target);
Expand Down Expand Up @@ -1024,7 +1026,7 @@ private void updateJmodFile(Path target, Path tempTarget,
{

try (JmodFile jf = new JmodFile(target);
JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget, options.date))
JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget, options.date, options.compressLevel))
{
jf.stream().forEach(e -> {
try (InputStream in = jf.getInputStream(e.section(), e.name())) {
Expand Down Expand Up @@ -1179,6 +1181,33 @@ public LocalDateTime convert(String value) {
@Override public String valuePattern() { return "date"; }
}

static class CompLevelConverter implements ValueConverter<Integer> {
@Override
public Integer convert(String value) {
int idx = value.indexOf("-");
int lastIdx = value.lastIndexOf("-");
if (idx == -1 || idx != lastIdx) {
throw new CommandException("err.compress.incorrect", value);
}
if (!value.substring(0, idx).equals("zip")) {
throw new CommandException("err.compress.incorrect", value);
}
try {
int level = Integer.parseInt(value.substring(idx + 1));
if (level < 0 || level > 9) {
throw new CommandException("err.compress.incorrect", value);
}
return level;
} catch (NumberFormatException x) {
throw new CommandException("err.compress.incorrect", value);
}
}

@Override public Class<Integer> valueType() { return Integer.class; }

@Override public String valuePattern() { return "compress"; }
}

static class WarnIfResolvedReasonConverter
implements ValueConverter<ModuleResolution>
{
Expand Down Expand Up @@ -1419,6 +1448,11 @@ private void handleOptions(String[] args) {
.withRequiredArg()
.withValuesConvertedBy(new DateConverter());

OptionSpec<Integer> compress
= parser.accepts("compress", getMessage("main.opt.compress"))
.withRequiredArg()
.withValuesConvertedBy(new CompLevelConverter());

NonOptionArgumentSpec<String> nonOptions
= parser.nonOptions();

Expand Down Expand Up @@ -1488,6 +1522,17 @@ private void handleOptions(String[] args) {
throw new CommandException("err.modulepath.must.be.specified")
.showUsage(true);
}
if (opts.has(compress)) {
if (!options.mode.equals(Mode.CREATE)) {
throw new CommandException("err.compress.wrong.mode")
.showUsage(true);
}
options.compressLevel = getLastElement(opts.valuesOf(compress));
} else {
// Default to the default from zlib. Hard-coded here to avoid accidental
// compression level change if zlib ever changes the default.
options.compressLevel = 6;
}

if (options.mode.equals(Mode.HASH)) {
if (options.moduleFinder == null || options.modulesToHash == null)
Expand Down
Expand Up @@ -82,6 +82,10 @@ main.opt.date=Date and time for the timestamps of entries, specified in ISO-8601

main.opt.cmdfile=Read options from the specified file

main.opt.compress=Compression to use when creating the JMOD archive.\
\ Accepted values are: zip-[0-9], where zip-0 provides no compression, and zip-9\
\ provides the best compression. Default is zip-6.

module.hashes.recorded=Hashes are recorded in module {0}

err.missing.mode=one of create, extract, list, describe, or hash must be specified
Expand Down Expand Up @@ -113,6 +117,8 @@ err.module.resolution.fail=Resolution failed: {0}
err.no.moduleToHash=No hashes recorded: no module matching {0} found to record hashes
err.invalid.date=--date {0} is not a valid ISO-8601 extended offset date-time with optional time-zone format: {1}
err.date.out.of.range=--date {0} is out of the valid range 1980-01-01T00:00:02Z to 2099-12-31T23:59:59Z
err.compress.incorrect=--compress value is invalid: {0}
err.compress.wrong.mode=--compress is only accepted with create mode
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}
Expand Down
97 changes: 96 additions & 1 deletion test/jdk/tools/jmod/JmodTest.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
Expand Down Expand Up @@ -739,6 +739,101 @@ public void testTmpFileRemoved() throws IOException {
});
}

@Test
public void testCompressionLevel() throws IOException {
String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
Path jmod = MODS_DIR.resolve("foo.jmod");
FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "zip-0",
jmod.toString())
.assertSuccess();

jmod("list",
"--compress", "zip-0",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress is only accepted with create mode"), "Error message printed");
});

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "zip-9",
jmod.toString())
.assertSuccess();

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "zip--1",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
});

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "zip-1-something",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
});

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "zip-10",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
});

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "zip-",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
});

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "test",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
});

FileUtils.deleteFileIfExistsWithRetry(jmod);

jmod("create",
"--class-path", cp,
"--compress", "test-0",
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid"), "Error message printed");
});
}

// ---

static boolean compileModule(String name, Path dest) throws IOException {
Expand Down

1 comment on commit 43f7f47

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.