Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8293499: Provide jmod --compress option #10213

Closed
Closed
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 compLevel) {
this.zos = new ZipOutputStream(out);
this.zos.setLevel(compLevel);
this.date = date;
try {
JmodFile.writeMagicNumber(out);
50 changes: 47 additions & 3 deletions src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
Original file line number Diff line number Diff line change
@@ -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
@@ -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;
@@ -167,6 +168,7 @@ static class Options {
List<PathMatcher> excludes;
Path extractDir;
LocalDateTime date;
int compressLevel;
}

// Valid --date range
@@ -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);
@@ -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())) {
@@ -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>
{
@@ -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();

@@ -1488,6 +1522,16 @@ 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 fast compression.
options.compressLevel = Deflater.DEFAULT_COMPRESSION;
}

if (options.mode.equals(Mode.HASH)) {
if (options.moduleFinder == null || options.modulesToHash == null)
Original file line number Diff line number Diff line change
@@ -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.

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

err.missing.mode=one of create, extract, list, describe, or hash must be specified
@@ -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}
86 changes: 85 additions & 1 deletion test/jdk/tools/jmod/JmodTest.java
Original file line number Diff line number Diff line change
@@ -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
@@ -739,6 +739,90 @@ 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", "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 {