Skip to content

Commit 8319375

Browse files
committed
Exit the jlink on modified files by default
This is configurable so add tests for both scenarios.
1 parent 54efc3a commit 8319375

File tree

11 files changed

+278
-77
lines changed

11 files changed

+278
-77
lines changed

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Jlink.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import java.lang.module.Configuration;
2828
import java.lang.module.ModuleFinder;
29-
import java.nio.ByteOrder;
3029
import java.nio.file.Path;
3130
import java.util.ArrayList;
3231
import java.util.Collections;
@@ -149,6 +148,7 @@ public static final class JlinkConfiguration {
149148
private final Set<String> modules;
150149
private final ModuleFinder finder;
151150
private final boolean useModulePath;
151+
private final boolean failOnMod;
152152

153153
/**
154154
* jlink configuration,
@@ -160,11 +160,13 @@ public static final class JlinkConfiguration {
160160
public JlinkConfiguration(Path output,
161161
Set<String> modules,
162162
ModuleFinder finder,
163-
boolean useModulePath) {
163+
boolean useModulePath,
164+
boolean failOnMod) {
164165
this.output = output;
165166
this.modules = Objects.requireNonNull(modules);
166167
this.finder = finder;
167168
this.useModulePath = useModulePath;
169+
this.failOnMod = failOnMod;
168170
}
169171

170172
/**
@@ -193,6 +195,10 @@ public boolean useModulePath() {
193195
return useModulePath;
194196
}
195197

198+
public boolean failOnMod() {
199+
return failOnMod;
200+
}
201+
196202
/**
197203
* Returns a {@link Configuration} of the given module path,
198204
* root modules with full service binding.

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,11 @@ public class JlinkTask {
183183
}, true, "--full-version"),
184184
new Option<JlinkTask>(false, (task, opt, arg) -> {
185185
task.options.ignoreSigning = true;
186-
}, "--ignore-signing-information"),};
186+
}, "--ignore-signing-information"),
187+
new Option<JlinkTask>(false, (task, opt, arg) -> {
188+
task.options.runImageOnlyWarning = true;
189+
}, "--run-image-only-warnings"),};
190+
187191

188192
private static final String PROGNAME = "jlink";
189193
private final OptionsValues options = new OptionsValues();
@@ -223,6 +227,7 @@ static class OptionsValues {
223227
boolean ignoreSigning = false;
224228
boolean bindServices = false;
225229
boolean suggestProviders = false;
230+
boolean runImageOnlyWarning = false;
226231
}
227232

228233
public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options";
@@ -392,7 +397,8 @@ private JlinkConfiguration initJlinkConfig(boolean useModulePath) throws BadArgs
392397
return new JlinkConfiguration(options.output,
393398
roots,
394399
finder,
395-
useModulePath);
400+
useModulePath,
401+
!options.runImageOnlyWarning);
396402
}
397403

398404
private void createImage(JlinkConfiguration config) throws Exception {
@@ -582,7 +588,8 @@ private static ImageHelper createImageProvider(JlinkConfiguration config,
582588
targetPlatform.arch().byteOrder(), targetPlatform);
583589
}
584590
}
585-
return new ImageHelper(cf, mods, targetPlatform, retainModulesPath, ignoreSigning, config.useModulePath());
591+
return new ImageHelper(cf, mods, targetPlatform, retainModulesPath,
592+
ignoreSigning, config.useModulePath(), config.failOnMod());
586593
}
587594

588595
/*
@@ -853,17 +860,20 @@ private static class ImageHelper implements ImageProvider {
853860
final boolean ignoreSigning;
854861
final Runtime.Version version;
855862
final Set<Archive> archives;
863+
final boolean failOnMod;
856864

857865
ImageHelper(Configuration cf,
858866
Map<String, Path> modsPaths,
859867
Platform targetPlatform,
860868
Path packagedModulesPath,
861869
boolean ignoreSigning,
862-
boolean useModulePath) throws IOException {
870+
boolean useModulePath,
871+
boolean failOnMod) throws IOException {
863872
Objects.requireNonNull(targetPlatform);
864873
this.targetPlatform = targetPlatform;
865874
this.packagedModulesPath = packagedModulesPath;
866875
this.ignoreSigning = ignoreSigning;
876+
this.failOnMod = failOnMod;
867877

868878
// use the version of java.base module, if present, as
869879
// the release version for multi-release JAR files
@@ -919,7 +929,7 @@ private Archive newArchive(String module, Path path, boolean useModulePath) {
919929
}
920930
} else if (ModuleFinder.ofSystem().find(module).isPresent()){
921931
// the path is a JRTPath, when using a jmod-less image
922-
return new JmodLessArchive(module, path);
932+
return new JmodLessArchive(module, path, failOnMod);
923933
} else {
924934
throw new IllegalArgumentException(
925935
taskHelper.getMessage("err.not.modular.format", module, path));

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JmodLessArchive.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,16 @@ public class JmodLessArchive implements Archive {
5353
private final Path path;
5454
private final ModuleReference ref;
5555
private final List<JmodLessFile> files = new ArrayList<>();
56+
private final boolean failOnMod;
5657

57-
JmodLessArchive(String module, Path path) {
58+
JmodLessArchive(String module, Path path, boolean failOnMod) {
5859
this.module = module;
5960
this.path = path;
6061
this.ref = ModuleFinder.ofSystem()
6162
.find(module)
6263
.orElseThrow(() ->
6364
new IllegalArgumentException("Module " + module + " not part of the JDK install"));
65+
this.failOnMod = failOnMod;
6466
}
6567

6668
@Override
@@ -121,7 +123,7 @@ private void collectFiles() throws IOException {
121123
// Add classes/resources from image module
122124
files.addAll(ref.open().list().map(s -> {
123125
return new JmodLessFile(JmodLessArchive.this, s,
124-
Type.CLASS_OR_RESOURCE, null /* sha */, false /* symlink */);
126+
Type.CLASS_OR_RESOURCE, null /* sha */, false /* symlink */, failOnMod);
125127
}).collect(Collectors.toList()));
126128
}
127129
}
@@ -136,7 +138,7 @@ private void addNonClassResources() throws IOException {
136138
files.addAll(Arrays.asList(input.split("\n")).stream()
137139
.map(s -> {
138140
TypePathMapping m = mappingResource(s);
139-
return new JmodLessFile(JmodLessArchive.this, m.resPath, m.resType, m.sha, m.symlink);
141+
return new JmodLessFile(JmodLessArchive.this, m.resPath, m.resType, m.sha, m.symlink, failOnMod);
140142
})
141143
.filter(m -> m != null)
142144
.collect(Collectors.toList()));
@@ -193,13 +195,15 @@ static class JmodLessFile {
193195
final Archive archive;
194196
final String sha; // Checksum for non-resource files
195197
final boolean symlink;
198+
final boolean failOnMod;
196199

197-
JmodLessFile(Archive archive, String resPath, Type resType, String sha, boolean symlink) {
200+
JmodLessFile(Archive archive, String resPath, Type resType, String sha, boolean symlink, boolean failOnMod) {
198201
this.resPath = resPath;
199202
this.resType = toEntryType(resType);
200203
this.archive = archive;
201204
this.sha = sha;
202205
this.symlink = symlink;
206+
this.failOnMod = failOnMod;
203207
}
204208

205209
Entry toEntry() {
@@ -234,9 +238,15 @@ public InputStream stream() throws IOException {
234238
if (resType != Archive.Entry.EntryType.CLASS_OR_RESOURCE) {
235239
// Read from the base JDK image.
236240
Path path = BASE.resolve(resPath);
237-
if (shaSumMismatch(path, sha, symlink) && !warningProduced) {
238-
System.err.printf("WARNING: %s has been modified. Please double check!%n", path.toString());
239-
warningProduced = true;
241+
if (shaSumMismatch(path, sha, symlink)) {
242+
String msg = String.format("%s has been modified. Please double check!%n", path.toString());
243+
if (failOnMod) {
244+
IllegalStateException ise = new IllegalStateException(msg);
245+
throw new RunImageLinkException(ise);
246+
} else if (!warningProduced) {
247+
System.err.printf("WARNING: %s", msg);
248+
warningProduced = true;
249+
}
240250
}
241251
if (symlink) {
242252
path = BASE.resolve(sha);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2023, Red Hat, Inc.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
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.jlink.internal;
27+
28+
/**
29+
* Exception thrown for links without packaged modules. I.e. run-image link.
30+
*
31+
*/
32+
public class RunImageLinkException extends IllegalStateException {
33+
34+
private static final long serialVersionUID = -1848914673073119403L;
35+
36+
public RunImageLinkException(Throwable cause) {
37+
super(cause);
38+
}
39+
40+
}

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AddJmodResourcesPlugin.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import jdk.internal.util.OperatingSystem;
4242
import jdk.tools.jlink.internal.Platform;
43+
import jdk.tools.jlink.internal.RunImageLinkException;
4344
import jdk.tools.jlink.plugin.ResourcePool;
4445
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
4546
import jdk.tools.jlink.plugin.ResourcePoolEntry;
@@ -162,6 +163,9 @@ private String computeSha512(ResourcePoolEntry entry, Platform platform) {
162163
HexFormat format = HexFormat.of();
163164
return format.formatHex(db);
164165
}
166+
} catch (RunImageLinkException e) {
167+
// JmodLessArchive::JmodLessFile.content() may throw this on sha mismatch
168+
throw (RuntimeException)e.getCause();
165169
} catch (Exception e) {
166170
throw new AssertionError("Failed to generate hash sum for " + entry.path());
167171
}

src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ main.opt.ignore-signing-information=\
8989
\ signed modular JARs are not copied to\n\
9090
\ the runtime image.
9191

92+
main.opt.run-image-only-warnings=\
93+
\ --run-image-only-warnings Only produce a warning for a modified\n\
94+
\ file taken from the current run-image\n\
95+
\ in a link using only the JDK run-image.\n\
96+
\ Default is a fatal error.
97+
9298
main.opt.verbose=\
9399
\ -v, --verbose Enable verbose tracing
94100

test/jdk/tools/jlink/IntegrationTest.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.io.FileReader;
2626
import java.io.IOException;
2727
import java.io.UncheckedIOException;
28-
import java.nio.ByteOrder;
2928
import java.nio.file.Files;
3029
import java.nio.file.Path;
3130
import java.nio.file.Paths;
@@ -38,20 +37,18 @@
3837
import java.util.Properties;
3938
import java.util.Set;
4039
import java.util.function.Function;
41-
import jdk.tools.jlink.internal.Jlink;
42-
import jdk.tools.jlink.internal.JlinkTask;
40+
4341
import jdk.tools.jlink.builder.DefaultImageBuilder;
44-
import jdk.tools.jlink.internal.Platform;
45-
import jdk.tools.jlink.plugin.ResourcePool;
46-
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
47-
import jdk.tools.jlink.plugin.Plugin;
4842
import jdk.tools.jlink.internal.ExecutableImage;
43+
import jdk.tools.jlink.internal.Jlink;
4944
import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
5045
import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
46+
import jdk.tools.jlink.internal.JlinkTask;
47+
import jdk.tools.jlink.internal.Platform;
5148
import jdk.tools.jlink.internal.PostProcessor;
52-
import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
53-
import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin;
54-
49+
import jdk.tools.jlink.plugin.Plugin;
50+
import jdk.tools.jlink.plugin.ResourcePool;
51+
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
5552
import tests.Helper;
5653
import tests.JImageGenerator;
5754

@@ -166,7 +163,7 @@ private static void test() throws Exception {
166163
limits.add("java.management");
167164
JlinkConfiguration config = new Jlink.JlinkConfiguration(output,
168165
mods,
169-
JlinkTask.newModuleFinder(modulePaths, limits, mods, true), true);
166+
JlinkTask.newModuleFinder(modulePaths, limits, mods, true), true, false);
170167

171168
List<Plugin> lst = new ArrayList<>();
172169

test/jdk/tools/jlink/JmodLess/AbstractJmodLessTest.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Collections;
2929
import java.util.List;
3030
import java.util.Scanner;
31+
import java.util.function.Predicate;
3132
import java.util.stream.Collectors;
3233

3334
import jdk.test.lib.process.OutputAnalyzer;
@@ -123,6 +124,10 @@ protected Path jlinkUsingImage(JlinkSpec spec) throws Exception {
123124
}
124125

125126
protected Path jlinkUsingImage(JlinkSpec spec, OutputAnalyzerHandler handler) throws Exception {
127+
return jlinkUsingImage(spec, handler, new DefaultSuccessExitPredicate());
128+
}
129+
130+
protected Path jlinkUsingImage(JlinkSpec spec, OutputAnalyzerHandler handler, Predicate<OutputAnalyzer> exitChecker) throws Exception {
126131
String jmodLessGeneratedImage = "target-jmodless-" + spec.getName();
127132
Path targetImageDir = spec.getHelper().createNewImageDir(jmodLessGeneratedImage);
128133
Path targetJlink = spec.getImageToUse().resolve("bin").resolve(getJlink());
@@ -146,23 +151,26 @@ protected Path jlinkUsingImage(JlinkSpec spec, OutputAnalyzerHandler handler) th
146151
} catch (Throwable t) {
147152
throw new AssertionError("Executing process failed!", t);
148153
}
149-
if (analyzer.getExitValue() != 0) {
154+
if (!exitChecker.test(analyzer)) {
150155
if (DEBUG) {
151156
System.err.println("Process stdout was: ");
152157
System.err.println(analyzer.getStdout());
153158
System.err.println("Process stderr was: ");
154159
System.err.println(analyzer.getStderr());
155160
}
156-
throw new AssertionError("Expected jlink to pass given a jmodless image");
161+
throw new AssertionError("Expected jlink to pass/fail given a jmodless image. Exit code was: " + analyzer.getExitValue());
157162
}
158163
handler.handleAnalyzer(analyzer); // Give tests a chance to process in/output
159164

160-
// validate the resulting image; Includes running 'java -version'
161-
JImageValidator validator = new JImageValidator(spec.getValidatingModule(), spec.getExpectedLocations(),
162-
targetImageDir.toFile(), spec.getUnexpectedLocations(), Collections.emptyList(), spec.getExpectedFiles());
163-
validator.validate(); // This doesn't validate locations
164-
if (!spec.getExpectedLocations().isEmpty() || !spec.getUnexpectedLocations().isEmpty()) {
165-
JImageValidator.validate(targetImageDir.resolve("lib").resolve("modules"), spec.getExpectedLocations(), spec.getUnexpectedLocations());
165+
// validate the resulting image; Includes running 'java -version', only do this
166+
// if the jlink succeeded.
167+
if (analyzer.getExitValue() == 0) {
168+
JImageValidator validator = new JImageValidator(spec.getValidatingModule(), spec.getExpectedLocations(),
169+
targetImageDir.toFile(), spec.getUnexpectedLocations(), Collections.emptyList(), spec.getExpectedFiles());
170+
validator.validate(); // This doesn't validate locations
171+
if (!spec.getExpectedLocations().isEmpty() || !spec.getUnexpectedLocations().isEmpty()) {
172+
JImageValidator.validate(targetImageDir.resolve("lib").resolve("modules"), spec.getExpectedLocations(), spec.getUnexpectedLocations());
173+
}
166174
}
167175
return targetImageDir;
168176
}
@@ -456,4 +464,13 @@ public void handleAnalyzer(OutputAnalyzer out) {
456464
}
457465

458466
}
467+
468+
static class DefaultSuccessExitPredicate implements Predicate<OutputAnalyzer> {
469+
470+
@Override
471+
public boolean test(OutputAnalyzer t) {
472+
return t.getExitValue() == 0;
473+
}
474+
475+
}
459476
}

0 commit comments

Comments
 (0)