8311302: Implement JEP 493: Linking Run-Time Images without JMODs
Co-authored-by: Mandy Chung <mchung@openjdk.org> Reviewed-by: mchung, alanb, erikj, ihse
This commit is contained in:
parent
f3ba767604
commit
2ec358082f
@ -96,6 +96,10 @@ JLINK_DISABLE_WARNINGS := | ( $(GREP) -v -e "WARNING: Using incubator module" ||
|
|||||||
JDK_IMAGE_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/images/jdk
|
JDK_IMAGE_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/images/jdk
|
||||||
JRE_IMAGE_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/images/jre
|
JRE_IMAGE_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/images/jre
|
||||||
|
|
||||||
|
ifeq ($(JLINK_PRODUCE_LINKABLE_RUNTIME), true)
|
||||||
|
JLINK_JDK_EXTRA_OPTS += --generate-linkable-runtime
|
||||||
|
endif
|
||||||
|
|
||||||
$(eval $(call SetupExecute, jlink_jdk, \
|
$(eval $(call SetupExecute, jlink_jdk, \
|
||||||
WARN := Creating jdk image, \
|
WARN := Creating jdk image, \
|
||||||
DEPS := $(JDK_JMODS) $(BASE_RELEASE_FILE) \
|
DEPS := $(JDK_JMODS) $(BASE_RELEASE_FILE) \
|
||||||
|
@ -586,13 +586,42 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JMOD_OPTIONS],
|
|||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# jlink options.
|
# jlink options.
|
||||||
# We always keep packaged modules in JDK image.
|
|
||||||
#
|
#
|
||||||
AC_DEFUN_ONCE([JDKOPT_SETUP_JLINK_OPTIONS],
|
AC_DEFUN_ONCE([JDKOPT_SETUP_JLINK_OPTIONS],
|
||||||
[
|
[
|
||||||
UTIL_ARG_ENABLE(NAME: keep-packaged-modules, DEFAULT: true,
|
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Configure option for building a JDK that is suitable for linking from the
|
||||||
|
# run-time image without JMODs.
|
||||||
|
#
|
||||||
|
# Determines whether or not a suitable run-time image is being produced from
|
||||||
|
# packaged modules. If set to 'true, changes the *default* of packaged
|
||||||
|
# modules to 'false'.
|
||||||
|
#
|
||||||
|
UTIL_ARG_ENABLE(NAME: linkable-runtime, DEFAULT: false,
|
||||||
|
RESULT: JLINK_PRODUCE_LINKABLE_RUNTIME,
|
||||||
|
DESC: [enable a JDK build suitable for linking from the run-time image],
|
||||||
|
CHECKING_MSG: [whether or not a JDK suitable for linking from the run-time image should be produced])
|
||||||
|
AC_SUBST(JLINK_PRODUCE_LINKABLE_RUNTIME)
|
||||||
|
|
||||||
|
if test "x$JLINK_PRODUCE_LINKABLE_RUNTIME" = xtrue; then
|
||||||
|
DEFAULT_PACKAGED_MODULES=false
|
||||||
|
else
|
||||||
|
DEFAULT_PACKAGED_MODULES=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Configure option for packaged modules
|
||||||
|
#
|
||||||
|
# We keep packaged modules in the JDK image unless --enable-linkable-runtime is
|
||||||
|
# requested.
|
||||||
|
#
|
||||||
|
UTIL_ARG_ENABLE(NAME: keep-packaged-modules, DEFAULT: $DEFAULT_PACKAGED_MODULES,
|
||||||
RESULT: JLINK_KEEP_PACKAGED_MODULES,
|
RESULT: JLINK_KEEP_PACKAGED_MODULES,
|
||||||
DESC: [enable keeping of packaged modules in jdk image],
|
DESC: [enable keeping of packaged modules in jdk image],
|
||||||
|
DEFAULT_DESC: [enabled by default unless --enable-linkable-runtime is set],
|
||||||
CHECKING_MSG: [if packaged modules are kept])
|
CHECKING_MSG: [if packaged modules are kept])
|
||||||
AC_SUBST(JLINK_KEEP_PACKAGED_MODULES)
|
AC_SUBST(JLINK_KEEP_PACKAGED_MODULES)
|
||||||
])
|
])
|
||||||
|
@ -706,6 +706,7 @@ NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) $(JAVADOC_MAIN_CLASS)
|
|||||||
|
|
||||||
JMOD_COMPRESS := @JMOD_COMPRESS@
|
JMOD_COMPRESS := @JMOD_COMPRESS@
|
||||||
JLINK_KEEP_PACKAGED_MODULES := @JLINK_KEEP_PACKAGED_MODULES@
|
JLINK_KEEP_PACKAGED_MODULES := @JLINK_KEEP_PACKAGED_MODULES@
|
||||||
|
JLINK_PRODUCE_LINKABLE_RUNTIME := @JLINK_PRODUCE_LINKABLE_RUNTIME@
|
||||||
|
|
||||||
RCFLAGS := @RCFLAGS@
|
RCFLAGS := @RCFLAGS@
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -24,29 +24,44 @@
|
|||||||
*/
|
*/
|
||||||
package jdk.tools.jlink.internal;
|
package jdk.tools.jlink.internal;
|
||||||
|
|
||||||
|
import static jdk.tools.jlink.internal.LinkableRuntimeImage.DIFF_PATTERN;
|
||||||
|
import static jdk.tools.jlink.internal.LinkableRuntimeImage.RESPATH_PATTERN;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.tools.jlink.internal.Archive.Entry;
|
import jdk.tools.jlink.internal.Archive.Entry;
|
||||||
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
||||||
|
import jdk.tools.jlink.internal.JRTArchive.ResourceFileEntry;
|
||||||
import jdk.tools.jlink.internal.ResourcePoolManager.CompressedModuleData;
|
import jdk.tools.jlink.internal.ResourcePoolManager.CompressedModuleData;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator.ImageResource;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.ResourcePoolReader;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException;
|
||||||
import jdk.tools.jlink.plugin.PluginException;
|
import jdk.tools.jlink.plugin.PluginException;
|
||||||
import jdk.tools.jlink.plugin.ResourcePool;
|
import jdk.tools.jlink.plugin.ResourcePool;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||||
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePoolModule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An image (native endian.)
|
* An image (native endian.)
|
||||||
@ -68,38 +83,61 @@ import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
public final class ImageFileCreator {
|
public final class ImageFileCreator {
|
||||||
|
private static final byte[] EMPTY_RESOURCE_BYTES = new byte[] {};
|
||||||
|
|
||||||
|
private static final String JLINK_MOD_NAME = "jdk.jlink";
|
||||||
|
private static final String RESPATH = "/" + JLINK_MOD_NAME + "/" + RESPATH_PATTERN;
|
||||||
|
private static final String DIFF_PATH = "/" + JLINK_MOD_NAME + "/" + DIFF_PATTERN;
|
||||||
private final Map<String, List<Entry>> entriesForModule = new HashMap<>();
|
private final Map<String, List<Entry>> entriesForModule = new HashMap<>();
|
||||||
private final ImagePluginStack plugins;
|
private final ImagePluginStack plugins;
|
||||||
private ImageFileCreator(ImagePluginStack plugins) {
|
private final boolean generateRuntimeImage;
|
||||||
|
private final TaskHelper helper;
|
||||||
|
|
||||||
|
private ImageFileCreator(ImagePluginStack plugins,
|
||||||
|
boolean generateRuntimeImage,
|
||||||
|
TaskHelper taskHelper) {
|
||||||
this.plugins = Objects.requireNonNull(plugins);
|
this.plugins = Objects.requireNonNull(plugins);
|
||||||
|
this.generateRuntimeImage = generateRuntimeImage;
|
||||||
|
this.helper = taskHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExecutableImage create(Set<Archive> archives,
|
/**
|
||||||
ImagePluginStack plugins)
|
* Create an executable image based on a set of input archives and a given
|
||||||
throws IOException {
|
* plugin stack for a given byte order. It optionally generates a runtime
|
||||||
return ImageFileCreator.create(archives, ByteOrder.nativeOrder(),
|
* that can be used for linking from the run-time image if
|
||||||
plugins);
|
* {@code generateRuntimeImage} is set to {@code true}.
|
||||||
}
|
*
|
||||||
|
* @param archives The set of input archives
|
||||||
public static ExecutableImage create(Set<Archive> archives,
|
* @param byteOrder The desired byte order of the result
|
||||||
ByteOrder byteOrder)
|
* @param plugins The plugin stack to apply to the input
|
||||||
throws IOException {
|
* @param generateRuntimeImage if a runtime suitable for linking from the
|
||||||
return ImageFileCreator.create(archives, byteOrder,
|
* run-time image should get created.
|
||||||
new ImagePluginStack());
|
* @return The executable image.
|
||||||
}
|
* @throws IOException
|
||||||
|
*/
|
||||||
public static ExecutableImage create(Set<Archive> archives,
|
public static ExecutableImage create(Set<Archive> archives,
|
||||||
ByteOrder byteOrder,
|
ByteOrder byteOrder,
|
||||||
ImagePluginStack plugins)
|
ImagePluginStack plugins,
|
||||||
|
boolean generateRuntimeImage,
|
||||||
|
TaskHelper taskHelper)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
ImageFileCreator image = new ImageFileCreator(plugins);
|
ImageFileCreator image = new ImageFileCreator(plugins,
|
||||||
|
generateRuntimeImage,
|
||||||
|
taskHelper);
|
||||||
try {
|
try {
|
||||||
image.readAllEntries(archives);
|
image.readAllEntries(archives);
|
||||||
// write to modular image
|
// write to modular image
|
||||||
image.writeImage(archives, byteOrder);
|
image.writeImage(archives, byteOrder);
|
||||||
|
} catch (RuntimeImageLinkException e) {
|
||||||
|
// readAllEntries() might throw this exception.
|
||||||
|
// Propagate as IOException with appropriate message for
|
||||||
|
// jlink runs from the run-time image. This handles better
|
||||||
|
// error messages for the case of modified files in the run-time
|
||||||
|
// image.
|
||||||
|
throw image.newIOException(e);
|
||||||
} finally {
|
} finally {
|
||||||
//Close all archives
|
// Close all archives
|
||||||
for (Archive a : archives) {
|
for (Archive a : archives) {
|
||||||
a.close();
|
a.close();
|
||||||
}
|
}
|
||||||
@ -125,7 +163,8 @@ public final class ImageFileCreator {
|
|||||||
|
|
||||||
public static void recreateJimage(Path jimageFile,
|
public static void recreateJimage(Path jimageFile,
|
||||||
Set<Archive> archives,
|
Set<Archive> archives,
|
||||||
ImagePluginStack pluginSupport)
|
ImagePluginStack pluginSupport,
|
||||||
|
boolean generateRuntimeImage)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try {
|
try {
|
||||||
Map<String, List<Entry>> entriesForModule
|
Map<String, List<Entry>> entriesForModule
|
||||||
@ -142,7 +181,7 @@ public final class ImageFileCreator {
|
|||||||
try (OutputStream fos = Files.newOutputStream(jimageFile);
|
try (OutputStream fos = Files.newOutputStream(jimageFile);
|
||||||
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||||
DataOutputStream out = new DataOutputStream(bos)) {
|
DataOutputStream out = new DataOutputStream(bos)) {
|
||||||
generateJImage(pool, writer, pluginSupport, out);
|
generateJImage(pool, writer, pluginSupport, out, generateRuntimeImage);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
//Close all archives
|
//Close all archives
|
||||||
@ -158,9 +197,14 @@ public final class ImageFileCreator {
|
|||||||
BasicImageWriter writer = new BasicImageWriter(byteOrder);
|
BasicImageWriter writer = new BasicImageWriter(byteOrder);
|
||||||
ResourcePoolManager allContent = createPoolManager(archives,
|
ResourcePoolManager allContent = createPoolManager(archives,
|
||||||
entriesForModule, byteOrder, writer);
|
entriesForModule, byteOrder, writer);
|
||||||
ResourcePool result;
|
ResourcePool result = null;
|
||||||
try (DataOutputStream out = plugins.getJImageFileOutputStream()) {
|
try (DataOutputStream out = plugins.getJImageFileOutputStream()) {
|
||||||
result = generateJImage(allContent, writer, plugins, out);
|
result = generateJImage(allContent, writer, plugins, out, generateRuntimeImage);
|
||||||
|
} catch (RuntimeImageLinkException e) {
|
||||||
|
// Propagate as IOException with appropriate message for
|
||||||
|
// jlink runs from the run-time image. This handles better
|
||||||
|
// error messages for the case of --patch-module.
|
||||||
|
throw newIOException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle files.
|
//Handle files.
|
||||||
@ -174,14 +218,53 @@ public final class ImageFileCreator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IOException newIOException(RuntimeImageLinkException e) throws IOException {
|
||||||
|
if (JlinkTask.DEBUG) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
String message = switch (e.getReason()) {
|
||||||
|
case PATCH_MODULE -> helper.getMessage("err.runtime.link.patched.module", e.getFile());
|
||||||
|
case MODIFIED_FILE -> helper.getMessage("err.runtime.link.modified.file", e.getFile());
|
||||||
|
default -> throw new AssertionError("Unexpected value: " + e.getReason());
|
||||||
|
};
|
||||||
|
throw new IOException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a jimage based on content of the given ResourcePoolManager,
|
||||||
|
* optionally creating a runtime that can be used for linking from the
|
||||||
|
* run-time image
|
||||||
|
*
|
||||||
|
* @param allContent The content that needs to get added to the resulting
|
||||||
|
* lib/modules (jimage) file.
|
||||||
|
* @param writer The writer for the jimage file.
|
||||||
|
* @param pluginSupport The stack of all plugins to apply.
|
||||||
|
* @param out The output stream to write the jimage to.
|
||||||
|
* @param generateRuntimeImage if a runtime suitable for linking from the
|
||||||
|
* run-time image should get created.
|
||||||
|
* @return A pool of the actual result resources.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
private static ResourcePool generateJImage(ResourcePoolManager allContent,
|
private static ResourcePool generateJImage(ResourcePoolManager allContent,
|
||||||
BasicImageWriter writer,
|
BasicImageWriter writer,
|
||||||
ImagePluginStack pluginSupport,
|
ImagePluginStack pluginSupport,
|
||||||
DataOutputStream out
|
DataOutputStream out,
|
||||||
|
boolean generateRuntimeImage
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
ResourcePool resultResources;
|
ResourcePool resultResources;
|
||||||
try {
|
try {
|
||||||
resultResources = pluginSupport.visitResources(allContent);
|
resultResources = pluginSupport.visitResources(allContent);
|
||||||
|
if (generateRuntimeImage) {
|
||||||
|
// Keep track of non-modules resources for linking from a run-time image
|
||||||
|
resultResources = addNonClassResourcesTrackFiles(resultResources,
|
||||||
|
writer);
|
||||||
|
// Generate the diff between the input resources from packaged
|
||||||
|
// modules in 'allContent' to the plugin- or otherwise
|
||||||
|
// generated-content in 'resultResources'
|
||||||
|
resultResources = addResourceDiffFiles(allContent.resourcePool(),
|
||||||
|
resultResources,
|
||||||
|
writer);
|
||||||
|
}
|
||||||
} catch (PluginException pe) {
|
} catch (PluginException pe) {
|
||||||
if (JlinkTask.DEBUG) {
|
if (JlinkTask.DEBUG) {
|
||||||
pe.printStackTrace();
|
pe.printStackTrace();
|
||||||
@ -198,7 +281,7 @@ public final class ImageFileCreator {
|
|||||||
|
|
||||||
List<ResourcePoolEntry> content = new ArrayList<>();
|
List<ResourcePoolEntry> content = new ArrayList<>();
|
||||||
List<String> paths = new ArrayList<>();
|
List<String> paths = new ArrayList<>();
|
||||||
// the order of traversing the resources and the order of
|
// the order of traversing the resources and the order of
|
||||||
// the module content being written must be the same
|
// the module content being written must be the same
|
||||||
resultResources.entries().forEach(res -> {
|
resultResources.entries().forEach(res -> {
|
||||||
if (res.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
|
if (res.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
|
||||||
@ -248,11 +331,225 @@ public final class ImageFileCreator {
|
|||||||
return resultResources;
|
return resultResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for creating a runtime suitable for linking from the run-time
|
||||||
|
* image.
|
||||||
|
*
|
||||||
|
* Generates differences between the packaged modules "view" in
|
||||||
|
* {@code jmodContent} to the optimized image in {@code resultContent} and
|
||||||
|
* adds the result to the returned resource pool.
|
||||||
|
*
|
||||||
|
* @param jmodContent The resource pool view of packaged modules
|
||||||
|
* @param resultContent The optimized result generated from the jmodContent
|
||||||
|
* input by applying the plugin stack.
|
||||||
|
* @param writer The image writer.
|
||||||
|
* @return The resource pool with the difference file resources added to
|
||||||
|
* the {@code resultContent}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("try")
|
||||||
|
private static ResourcePool addResourceDiffFiles(ResourcePool jmodContent,
|
||||||
|
ResourcePool resultContent,
|
||||||
|
BasicImageWriter writer) {
|
||||||
|
JimageDiffGenerator generator = new JimageDiffGenerator();
|
||||||
|
List<ResourceDiff> diff;
|
||||||
|
try (ImageResource jmods = new ResourcePoolReader(jmodContent);
|
||||||
|
ImageResource jimage = new ResourcePoolReader(resultContent)) {
|
||||||
|
diff = generator.generateDiff(jmods, jimage);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError("Failed to generate the runtime image diff", e);
|
||||||
|
}
|
||||||
|
Set<String> modules = resultContent.moduleView().modules()
|
||||||
|
.map(a -> a.name())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
// Add resource diffs for the resource files we are about to add
|
||||||
|
modules.stream().forEach(m -> {
|
||||||
|
String resourceName = String.format(DIFF_PATH, m);
|
||||||
|
ResourceDiff.Builder builder = new ResourceDiff.Builder();
|
||||||
|
ResourceDiff d = builder.setKind(ResourceDiff.Kind.ADDED)
|
||||||
|
.setName(resourceName)
|
||||||
|
.build();
|
||||||
|
diff.add(d);
|
||||||
|
});
|
||||||
|
Map<String, List<ResourceDiff>> perModDiffs = preparePerModuleDiffs(diff,
|
||||||
|
modules);
|
||||||
|
return addDiffResourcesFiles(modules, perModDiffs, resultContent, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, List<ResourceDiff>> preparePerModuleDiffs(List<ResourceDiff> resDiffs,
|
||||||
|
Set<String> modules) {
|
||||||
|
Map<String, List<ResourceDiff>> modToDiff = new HashMap<>();
|
||||||
|
resDiffs.forEach(d -> {
|
||||||
|
int secondSlash = d.getName().indexOf("/", 1);
|
||||||
|
if (secondSlash == -1) {
|
||||||
|
throw new AssertionError("Module name not present");
|
||||||
|
}
|
||||||
|
String module = d.getName().substring(1, secondSlash);
|
||||||
|
List<ResourceDiff> perModDiff = modToDiff.computeIfAbsent(module,
|
||||||
|
a -> new ArrayList<>());
|
||||||
|
perModDiff.add(d);
|
||||||
|
});
|
||||||
|
Map<String, List<ResourceDiff>> allModsToDiff = new HashMap<>();
|
||||||
|
modules.stream().forEach(m -> {
|
||||||
|
List<ResourceDiff> d = modToDiff.get(m);
|
||||||
|
if (d == null) {
|
||||||
|
// Not all modules will have a diff
|
||||||
|
allModsToDiff.put(m, Collections.emptyList());
|
||||||
|
} else {
|
||||||
|
allModsToDiff.put(m, d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allModsToDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourcePool addDiffResourcesFiles(Set<String> modules,
|
||||||
|
Map<String, List<ResourceDiff>> perModDiffs,
|
||||||
|
ResourcePool resultResources,
|
||||||
|
BasicImageWriter writer) {
|
||||||
|
ResourcePoolManager mgr = createPoolManager(resultResources, writer);
|
||||||
|
ResourcePoolBuilder out = mgr.resourcePoolBuilder();
|
||||||
|
modules.stream().sorted().forEach(module -> {
|
||||||
|
String mResource = String.format(DIFF_PATH, module);
|
||||||
|
List<ResourceDiff> diff = perModDiffs.get(module);
|
||||||
|
// Note that for modules without diff to the packaged modules view
|
||||||
|
// we create resource diff files with just the header and no content.
|
||||||
|
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ResourceDiff.write(diff, bout);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError("Failed to write resource diff file" +
|
||||||
|
" for module " + module, e);
|
||||||
|
}
|
||||||
|
out.add(ResourcePoolEntry.create(mResource, bout.toByteArray()));
|
||||||
|
});
|
||||||
|
return out.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for creating runtimes that can be used for linking from the
|
||||||
|
* run-time image. Adds meta-data files for resources not in the lib/modules
|
||||||
|
* file of the JDK. That is, mapping files for which on-disk files belong to
|
||||||
|
* which module.
|
||||||
|
*
|
||||||
|
* @param resultResources
|
||||||
|
* The original resources which serve as the basis for generating
|
||||||
|
* the meta-data files.
|
||||||
|
* @param writer
|
||||||
|
* The image writer.
|
||||||
|
*
|
||||||
|
* @return An amended resource pool which includes meta-data files.
|
||||||
|
*/
|
||||||
|
private static ResourcePool addNonClassResourcesTrackFiles(ResourcePool resultResources,
|
||||||
|
BasicImageWriter writer) {
|
||||||
|
// Only add resources if jdk.jlink module is present in the target image
|
||||||
|
Optional<ResourcePoolModule> jdkJlink = resultResources.moduleView()
|
||||||
|
.findModule(JLINK_MOD_NAME);
|
||||||
|
if (jdkJlink.isPresent()) {
|
||||||
|
Map<String, List<String>> nonClassResources = recordAndFilterEntries(resultResources);
|
||||||
|
return addModuleResourceEntries(resultResources, nonClassResources, writer);
|
||||||
|
} else {
|
||||||
|
return resultResources; // No-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for creating runtimes that can be used for linking from the
|
||||||
|
* run-time image. Adds the given mapping of files as a meta-data file to
|
||||||
|
* the given resource pool.
|
||||||
|
*
|
||||||
|
* @param resultResources
|
||||||
|
* The resource pool to add files to.
|
||||||
|
* @param nonClassResEntries
|
||||||
|
* The per module mapping for which to create the meta-data files
|
||||||
|
* for.
|
||||||
|
* @param writer
|
||||||
|
* The image writer.
|
||||||
|
*
|
||||||
|
* @return A resource pool with meta-data files added.
|
||||||
|
*/
|
||||||
|
private static ResourcePool addModuleResourceEntries(ResourcePool resultResources,
|
||||||
|
Map<String, List<String>> nonClassResEntries,
|
||||||
|
BasicImageWriter writer) {
|
||||||
|
Set<String> inputModules = resultResources.moduleView().modules()
|
||||||
|
.map(rm -> rm.name())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
ResourcePoolManager mgr = createPoolManager(resultResources, writer);
|
||||||
|
ResourcePoolBuilder out = mgr.resourcePoolBuilder();
|
||||||
|
inputModules.stream().sorted().forEach(module -> {
|
||||||
|
String mResource = String.format(RESPATH, module);
|
||||||
|
List<String> mResources = nonClassResEntries.get(module);
|
||||||
|
if (mResources == null) {
|
||||||
|
// We create empty resource files for modules in the resource
|
||||||
|
// pool view that don't themselves contain native resources
|
||||||
|
// or config files.
|
||||||
|
out.add(ResourcePoolEntry.create(mResource, EMPTY_RESOURCE_BYTES));
|
||||||
|
} else {
|
||||||
|
String mResContent = mResources.stream().sorted()
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
out.add(ResourcePoolEntry.create(mResource,
|
||||||
|
mResContent.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return out.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for creating runtimes that can be used for linking from the
|
||||||
|
* run-time image. Generates a per module mapping of files not part of the
|
||||||
|
* modules image (jimage). This mapping is needed so as to know which files
|
||||||
|
* of the installed JDK belong to which module.
|
||||||
|
*
|
||||||
|
* @param resultResources
|
||||||
|
* The resources from which the mapping gets generated
|
||||||
|
* @return A mapping with the module names as keys and the list of files not
|
||||||
|
* part of the modules image (jimage) as values.
|
||||||
|
*/
|
||||||
|
private static Map<String, List<String>> recordAndFilterEntries(ResourcePool resultResources) {
|
||||||
|
Map<String, List<String>> nonClassResEntries = new HashMap<>();
|
||||||
|
Platform platform = getTargetPlatform(resultResources);
|
||||||
|
resultResources.entries().forEach(entry -> {
|
||||||
|
// Note that the fs_$module_files file is a resource file itself, so
|
||||||
|
// we cannot add fs_$module_files themselves due to the
|
||||||
|
// not(class_or_resources) condition. However, we also don't want
|
||||||
|
// to track 'release' file entries (not(top) condition) as those are
|
||||||
|
// handled by the release info plugin.
|
||||||
|
if (entry.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE &&
|
||||||
|
entry.type() != ResourcePoolEntry.Type.TOP) {
|
||||||
|
List<String> mRes = nonClassResEntries.computeIfAbsent(entry.moduleName(),
|
||||||
|
a -> new ArrayList<>());
|
||||||
|
ResourceFileEntry rfEntry = ResourceFileEntry.toResourceFileEntry(entry,
|
||||||
|
platform);
|
||||||
|
mRes.add(rfEntry.encodeToString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return nonClassResEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Platform getTargetPlatform(ResourcePool in) {
|
||||||
|
String platform = in.moduleView().findModule("java.base")
|
||||||
|
.map(ResourcePoolModule::targetPlatform)
|
||||||
|
.orElseThrow(() -> new AssertionError("java.base not found"));
|
||||||
|
return Platform.parsePlatform(platform);
|
||||||
|
}
|
||||||
|
|
||||||
private static ResourcePoolManager createPoolManager(Set<Archive> archives,
|
private static ResourcePoolManager createPoolManager(Set<Archive> archives,
|
||||||
Map<String, List<Entry>> entriesForModule,
|
Map<String, List<Entry>> entriesForModule,
|
||||||
ByteOrder byteOrder,
|
ByteOrder byteOrder,
|
||||||
BasicImageWriter writer) throws IOException {
|
BasicImageWriter writer) throws IOException {
|
||||||
ResourcePoolManager resources = new ResourcePoolManager(byteOrder, new StringTable() {
|
ResourcePoolManager resources = createBasicResourcePoolManager(byteOrder, writer);
|
||||||
|
archives.stream()
|
||||||
|
.map(Archive::moduleName)
|
||||||
|
.sorted()
|
||||||
|
.flatMap(mn ->
|
||||||
|
entriesForModule.get(mn).stream()
|
||||||
|
.map(e -> new ArchiveEntryResourcePoolEntry(mn,
|
||||||
|
e.getResourcePoolEntryName(), e)))
|
||||||
|
.forEach(resources::add);
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourcePoolManager createBasicResourcePoolManager(ByteOrder byteOrder,
|
||||||
|
BasicImageWriter writer) {
|
||||||
|
return new ResourcePoolManager(byteOrder, new StringTable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int addString(String str) {
|
public int addString(String str) {
|
||||||
@ -264,14 +561,25 @@ public final class ImageFileCreator {
|
|||||||
return writer.getString(id);
|
return writer.getString(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
archives.stream()
|
}
|
||||||
.map(Archive::moduleName)
|
|
||||||
.sorted()
|
/**
|
||||||
.flatMap(mn ->
|
* Creates a ResourcePoolManager from existing resources so that more
|
||||||
entriesForModule.get(mn).stream()
|
* resources can be appended.
|
||||||
.map(e -> new ArchiveEntryResourcePoolEntry(mn,
|
*
|
||||||
e.getResourcePoolEntryName(), e)))
|
* @param resultResources The existing resources to initially add.
|
||||||
.forEach(resources::add);
|
* @param writer The basic image writer.
|
||||||
|
* @return An appendable ResourcePoolManager.
|
||||||
|
*/
|
||||||
|
private static ResourcePoolManager createPoolManager(ResourcePool resultResources,
|
||||||
|
BasicImageWriter writer) {
|
||||||
|
ResourcePoolManager resources = createBasicResourcePoolManager(resultResources.byteOrder(),
|
||||||
|
writer);
|
||||||
|
// Note that resources are already sorted in the correct order.
|
||||||
|
// The underlying ResourcePoolManager keeps track of entries via
|
||||||
|
// LinkedHashMap, which keeps values in insertion order. Therefore
|
||||||
|
// adding resources here, preserving that same order is OK.
|
||||||
|
resultResources.entries().forEach(resources::add);
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,525 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.tools.jlink.internal;
|
||||||
|
|
||||||
|
import static jdk.tools.jlink.internal.LinkableRuntimeImage.RESPATH_PATTERN;
|
||||||
|
import static jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException.Reason.MODIFIED_FILE;
|
||||||
|
import static jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException.Reason.PATCH_MODULE;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.lang.module.ModuleFinder;
|
||||||
|
import java.lang.module.ModuleReference;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import jdk.internal.util.OperatingSystem;
|
||||||
|
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePoolEntry.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An archive implementation based on the JDK's run-time image. That is, classes
|
||||||
|
* and resources from the modules image (lib/modules, or jimage) and other
|
||||||
|
* associated files from the filesystem of the JDK installation.
|
||||||
|
*/
|
||||||
|
public class JRTArchive implements Archive {
|
||||||
|
|
||||||
|
private final String module;
|
||||||
|
private final Path path;
|
||||||
|
private final ModuleReference ref;
|
||||||
|
// The collection of files of this module
|
||||||
|
private final List<JRTFile> files = new ArrayList<>();
|
||||||
|
// Files not part of the lib/modules image of the JDK install.
|
||||||
|
// Thus, native libraries, binaries, legal files, etc.
|
||||||
|
private final List<String> otherRes;
|
||||||
|
// Maps a module resource path to the corresponding diff to packaged
|
||||||
|
// modules for that resource (if any)
|
||||||
|
private final Map<String, ResourceDiff> resDiff;
|
||||||
|
private final boolean errorOnModifiedFile;
|
||||||
|
private final TaskHelper taskHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JRTArchive constructor
|
||||||
|
*
|
||||||
|
* @param module The module name this archive refers to
|
||||||
|
* @param path The JRT filesystem path.
|
||||||
|
* @param errorOnModifiedFile Whether or not modified files of the JDK
|
||||||
|
* install aborts the link.
|
||||||
|
* @param perModDiff The lib/modules (a.k.a jimage) diff for this module,
|
||||||
|
* possibly an empty list if there are no differences.
|
||||||
|
*/
|
||||||
|
JRTArchive(String module,
|
||||||
|
Path path,
|
||||||
|
boolean errorOnModifiedFile,
|
||||||
|
List<ResourceDiff> perModDiff,
|
||||||
|
TaskHelper taskHelper) {
|
||||||
|
this.module = module;
|
||||||
|
this.path = path;
|
||||||
|
this.ref = ModuleFinder.ofSystem()
|
||||||
|
.find(module)
|
||||||
|
.orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
"Module " + module +
|
||||||
|
" not part of the JDK install"));
|
||||||
|
this.errorOnModifiedFile = errorOnModifiedFile;
|
||||||
|
this.otherRes = readModuleResourceFile(module);
|
||||||
|
this.resDiff = Objects.requireNonNull(perModDiff).stream()
|
||||||
|
.collect(Collectors.toMap(ResourceDiff::getName, Function.identity()));
|
||||||
|
this.taskHelper = taskHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String moduleName() {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Entry> entries() {
|
||||||
|
try {
|
||||||
|
collectFiles();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
return files.stream().map(JRTFile::toEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void open() throws IOException {
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
collectFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!files.isEmpty()) {
|
||||||
|
files.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(module, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return (obj instanceof JRTArchive other &&
|
||||||
|
Objects.equals(module, other.module) &&
|
||||||
|
Objects.equals(path, other.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectFiles() throws IOException {
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
addNonClassResources();
|
||||||
|
// Add classes/resources from the run-time image,
|
||||||
|
// patched with the run-time image diff
|
||||||
|
files.addAll(ref.open().list()
|
||||||
|
.filter(i -> {
|
||||||
|
String lookupKey = String.format("/%s/%s", module, i);
|
||||||
|
ResourceDiff rd = resDiff.get(lookupKey);
|
||||||
|
// Filter all resources with a resource diff
|
||||||
|
// that are of kind MODIFIED.
|
||||||
|
// Note that REMOVED won't happen since in
|
||||||
|
// that case the module listing won't have
|
||||||
|
// the resource anyway.
|
||||||
|
// Note as well that filter removes files
|
||||||
|
// of kind ADDED. Those files are not in
|
||||||
|
// the packaged modules, so ought not to
|
||||||
|
// get returned from the pipeline.
|
||||||
|
return (rd == null ||
|
||||||
|
rd.getKind() == ResourceDiff.Kind.MODIFIED);
|
||||||
|
})
|
||||||
|
.map(s -> {
|
||||||
|
String lookupKey = String.format("/%s/%s", module, s);
|
||||||
|
return new JRTArchiveFile(JRTArchive.this, s,
|
||||||
|
EntryType.CLASS_OR_RESOURCE,
|
||||||
|
null /* hashOrTarget */,
|
||||||
|
false /* symlink */,
|
||||||
|
resDiff.get(lookupKey));
|
||||||
|
})
|
||||||
|
.toList());
|
||||||
|
// Finally add all files only present in the resource diff
|
||||||
|
// That is, removed items in the run-time image.
|
||||||
|
files.addAll(resDiff.values().stream()
|
||||||
|
.filter(rd -> rd.getKind() == ResourceDiff.Kind.REMOVED)
|
||||||
|
.map(s -> {
|
||||||
|
int secondSlash = s.getName().indexOf("/", 1);
|
||||||
|
assert secondSlash != -1;
|
||||||
|
String pathWithoutModule = s.getName().substring(secondSlash + 1);
|
||||||
|
return new JRTArchiveFile(JRTArchive.this,
|
||||||
|
pathWithoutModule,
|
||||||
|
EntryType.CLASS_OR_RESOURCE,
|
||||||
|
null /* hashOrTarget */,
|
||||||
|
false /* symlink */,
|
||||||
|
s);
|
||||||
|
})
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* no need to keep track of the warning produced since this is eagerly
|
||||||
|
* checked once.
|
||||||
|
*/
|
||||||
|
private void addNonClassResources() {
|
||||||
|
// Not all modules will have other resources like bin, lib, legal etc.
|
||||||
|
// files. In that case the list will be empty.
|
||||||
|
if (!otherRes.isEmpty()) {
|
||||||
|
files.addAll(otherRes.stream()
|
||||||
|
.filter(Predicate.not(String::isEmpty))
|
||||||
|
.map(s -> {
|
||||||
|
ResourceFileEntry m = ResourceFileEntry.decodeFromString(s);
|
||||||
|
|
||||||
|
// Read from the base JDK image.
|
||||||
|
Path path = BASE.resolve(m.resPath);
|
||||||
|
if (shaSumMismatch(path, m.hashOrTarget, m.symlink)) {
|
||||||
|
if (errorOnModifiedFile) {
|
||||||
|
throw new RuntimeImageLinkException(path.toString(), MODIFIED_FILE);
|
||||||
|
} else {
|
||||||
|
taskHelper.warning("err.runtime.link.modified.file", path.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JRTArchiveFile(JRTArchive.this,
|
||||||
|
m.resPath,
|
||||||
|
toEntryType(m.resType),
|
||||||
|
m.hashOrTarget,
|
||||||
|
m.symlink,
|
||||||
|
/* diff only for resources */
|
||||||
|
null);
|
||||||
|
})
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean shaSumMismatch(Path res, String expectedSha, boolean isSymlink) {
|
||||||
|
if (isSymlink) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// handle non-symlink resources
|
||||||
|
try {
|
||||||
|
HexFormat format = HexFormat.of();
|
||||||
|
byte[] expected = format.parseHex(expectedSha);
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
|
try (InputStream is = Files.newInputStream(res)) {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int readBytes = -1;
|
||||||
|
while ((readBytes = is.read(buf)) != -1) {
|
||||||
|
digest.update(buf, 0, readBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] actual = digest.digest();
|
||||||
|
return !MessageDigest.isEqual(expected, actual);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError("SHA-512 sum check failed!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EntryType toEntryType(Type input) {
|
||||||
|
return switch(input) {
|
||||||
|
case CLASS_OR_RESOURCE -> EntryType.CLASS_OR_RESOURCE;
|
||||||
|
case CONFIG -> EntryType.CONFIG;
|
||||||
|
case HEADER_FILE -> EntryType.HEADER_FILE;
|
||||||
|
case LEGAL_NOTICE -> EntryType.LEGAL_NOTICE;
|
||||||
|
case MAN_PAGE -> EntryType.MAN_PAGE;
|
||||||
|
case NATIVE_CMD -> EntryType.NATIVE_CMD;
|
||||||
|
case NATIVE_LIB -> EntryType.NATIVE_LIB;
|
||||||
|
case TOP -> throw new IllegalArgumentException(
|
||||||
|
"TOP files should be handled by ReleaseInfoPlugin!");
|
||||||
|
default -> throw new IllegalArgumentException("Unknown type: " + input);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ResourceFileEntry(Type resType,
|
||||||
|
boolean symlink,
|
||||||
|
String hashOrTarget,
|
||||||
|
String resPath) {
|
||||||
|
// Type file format:
|
||||||
|
// '<type>|{0,1}|<sha-sum>|<file-path>'
|
||||||
|
// (1) (2) (3) (4)
|
||||||
|
//
|
||||||
|
// Where fields are:
|
||||||
|
//
|
||||||
|
// (1) The resource type as specified by ResourcePoolEntry.type()
|
||||||
|
// (2) Symlink designator. 0 => regular resource, 1 => symlinked resource
|
||||||
|
// (3) The SHA-512 sum of the resources' content. The link to the target
|
||||||
|
// for symlinked resources.
|
||||||
|
// (4) The relative file path of the resource
|
||||||
|
private static final String TYPE_FILE_FORMAT = "%d|%d|%s|%s";
|
||||||
|
|
||||||
|
private static final Map<Integer, Type> typeMap = Arrays.stream(Type.values())
|
||||||
|
.collect(Collectors.toMap(Type::ordinal, Function.identity()));
|
||||||
|
|
||||||
|
public String encodeToString() {
|
||||||
|
return String.format(TYPE_FILE_FORMAT,
|
||||||
|
resType.ordinal(),
|
||||||
|
symlink ? 1 : 0,
|
||||||
|
hashOrTarget,
|
||||||
|
resPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* line: <int>|<int>|<hashOrTarget>|<path>
|
||||||
|
*
|
||||||
|
* Take the integer before '|' convert it to a Type. The second
|
||||||
|
* token is an integer representing symlinks (or not). The third token is
|
||||||
|
* a hash sum (sha512) of the file denoted by the fourth token (path).
|
||||||
|
*/
|
||||||
|
static ResourceFileEntry decodeFromString(String line) {
|
||||||
|
assert !line.isEmpty();
|
||||||
|
|
||||||
|
String[] tokens = line.split("\\|", 4);
|
||||||
|
Type type = null;
|
||||||
|
int symlinkNum = -1;
|
||||||
|
try {
|
||||||
|
Integer typeInt = Integer.valueOf(tokens[0]);
|
||||||
|
type = typeMap.get(typeInt);
|
||||||
|
if (type == null) {
|
||||||
|
throw new AssertionError("Illegal type ordinal: " + typeInt);
|
||||||
|
}
|
||||||
|
symlinkNum = Integer.valueOf(tokens[1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new AssertionError(e); // must not happen
|
||||||
|
}
|
||||||
|
if (symlinkNum < 0 || symlinkNum > 1) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"Symlink designator out of range [0,1] got: " +
|
||||||
|
symlinkNum);
|
||||||
|
}
|
||||||
|
return new ResourceFileEntry(type,
|
||||||
|
symlinkNum == 1,
|
||||||
|
tokens[2] /* hash or target */,
|
||||||
|
tokens[3] /* resource path */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceFileEntry toResourceFileEntry(ResourcePoolEntry entry,
|
||||||
|
Platform platform) {
|
||||||
|
String resPathWithoutMod = dropModuleFromPath(entry, platform);
|
||||||
|
// Symlinks don't have a hash sum, but a link to the target instead
|
||||||
|
String hashOrTarget = entry.linkedTarget() == null
|
||||||
|
? computeSha512(entry)
|
||||||
|
: dropModuleFromPath(entry.linkedTarget(),
|
||||||
|
platform);
|
||||||
|
return new ResourceFileEntry(entry.type(),
|
||||||
|
entry.linkedTarget() != null,
|
||||||
|
hashOrTarget,
|
||||||
|
resPathWithoutMod);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String computeSha512(ResourcePoolEntry entry) {
|
||||||
|
try {
|
||||||
|
assert entry.linkedTarget() == null;
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
|
try (InputStream is = entry.content()) {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int bytesRead = -1;
|
||||||
|
while ((bytesRead = is.read(buf)) != -1) {
|
||||||
|
digest.update(buf, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] db = digest.digest();
|
||||||
|
HexFormat format = HexFormat.of();
|
||||||
|
return format.formatHex(db);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError("Failed to generate hash sum for " +
|
||||||
|
entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String dropModuleFromPath(ResourcePoolEntry entry,
|
||||||
|
Platform platform) {
|
||||||
|
String resPath = entry.path()
|
||||||
|
.substring(
|
||||||
|
// + 2 => prefixed and suffixed '/'
|
||||||
|
// For example: '/java.base/'
|
||||||
|
entry.moduleName().length() + 2);
|
||||||
|
if (!isWindows(platform)) {
|
||||||
|
return resPath;
|
||||||
|
}
|
||||||
|
// For Windows the libraries live in the 'bin' folder rather than
|
||||||
|
// the 'lib' folder in the final image. Note that going by the
|
||||||
|
// NATIVE_LIB type only is insufficient since only files with suffix
|
||||||
|
// .dll/diz/map/pdb are transplanted to 'bin'.
|
||||||
|
// See: DefaultImageBuilder.nativeDir()
|
||||||
|
return nativeDir(entry, resPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWindows(Platform platform) {
|
||||||
|
return platform.os() == OperatingSystem.WINDOWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String nativeDir(ResourcePoolEntry entry, String resPath) {
|
||||||
|
if (entry.type() != ResourcePoolEntry.Type.NATIVE_LIB) {
|
||||||
|
return resPath;
|
||||||
|
}
|
||||||
|
// precondition: Native lib, windows platform
|
||||||
|
if (resPath.endsWith(".dll") || resPath.endsWith(".diz")
|
||||||
|
|| resPath.endsWith(".pdb") || resPath.endsWith(".map")) {
|
||||||
|
if (resPath.startsWith(LIB_DIRNAME + "/")) {
|
||||||
|
return BIN_DIRNAME + "/" +
|
||||||
|
resPath.substring((LIB_DIRNAME + "/").length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resPath;
|
||||||
|
}
|
||||||
|
private static final String BIN_DIRNAME = "bin";
|
||||||
|
private static final String LIB_DIRNAME = "lib";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Path BASE = Paths.get(System.getProperty("java.home"));
|
||||||
|
|
||||||
|
interface JRTFile {
|
||||||
|
Entry toEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
record JRTArchiveFile(Archive archive,
|
||||||
|
String resPath,
|
||||||
|
EntryType resType,
|
||||||
|
String sha,
|
||||||
|
boolean symlink,
|
||||||
|
ResourceDiff diff) implements JRTFile {
|
||||||
|
public Entry toEntry() {
|
||||||
|
return new Entry(archive,
|
||||||
|
String.format("/%s/%s",
|
||||||
|
archive.moduleName(),
|
||||||
|
resPath),
|
||||||
|
resPath,
|
||||||
|
resType) {
|
||||||
|
@Override
|
||||||
|
public long size() {
|
||||||
|
try {
|
||||||
|
if (resType != EntryType.CLASS_OR_RESOURCE) {
|
||||||
|
// Read from the base JDK image, special casing
|
||||||
|
// symlinks, which have the link target in the
|
||||||
|
// hashOrTarget field
|
||||||
|
if (symlink) {
|
||||||
|
return Files.size(BASE.resolve(sha));
|
||||||
|
}
|
||||||
|
return Files.size(BASE.resolve(resPath));
|
||||||
|
} else {
|
||||||
|
if (diff != null) {
|
||||||
|
// If the resource has a diff to the
|
||||||
|
// packaged modules, use the diff. Diffs of kind
|
||||||
|
// ADDED have been filtered out in collectFiles();
|
||||||
|
assert diff.getKind() != ResourceDiff.Kind.ADDED;
|
||||||
|
assert diff.getName().equals(String.format("/%s/%s",
|
||||||
|
archive.moduleName(),
|
||||||
|
resPath));
|
||||||
|
return diff.getResourceBytes().length;
|
||||||
|
}
|
||||||
|
// Read from the module image. This works, because
|
||||||
|
// the underlying base path is a JrtPath with the
|
||||||
|
// JrtFileSystem underneath which is able to handle
|
||||||
|
// this size query.
|
||||||
|
try {
|
||||||
|
return Files.size(archive.getPath().resolve(resPath));
|
||||||
|
} catch (NoSuchFileException file) {
|
||||||
|
// This indicates that we don't find the class in the
|
||||||
|
// modules image using the JRT FS provider. Yet, we find
|
||||||
|
// the class using the system module finder. Therefore,
|
||||||
|
// we have a patched module. Mention that module patching
|
||||||
|
// is not supported.
|
||||||
|
throw new RuntimeImageLinkException(file.getFile(), PATCH_MODULE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream stream() throws IOException {
|
||||||
|
if (resType != EntryType.CLASS_OR_RESOURCE) {
|
||||||
|
// Read from the base JDK image.
|
||||||
|
Path path = symlink ? BASE.resolve(sha) : BASE.resolve(resPath);
|
||||||
|
return Files.newInputStream(path);
|
||||||
|
} else {
|
||||||
|
// Read from the module image. Use the diff to the
|
||||||
|
// packaged modules if we have one. Diffs of kind
|
||||||
|
// ADDED have been filtered out in collectFiles();
|
||||||
|
if (diff != null) {
|
||||||
|
assert diff.getKind() != ResourceDiff.Kind.ADDED;
|
||||||
|
assert diff.getName().equals(String.format("/%s/%s",
|
||||||
|
archive.moduleName(),
|
||||||
|
resPath));
|
||||||
|
return new ByteArrayInputStream(diff.getResourceBytes());
|
||||||
|
}
|
||||||
|
String module = archive.moduleName();
|
||||||
|
ModuleReference mRef = ModuleFinder.ofSystem()
|
||||||
|
.find(module).orElseThrow();
|
||||||
|
return mRef.open().open(resPath).orElseThrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> readModuleResourceFile(String modName) {
|
||||||
|
String resName = String.format(RESPATH_PATTERN, modName);
|
||||||
|
try {
|
||||||
|
try (InputStream inStream = JRTArchive.class.getModule()
|
||||||
|
.getResourceAsStream(resName)) {
|
||||||
|
String input = new String(inStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
if (input.isEmpty()) {
|
||||||
|
// Not all modules have non-class resources
|
||||||
|
return Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
return Arrays.asList(input.split("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException("Failed to process resources from the " +
|
||||||
|
"run-time image for module " + modName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,7 +26,6 @@ package jdk.tools.jlink.internal;
|
|||||||
|
|
||||||
import java.lang.module.Configuration;
|
import java.lang.module.Configuration;
|
||||||
import java.lang.module.ModuleFinder;
|
import java.lang.module.ModuleFinder;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -148,6 +147,9 @@ public final class Jlink {
|
|||||||
private final Path output;
|
private final Path output;
|
||||||
private final Set<String> modules;
|
private final Set<String> modules;
|
||||||
private final ModuleFinder finder;
|
private final ModuleFinder finder;
|
||||||
|
private final boolean linkFromRuntimeImage;
|
||||||
|
private final boolean ignoreModifiedRuntime;
|
||||||
|
private final boolean generateRuntimeImage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jlink configuration,
|
* jlink configuration,
|
||||||
@ -158,10 +160,16 @@ public final class Jlink {
|
|||||||
*/
|
*/
|
||||||
public JlinkConfiguration(Path output,
|
public JlinkConfiguration(Path output,
|
||||||
Set<String> modules,
|
Set<String> modules,
|
||||||
ModuleFinder finder) {
|
ModuleFinder finder,
|
||||||
|
boolean linkFromRuntimeImage,
|
||||||
|
boolean ignoreModifiedRuntime,
|
||||||
|
boolean generateRuntimeImage) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.modules = Objects.requireNonNull(modules);
|
this.modules = Objects.requireNonNull(modules);
|
||||||
this.finder = finder;
|
this.finder = finder;
|
||||||
|
this.linkFromRuntimeImage = linkFromRuntimeImage;
|
||||||
|
this.ignoreModifiedRuntime = ignoreModifiedRuntime;
|
||||||
|
this.generateRuntimeImage = generateRuntimeImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,6 +194,18 @@ public final class Jlink {
|
|||||||
return finder;
|
return finder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean linkFromRuntimeImage() {
|
||||||
|
return linkFromRuntimeImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ignoreModifiedRuntime() {
|
||||||
|
return ignoreModifiedRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGenerateRuntimeImage() {
|
||||||
|
return generateRuntimeImage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Configuration} of the given module path,
|
* Returns a {@link Configuration} of the given module path,
|
||||||
* root modules with full service binding.
|
* root modules with full service binding.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
package jdk.tools.jlink.internal;
|
package jdk.tools.jlink.internal;
|
||||||
|
|
||||||
|
import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -39,14 +41,15 @@ import java.lang.module.ResolutionException;
|
|||||||
import java.lang.module.ResolvedModule;
|
import java.lang.module.ResolvedModule;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.FileVisitResult;
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.SimpleFileVisitor;
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -60,18 +63,18 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import jdk.internal.module.ModulePath;
|
||||||
import jdk.internal.module.ModuleReferenceImpl;
|
import jdk.internal.module.ModuleReferenceImpl;
|
||||||
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
|
import jdk.internal.module.ModuleResolution;
|
||||||
import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
|
import jdk.internal.opt.CommandLine;
|
||||||
|
import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
|
||||||
import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
|
import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
|
||||||
import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
|
import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
|
||||||
|
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
|
||||||
import jdk.tools.jlink.internal.TaskHelper.Option;
|
import jdk.tools.jlink.internal.TaskHelper.Option;
|
||||||
import jdk.tools.jlink.internal.TaskHelper.OptionsHelper;
|
import jdk.tools.jlink.internal.TaskHelper.OptionsHelper;
|
||||||
import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
|
import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException;
|
||||||
import jdk.tools.jlink.plugin.PluginException;
|
import jdk.tools.jlink.plugin.PluginException;
|
||||||
import jdk.internal.opt.CommandLine;
|
|
||||||
import jdk.internal.module.ModulePath;
|
|
||||||
import jdk.internal.module.ModuleResolution;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for the jlink tool.
|
* Implementation for the jlink tool.
|
||||||
@ -86,7 +89,6 @@ public class JlinkTask {
|
|||||||
|
|
||||||
private static final TaskHelper taskHelper
|
private static final TaskHelper taskHelper
|
||||||
= new TaskHelper(JLINK_BUNDLE);
|
= new TaskHelper(JLINK_BUNDLE);
|
||||||
|
|
||||||
private static final Option<?>[] recognizedOptions = {
|
private static final Option<?>[] recognizedOptions = {
|
||||||
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||||
task.options.help = true;
|
task.options.help = true;
|
||||||
@ -182,7 +184,17 @@ public class JlinkTask {
|
|||||||
}, true, "--full-version"),
|
}, true, "--full-version"),
|
||||||
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||||
task.options.ignoreSigning = true;
|
task.options.ignoreSigning = true;
|
||||||
}, "--ignore-signing-information"),};
|
}, "--ignore-signing-information"),
|
||||||
|
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||||
|
task.options.ignoreModifiedRuntime = true;
|
||||||
|
}, true, "--ignore-modified-runtime"),
|
||||||
|
// option for generating a runtime that can then
|
||||||
|
// be used for linking from the run-time image.
|
||||||
|
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||||
|
task.options.generateLinkableRuntime = true;
|
||||||
|
}, true, "--generate-linkable-runtime")
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
private static final String PROGNAME = "jlink";
|
private static final String PROGNAME = "jlink";
|
||||||
private final OptionsValues options = new OptionsValues();
|
private final OptionsValues options = new OptionsValues();
|
||||||
@ -222,6 +234,8 @@ public class JlinkTask {
|
|||||||
boolean ignoreSigning = false;
|
boolean ignoreSigning = false;
|
||||||
boolean bindServices = false;
|
boolean bindServices = false;
|
||||||
boolean suggestProviders = false;
|
boolean suggestProviders = false;
|
||||||
|
boolean ignoreModifiedRuntime = false;
|
||||||
|
boolean generateLinkableRuntime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options";
|
public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options";
|
||||||
@ -252,7 +266,7 @@ public class JlinkTask {
|
|||||||
.showUsage(true);
|
.showUsage(true);
|
||||||
}
|
}
|
||||||
if (options.help) {
|
if (options.help) {
|
||||||
optionsHelper.showHelp(PROGNAME);
|
optionsHelper.showHelp(PROGNAME, LinkableRuntimeImage.isLinkableRuntime());
|
||||||
return EXIT_OK;
|
return EXIT_OK;
|
||||||
}
|
}
|
||||||
if (optionsHelper.shouldListPlugins()) {
|
if (optionsHelper.shouldListPlugins()) {
|
||||||
@ -270,11 +284,6 @@ public class JlinkTask {
|
|||||||
if (jmods != null) {
|
if (jmods != null) {
|
||||||
options.modulePath.add(jmods);
|
options.modulePath.add(jmods);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.modulePath.isEmpty()) {
|
|
||||||
throw taskHelper.newBadArgs("err.modulepath.must.be.specified")
|
|
||||||
.showUsage(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JlinkConfiguration config = initJlinkConfig();
|
JlinkConfiguration config = initJlinkConfig();
|
||||||
@ -300,7 +309,7 @@ public class JlinkTask {
|
|||||||
}
|
}
|
||||||
cleanupOutput(outputPath);
|
cleanupOutput(outputPath);
|
||||||
return EXIT_ERROR;
|
return EXIT_ERROR;
|
||||||
} catch (IllegalArgumentException | ResolutionException e) {
|
} catch (IllegalArgumentException | ResolutionException | RuntimeImageLinkException e) {
|
||||||
log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
|
log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
e.printStackTrace(log);
|
e.printStackTrace(log);
|
||||||
@ -356,6 +365,7 @@ public class JlinkTask {
|
|||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
|
new OptionsValues(),
|
||||||
null);
|
null);
|
||||||
|
|
||||||
// Then create the Plugin Stack
|
// Then create the Plugin Stack
|
||||||
@ -370,7 +380,7 @@ public class JlinkTask {
|
|||||||
private JlinkConfiguration initJlinkConfig() throws BadArgs {
|
private JlinkConfiguration initJlinkConfig() throws BadArgs {
|
||||||
Set<String> roots = new HashSet<>();
|
Set<String> roots = new HashSet<>();
|
||||||
for (String mod : options.addMods) {
|
for (String mod : options.addMods) {
|
||||||
if (mod.equals(ALL_MODULE_PATH)) {
|
if (mod.equals(ALL_MODULE_PATH) && options.modulePath.size() > 0) {
|
||||||
ModuleFinder finder = newModuleFinder(options.modulePath, options.limitMods, Set.of());
|
ModuleFinder finder = newModuleFinder(options.modulePath, options.limitMods, Set.of());
|
||||||
// all observable modules are roots
|
// all observable modules are roots
|
||||||
finder.findAll()
|
finder.findAll()
|
||||||
@ -392,9 +402,75 @@ public class JlinkTask {
|
|||||||
finder = newModuleFinder(options.modulePath, options.limitMods, roots);
|
finder = newModuleFinder(options.modulePath, options.limitMods, roots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isLinkFromRuntime = options.modulePath.isEmpty();
|
||||||
|
// In case of custom modules outside the JDK we may
|
||||||
|
// have a non-empty module path, which must not include
|
||||||
|
// java.base. If it did, we link using packaged modules from that
|
||||||
|
// module path. If the module path does not include java.base, we have
|
||||||
|
// the case where we link from the run-time image. In that case, we take
|
||||||
|
// the JDK modules from the run-time image (ModuleFinder.ofSystem()).
|
||||||
|
if (finder.find("java.base").isEmpty()) {
|
||||||
|
isLinkFromRuntime = true;
|
||||||
|
ModuleFinder runtimeImageFinder = ModuleFinder.ofSystem();
|
||||||
|
finder = combinedFinders(runtimeImageFinder, finder, options.limitMods, roots);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --keep-packaged-modules doesn't make sense as we are not linking
|
||||||
|
// from packaged modules to begin with.
|
||||||
|
if (isLinkFromRuntime && options.packagedModulesPath != null) {
|
||||||
|
throw taskHelper.newBadArgs("err.runtime.link.packaged.mods");
|
||||||
|
}
|
||||||
|
|
||||||
return new JlinkConfiguration(options.output,
|
return new JlinkConfiguration(options.output,
|
||||||
roots,
|
roots,
|
||||||
finder);
|
finder,
|
||||||
|
isLinkFromRuntime,
|
||||||
|
options.ignoreModifiedRuntime,
|
||||||
|
options.generateLinkableRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a combined module finder of {@code finder} and
|
||||||
|
* {@code runtimeImageFinder} that first looks-up modules in the
|
||||||
|
* {@code runtimeImageFinder} and if not present in {@code finder}.
|
||||||
|
*
|
||||||
|
* @param runtimeImageFinder A system modules finder.
|
||||||
|
* @param finder A module finder based on packaged modules.
|
||||||
|
* @param limitMods The set of limited modules for the resulting
|
||||||
|
* finder (if any).
|
||||||
|
* @param roots All module roots.
|
||||||
|
*
|
||||||
|
* @return A combined finder, or the input finder, potentially applying
|
||||||
|
* module limits.
|
||||||
|
*/
|
||||||
|
private ModuleFinder combinedFinders(ModuleFinder runtimeImageFinder,
|
||||||
|
ModuleFinder finder,
|
||||||
|
Set<String> limitMods,
|
||||||
|
Set<String> roots) {
|
||||||
|
ModuleFinder combined = new ModuleFinder() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ModuleReference> find(String name) {
|
||||||
|
Optional<ModuleReference> mref = runtimeImageFinder.find(name);
|
||||||
|
if (mref.isEmpty()) {
|
||||||
|
return finder.find(name);
|
||||||
|
}
|
||||||
|
return mref;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ModuleReference> findAll() {
|
||||||
|
Set<ModuleReference> all = new HashSet<>();
|
||||||
|
all.addAll(runtimeImageFinder.findAll());
|
||||||
|
all.addAll(finder.findAll());
|
||||||
|
return Collections.unmodifiableSet(all);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// if limitmods is specified then limit the universe
|
||||||
|
if (limitMods != null && !limitMods.isEmpty()) {
|
||||||
|
return limitFinder(combined, limitMods, Objects.requireNonNull(roots));
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createImage(JlinkConfiguration config) throws Exception {
|
private void createImage(JlinkConfiguration config) throws Exception {
|
||||||
@ -413,6 +489,7 @@ public class JlinkTask {
|
|||||||
options.bindServices,
|
options.bindServices,
|
||||||
options.endian,
|
options.endian,
|
||||||
options.verbose,
|
options.verbose,
|
||||||
|
options,
|
||||||
log);
|
log);
|
||||||
|
|
||||||
// Then create the Plugin Stack
|
// Then create the Plugin Stack
|
||||||
@ -433,10 +510,10 @@ public class JlinkTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a module finder of the given module path that limits
|
* Returns a module finder of the given module path or the system modules
|
||||||
* the observable modules to those in the transitive closure of
|
* if the module path is empty that limits the observable modules to those
|
||||||
* the modules specified in {@code limitMods} plus other modules
|
* in the transitive closure of the modules specified in {@code limitMods}
|
||||||
* specified in the {@code roots} set.
|
* plus other modules specified in the {@code roots} set.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if java.base module is present
|
* @throws IllegalArgumentException if java.base module is present
|
||||||
* but its descriptor has no version
|
* but its descriptor has no version
|
||||||
@ -445,14 +522,10 @@ public class JlinkTask {
|
|||||||
Set<String> limitMods,
|
Set<String> limitMods,
|
||||||
Set<String> roots)
|
Set<String> roots)
|
||||||
{
|
{
|
||||||
if (Objects.requireNonNull(paths).isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(taskHelper.getMessage("err.empty.module.path"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Path[] entries = paths.toArray(new Path[0]);
|
|
||||||
Runtime.Version version = Runtime.version();
|
Runtime.Version version = Runtime.version();
|
||||||
ModuleFinder finder = ModulePath.of(version, true, entries);
|
Path[] entries = paths.toArray(new Path[0]);
|
||||||
|
ModuleFinder finder = paths.isEmpty() ? ModuleFinder.ofSystem()
|
||||||
|
: ModulePath.of(version, true, entries);
|
||||||
if (finder.find("java.base").isPresent()) {
|
if (finder.find("java.base").isPresent()) {
|
||||||
// use the version of java.base module, if present, as
|
// use the version of java.base module, if present, as
|
||||||
// the release version for multi-release JAR files
|
// the release version for multi-release JAR files
|
||||||
@ -505,8 +578,9 @@ public class JlinkTask {
|
|||||||
|
|
||||||
private static Path toPathLocation(ResolvedModule m) {
|
private static Path toPathLocation(ResolvedModule m) {
|
||||||
Optional<URI> ouri = m.reference().location();
|
Optional<URI> ouri = m.reference().location();
|
||||||
if (ouri.isEmpty())
|
if (ouri.isEmpty()) {
|
||||||
throw new InternalError(m + " does not have a location");
|
throw new InternalError(m + " does not have a location");
|
||||||
|
}
|
||||||
URI uri = ouri.get();
|
URI uri = ouri.get();
|
||||||
return Paths.get(uri);
|
return Paths.get(uri);
|
||||||
}
|
}
|
||||||
@ -518,6 +592,7 @@ public class JlinkTask {
|
|||||||
boolean bindService,
|
boolean bindService,
|
||||||
ByteOrder endian,
|
ByteOrder endian,
|
||||||
boolean verbose,
|
boolean verbose,
|
||||||
|
OptionsValues opts,
|
||||||
PrintWriter log)
|
PrintWriter log)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -534,12 +609,35 @@ public class JlinkTask {
|
|||||||
taskHelper.getMessage("err.automatic.module", mref.descriptor().name(), loc));
|
taskHelper.getMessage("err.automatic.module", mref.descriptor().name(), loc));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Perform some sanity checks for linking from the run-time image
|
||||||
|
if (config.linkFromRuntimeImage()) {
|
||||||
|
if (!LinkableRuntimeImage.isLinkableRuntime()) {
|
||||||
|
String msg = taskHelper.getMessage("err.runtime.link.not.linkable.runtime");
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
|
}
|
||||||
|
// Do not permit linking from run-time image and also including jdk.jlink module
|
||||||
|
if (cf.findModule(JlinkTask.class.getModule().getName()).isPresent()) {
|
||||||
|
String msg = taskHelper.getMessage("err.runtime.link.jdk.jlink.prohibited");
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print info message indicating linking from the run-time image
|
||||||
|
if (verbose && log != null) {
|
||||||
|
log.println(taskHelper.getMessage("runtime.link.info"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose && log != null) {
|
if (verbose && log != null) {
|
||||||
// print modules to be linked in
|
// print modules to be linked in
|
||||||
cf.modules().stream()
|
cf.modules().stream()
|
||||||
.sorted(Comparator.comparing(ResolvedModule::name))
|
.sorted(Comparator.comparing(ResolvedModule::name))
|
||||||
.forEach(rm -> log.format("%s %s%n",
|
.forEach(rm -> log.format("%s %s%s%n",
|
||||||
rm.name(), rm.reference().location().get()));
|
rm.name(),
|
||||||
|
rm.reference().location().get(),
|
||||||
|
// We have a link from run-time image when scheme is 'jrt'
|
||||||
|
"jrt".equals(rm.reference().location().get().getScheme())
|
||||||
|
? " " + taskHelper.getMessage("runtime.link.jprt.path.extra")
|
||||||
|
: ""));
|
||||||
|
|
||||||
// print provider info
|
// print provider info
|
||||||
Set<ModuleReference> references = cf.modules().stream()
|
Set<ModuleReference> references = cf.modules().stream()
|
||||||
@ -559,14 +657,15 @@ public class JlinkTask {
|
|||||||
.map(ModuleDescriptor::name)
|
.map(ModuleDescriptor::name)
|
||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
if (!"".equals(im))
|
if (!"".equals(im)) {
|
||||||
log.println("WARNING: Using incubator modules: " + im);
|
log.println("WARNING: Using incubator modules: " + im);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Path> mods = cf.modules().stream()
|
Map<String, Path> mods = cf.modules().stream()
|
||||||
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
|
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
|
||||||
// determine the target platform of the image being created
|
// determine the target platform of the image being created
|
||||||
Platform targetPlatform = targetPlatform(cf, mods);
|
Platform targetPlatform = targetPlatform(cf, mods, config.linkFromRuntimeImage());
|
||||||
// if the user specified any --endian, then it must match the target platform's native
|
// if the user specified any --endian, then it must match the target platform's native
|
||||||
// endianness
|
// endianness
|
||||||
if (endian != null && endian != targetPlatform.arch().byteOrder()) {
|
if (endian != null && endian != targetPlatform.arch().byteOrder()) {
|
||||||
@ -580,7 +679,92 @@ public class JlinkTask {
|
|||||||
targetPlatform.arch().byteOrder(), targetPlatform);
|
targetPlatform.arch().byteOrder(), targetPlatform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ImageHelper(cf, mods, targetPlatform, retainModulesPath, ignoreSigning);
|
|
||||||
|
// use the version of java.base module, if present, as
|
||||||
|
// the release version for multi-release JAR files
|
||||||
|
var version = cf.findModule("java.base")
|
||||||
|
.map(ResolvedModule::reference)
|
||||||
|
.map(ModuleReference::descriptor)
|
||||||
|
.flatMap(ModuleDescriptor::version)
|
||||||
|
.map(ModuleDescriptor.Version::toString)
|
||||||
|
.map(Runtime.Version::parse)
|
||||||
|
.orElse(Runtime.version());
|
||||||
|
|
||||||
|
Set<Archive> archives = mods.entrySet().stream()
|
||||||
|
.map(e -> newArchive(e.getKey(),
|
||||||
|
e.getValue(),
|
||||||
|
version,
|
||||||
|
ignoreSigning,
|
||||||
|
config,
|
||||||
|
log))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
return new ImageHelper(archives,
|
||||||
|
targetPlatform,
|
||||||
|
retainModulesPath,
|
||||||
|
config.isGenerateRuntimeImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Archive newArchive(String module,
|
||||||
|
Path path,
|
||||||
|
Runtime.Version version,
|
||||||
|
boolean ignoreSigning,
|
||||||
|
JlinkConfiguration config,
|
||||||
|
PrintWriter log) {
|
||||||
|
if (path.toString().endsWith(".jmod")) {
|
||||||
|
return new JmodArchive(module, path);
|
||||||
|
} else if (path.toString().endsWith(".jar")) {
|
||||||
|
ModularJarArchive modularJarArchive = new ModularJarArchive(module, path, version);
|
||||||
|
try (Stream<Archive.Entry> entries = modularJarArchive.entries()) {
|
||||||
|
boolean hasSignatures = entries.anyMatch((entry) -> {
|
||||||
|
String name = entry.name().toUpperCase(Locale.ROOT);
|
||||||
|
|
||||||
|
return name.startsWith("META-INF/") && name.indexOf('/', 9) == -1 && (
|
||||||
|
name.endsWith(".SF") ||
|
||||||
|
name.endsWith(".DSA") ||
|
||||||
|
name.endsWith(".RSA") ||
|
||||||
|
name.endsWith(".EC") ||
|
||||||
|
name.startsWith("META-INF/SIG-")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasSignatures) {
|
||||||
|
if (ignoreSigning) {
|
||||||
|
System.err.println(taskHelper.getMessage("warn.signing", path));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(taskHelper.getMessage("err.signing", path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modularJarArchive;
|
||||||
|
} else if (Files.isDirectory(path) && !"jrt".equals(path.toUri().getScheme())) {
|
||||||
|
// The jrt URI path scheme conditional is there since we'd otherwise
|
||||||
|
// enter this branch for linking from the run-time image where the
|
||||||
|
// path is a jrt path. Note that the specific module would be a
|
||||||
|
// directory. I.e. Files.isDirectory() would be true.
|
||||||
|
Path modInfoPath = path.resolve("module-info.class");
|
||||||
|
if (Files.isRegularFile(modInfoPath)) {
|
||||||
|
return new DirArchive(path, findModuleName(modInfoPath));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
taskHelper.getMessage("err.not.a.module.directory", path));
|
||||||
|
}
|
||||||
|
} else if (config.linkFromRuntimeImage()) {
|
||||||
|
return LinkableRuntimeImage.newArchive(module, path, config.ignoreModifiedRuntime(), taskHelper);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
taskHelper.getMessage("err.not.modular.format", module, path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String findModuleName(Path modInfoPath) {
|
||||||
|
try (BufferedInputStream bis = new BufferedInputStream(
|
||||||
|
Files.newInputStream(modInfoPath))) {
|
||||||
|
return ModuleDescriptor.read(bis).name();
|
||||||
|
} catch (IOException exp) {
|
||||||
|
throw new IllegalArgumentException(taskHelper.getMessage(
|
||||||
|
"err.cannot.read.module.info", modInfoPath), exp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -626,10 +810,12 @@ public class JlinkTask {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Platform targetPlatform(Configuration cf, Map<String, Path> modsPaths) throws IOException {
|
private static Platform targetPlatform(Configuration cf,
|
||||||
|
Map<String, Path> modsPaths,
|
||||||
|
boolean runtimeImageLink) throws IOException {
|
||||||
Path javaBasePath = modsPaths.get("java.base");
|
Path javaBasePath = modsPaths.get("java.base");
|
||||||
assert javaBasePath != null : "java.base module path is missing";
|
assert javaBasePath != null : "java.base module path is missing";
|
||||||
if (isJavaBaseFromDefaultModulePath(javaBasePath)) {
|
if (runtimeImageLink || isJavaBaseFromDefaultModulePath(javaBasePath)) {
|
||||||
// this implies that the java.base module used for the target image
|
// this implies that the java.base module used for the target image
|
||||||
// will correspond to the current platform. So this isn't an attempt to
|
// will correspond to the current platform. So this isn't an attempt to
|
||||||
// build a cross-platform image. We use the current platform's endianness
|
// build a cross-platform image. We use the current platform's endianness
|
||||||
@ -720,8 +906,9 @@ public class JlinkTask {
|
|||||||
String header,
|
String header,
|
||||||
Set<ModuleReference> modules,
|
Set<ModuleReference> modules,
|
||||||
Map<String, Set<String>> serviceToUses) {
|
Map<String, Set<String>> serviceToUses) {
|
||||||
if (modules.isEmpty())
|
if (modules.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Build a map of a service type to the provider modules
|
// Build a map of a service type to the provider modules
|
||||||
Map<String, Set<ModuleDescriptor>> providers = new HashMap<>();
|
Map<String, Set<ModuleDescriptor>> providers = new HashMap<>();
|
||||||
@ -845,95 +1032,14 @@ public class JlinkTask {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ImageHelper implements ImageProvider {
|
private static record ImageHelper(Set<Archive> archives,
|
||||||
final Platform targetPlatform;
|
Platform targetPlatform,
|
||||||
final Path packagedModulesPath;
|
Path packagedModulesPath,
|
||||||
final boolean ignoreSigning;
|
boolean generateRuntimeImage) implements ImageProvider {
|
||||||
final Runtime.Version version;
|
|
||||||
final Set<Archive> archives;
|
|
||||||
|
|
||||||
ImageHelper(Configuration cf,
|
|
||||||
Map<String, Path> modsPaths,
|
|
||||||
Platform targetPlatform,
|
|
||||||
Path packagedModulesPath,
|
|
||||||
boolean ignoreSigning) throws IOException {
|
|
||||||
Objects.requireNonNull(targetPlatform);
|
|
||||||
this.targetPlatform = targetPlatform;
|
|
||||||
this.packagedModulesPath = packagedModulesPath;
|
|
||||||
this.ignoreSigning = ignoreSigning;
|
|
||||||
|
|
||||||
// use the version of java.base module, if present, as
|
|
||||||
// the release version for multi-release JAR files
|
|
||||||
this.version = cf.findModule("java.base")
|
|
||||||
.map(ResolvedModule::reference)
|
|
||||||
.map(ModuleReference::descriptor)
|
|
||||||
.flatMap(ModuleDescriptor::version)
|
|
||||||
.map(ModuleDescriptor.Version::toString)
|
|
||||||
.map(Runtime.Version::parse)
|
|
||||||
.orElse(Runtime.version());
|
|
||||||
|
|
||||||
this.archives = modsPaths.entrySet().stream()
|
|
||||||
.map(e -> newArchive(e.getKey(), e.getValue()))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Archive newArchive(String module, Path path) {
|
|
||||||
if (path.toString().endsWith(".jmod")) {
|
|
||||||
return new JmodArchive(module, path);
|
|
||||||
} else if (path.toString().endsWith(".jar")) {
|
|
||||||
ModularJarArchive modularJarArchive = new ModularJarArchive(module, path, version);
|
|
||||||
|
|
||||||
try (Stream<Archive.Entry> entries = modularJarArchive.entries()) {
|
|
||||||
boolean hasSignatures = entries.anyMatch((entry) -> {
|
|
||||||
String name = entry.name().toUpperCase(Locale.ROOT);
|
|
||||||
|
|
||||||
return name.startsWith("META-INF/") && name.indexOf('/', 9) == -1 && (
|
|
||||||
name.endsWith(".SF") ||
|
|
||||||
name.endsWith(".DSA") ||
|
|
||||||
name.endsWith(".RSA") ||
|
|
||||||
name.endsWith(".EC") ||
|
|
||||||
name.startsWith("META-INF/SIG-")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hasSignatures) {
|
|
||||||
if (ignoreSigning) {
|
|
||||||
System.err.println(taskHelper.getMessage("warn.signing", path));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(taskHelper.getMessage("err.signing", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return modularJarArchive;
|
|
||||||
} else if (Files.isDirectory(path)) {
|
|
||||||
Path modInfoPath = path.resolve("module-info.class");
|
|
||||||
if (Files.isRegularFile(modInfoPath)) {
|
|
||||||
return new DirArchive(path, findModuleName(modInfoPath));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
taskHelper.getMessage("err.not.a.module.directory", path));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
taskHelper.getMessage("err.not.modular.format", module, path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String findModuleName(Path modInfoPath) {
|
|
||||||
try (BufferedInputStream bis = new BufferedInputStream(
|
|
||||||
Files.newInputStream(modInfoPath))) {
|
|
||||||
return ModuleDescriptor.read(bis).name();
|
|
||||||
} catch (IOException exp) {
|
|
||||||
throw new IllegalArgumentException(taskHelper.getMessage(
|
|
||||||
"err.cannot.read.module.info", modInfoPath), exp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
|
public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
|
||||||
ExecutableImage image = ImageFileCreator.create(archives,
|
ExecutableImage image = ImageFileCreator.create(archives,
|
||||||
targetPlatform.arch().byteOrder(), stack);
|
targetPlatform.arch().byteOrder(), stack, generateRuntimeImage, taskHelper);
|
||||||
if (packagedModulesPath != null) {
|
if (packagedModulesPath != null) {
|
||||||
// copy the packaged modules to the given path
|
// copy the packaged modules to the given path
|
||||||
Files.createDirectories(packagedModulesPath);
|
Files.createDirectories(packagedModulesPath);
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.tools.jlink.internal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that supports the feature of running jlink based on the current
|
||||||
|
* run-time image.
|
||||||
|
*/
|
||||||
|
public class LinkableRuntimeImage {
|
||||||
|
|
||||||
|
// meta-data files per module for supporting linking from the run-time image
|
||||||
|
public static final String RESPATH_PATTERN = "jdk/tools/jlink/internal/runtimelink/fs_%s_files";
|
||||||
|
// The diff files per module for supporting linking from the run-time image
|
||||||
|
public static final String DIFF_PATTERN = "jdk/tools/jlink/internal/runtimelink/diff_%s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order to be able to show whether or not a runtime is capable of
|
||||||
|
* linking from it in {@code jlink --help} we need to look for the delta
|
||||||
|
* files in the {@code jdk.jlink} module. If present we have the capability.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff this jlink is capable of linking from the
|
||||||
|
* run-time image.
|
||||||
|
*/
|
||||||
|
public static boolean isLinkableRuntime() {
|
||||||
|
try (InputStream in = getDiffInputStream("java.base")) {
|
||||||
|
return in != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// fall-through
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream getDiffInputStream(String module) throws IOException {
|
||||||
|
String resourceName = String.format(DIFF_PATTERN, module);
|
||||||
|
return LinkableRuntimeImage.class.getModule().getResourceAsStream(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Archive newArchive(String module,
|
||||||
|
Path path,
|
||||||
|
boolean ignoreModifiedRuntime,
|
||||||
|
TaskHelper taskHelper) {
|
||||||
|
assert isLinkableRuntime();
|
||||||
|
// Here we retrieve the per module difference file, which is
|
||||||
|
// potentially empty, from the modules image and pass that on to
|
||||||
|
// JRTArchive for further processing. When streaming resources from
|
||||||
|
// the archive, the diff is being applied.
|
||||||
|
List<ResourceDiff> perModuleDiff = null;
|
||||||
|
try (InputStream in = getDiffInputStream(module)){
|
||||||
|
perModuleDiff = ResourceDiff.read(in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError("Failure to retrieve resource diff for " +
|
||||||
|
"module " + module, e);
|
||||||
|
}
|
||||||
|
return new JRTArchive(module, path, !ignoreModifiedRuntime, perModuleDiff, taskHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -28,23 +28,21 @@ import java.io.IOException;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.MissingResourceException;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.tools.jlink.builder.DefaultImageBuilder;
|
import jdk.tools.jlink.builder.DefaultImageBuilder;
|
||||||
import jdk.tools.jlink.builder.ImageBuilder;
|
import jdk.tools.jlink.builder.ImageBuilder;
|
||||||
@ -55,7 +53,6 @@ import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin;
|
|||||||
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
|
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
|
||||||
import jdk.tools.jlink.plugin.Plugin;
|
import jdk.tools.jlink.plugin.Plugin;
|
||||||
import jdk.tools.jlink.plugin.Plugin.Category;
|
import jdk.tools.jlink.plugin.Plugin.Category;
|
||||||
import jdk.tools.jlink.plugin.PluginException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -584,7 +581,7 @@ public final class TaskHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showHelp(String progName) {
|
public void showHelp(String progName, boolean linkableRuntimeEnabled) {
|
||||||
log.println(bundleHelper.getMessage("main.usage", progName));
|
log.println(bundleHelper.getMessage("main.usage", progName));
|
||||||
Stream.concat(options.stream(), pluginOptions.mainOptions.stream())
|
Stream.concat(options.stream(), pluginOptions.mainOptions.stream())
|
||||||
.filter(option -> !option.isHidden())
|
.filter(option -> !option.isHidden())
|
||||||
@ -594,6 +591,17 @@ public final class TaskHelper {
|
|||||||
});
|
});
|
||||||
|
|
||||||
log.println(bundleHelper.getMessage("main.command.files"));
|
log.println(bundleHelper.getMessage("main.command.files"));
|
||||||
|
// If the JDK build has the run-time image capability show it
|
||||||
|
// in the help output in human readable form.
|
||||||
|
String qualifier = null;
|
||||||
|
if (linkableRuntimeEnabled) {
|
||||||
|
qualifier = bundleHelper.getMessage("main.runtime.image.linking.cap.enabled");
|
||||||
|
} else {
|
||||||
|
qualifier = bundleHelper.getMessage("main.runtime.image.linking.cap.disabled");
|
||||||
|
}
|
||||||
|
log.println(bundleHelper.getMessage("main.runtime.image.linking.cap.sect.header"));
|
||||||
|
log.println(bundleHelper.getMessage("main.runtime.image.linking.cap.msg",
|
||||||
|
qualifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void listPlugins() {
|
public void listPlugins() {
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package jdk.tools.jlink.internal.runtimelink;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a delta between packaged modules (as an ImageResource) and an
|
||||||
|
* optimized jimage (lib/modules) as an ImageResource. The result can be
|
||||||
|
* serialized to a file using {@link ResourceDiff}.
|
||||||
|
*/
|
||||||
|
public class JimageDiffGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource used for linking. Either packaged modules or
|
||||||
|
* packaged modules transformed to an optimized run-time image by applying
|
||||||
|
* the jlink plug-in pipeline. The canonical source, the packaged modules,
|
||||||
|
* are being used to devise the delta to the transformed run-time image. The
|
||||||
|
* delta can can then be used for jlink input together *with* a prepared
|
||||||
|
* run-time image.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("try")
|
||||||
|
public interface ImageResource extends AutoCloseable {
|
||||||
|
public List<String> getEntries();
|
||||||
|
public byte[] getResourceBytes(String name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a difference between packaged modules' resources (base) and the
|
||||||
|
* result of all plug-ins being applied on those resources (image).
|
||||||
|
*
|
||||||
|
* @param base
|
||||||
|
* The ImageResource view of unmodified resources coming from
|
||||||
|
* packaged modules.
|
||||||
|
* @param image
|
||||||
|
* The ImageResource view of the jlink plug-in pipeline having
|
||||||
|
* been applied to the resources in base.
|
||||||
|
* @return The list of resource differences across all modules.
|
||||||
|
*/
|
||||||
|
public List<ResourceDiff> generateDiff(ImageResource base, ImageResource image) throws Exception {
|
||||||
|
List<String> baseResources;
|
||||||
|
Set<String> resources = new HashSet<>();
|
||||||
|
List<ResourceDiff> diffs = new ArrayList<>();
|
||||||
|
try (base; image) {
|
||||||
|
resources.addAll(image.getEntries());
|
||||||
|
baseResources = base.getEntries();
|
||||||
|
for (String item: baseResources) {
|
||||||
|
byte[] baseBytes = base.getResourceBytes(item);
|
||||||
|
// First check that every item in the base image exist in
|
||||||
|
// the optimized image as well. If it does not, it's a removed
|
||||||
|
// item in the optimized image.
|
||||||
|
if (!resources.remove(item)) {
|
||||||
|
// keep track of original bytes for removed item in the
|
||||||
|
// optimized image, since we need to restore them for the
|
||||||
|
// runtime image link
|
||||||
|
ResourceDiff.Builder builder = new ResourceDiff.Builder();
|
||||||
|
ResourceDiff diff = builder.setKind(ResourceDiff.Kind.REMOVED)
|
||||||
|
.setName(item)
|
||||||
|
.setResourceBytes(baseBytes)
|
||||||
|
.build();
|
||||||
|
diffs.add(diff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Verify resource bytes are equal if present in both images
|
||||||
|
boolean contentEquals = Arrays.equals(baseBytes, image.getResourceBytes(item));
|
||||||
|
if (!contentEquals) {
|
||||||
|
// keep track of original bytes (non-optimized)
|
||||||
|
ResourceDiff.Builder builder = new ResourceDiff.Builder();
|
||||||
|
ResourceDiff diff = builder.setKind(ResourceDiff.Kind.MODIFIED)
|
||||||
|
.setName(item)
|
||||||
|
.setResourceBytes(baseBytes)
|
||||||
|
.build();
|
||||||
|
diffs.add(diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// What's now left in the set are the resources only present in the
|
||||||
|
// optimized image (generated by some plugins; not present in jmods)
|
||||||
|
for (String e: resources) {
|
||||||
|
ResourceDiff.Builder builder = new ResourceDiff.Builder();
|
||||||
|
ResourceDiff diff = builder.setKind(ResourceDiff.Kind.ADDED)
|
||||||
|
.setName(e)
|
||||||
|
.build();
|
||||||
|
diffs.add(diff);
|
||||||
|
}
|
||||||
|
return diffs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,286 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package jdk.tools.jlink.internal.runtimelink;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a difference of a jimage resource. For all intents
|
||||||
|
* and purposes this represents a difference between a resource in an optimized
|
||||||
|
* jimage (e.g. images/jdk/lib/modules) and the underlying basic resources from
|
||||||
|
* which the optimized image got derived from (e.g. packaged modules). The
|
||||||
|
* differences are being used in JRTArchive so as to back-track from an optimized
|
||||||
|
* jimage to the original (i.e. it restores original resources using the diff).
|
||||||
|
*/
|
||||||
|
public class ResourceDiff implements Comparable<ResourceDiff> {
|
||||||
|
|
||||||
|
private static final int MAGIC = 0xabba;
|
||||||
|
|
||||||
|
public static enum Kind {
|
||||||
|
ADDED((short)1), // Resource added
|
||||||
|
REMOVED((short)2), // Resource removed
|
||||||
|
MODIFIED((short)3); // Resource modified
|
||||||
|
|
||||||
|
private short value;
|
||||||
|
|
||||||
|
private Kind(short value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Kind fromShort(short v) {
|
||||||
|
if (v > 3 || v < 1) {
|
||||||
|
throw new IllegalArgumentException("Must be within range [1-3]");
|
||||||
|
}
|
||||||
|
switch (v) {
|
||||||
|
case 1: return ADDED;
|
||||||
|
case 2: return REMOVED;
|
||||||
|
case 3: return MODIFIED;
|
||||||
|
}
|
||||||
|
throw new AssertionError("Must not reach here!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Kind kind;
|
||||||
|
private final byte[] resourceBytes;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private ResourceDiff(Kind kind, String name, byte[] resourceBytes) {
|
||||||
|
this.kind = kind;
|
||||||
|
this.name = name;
|
||||||
|
if ((kind == Kind.REMOVED || kind == Kind.MODIFIED) &&
|
||||||
|
resourceBytes == null) {
|
||||||
|
throw new AssertionError("Resource bytes must be set for REMOVED or MODIFIED");
|
||||||
|
}
|
||||||
|
this.resourceBytes = resourceBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Kind getKind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResourceBytes() {
|
||||||
|
return resourceBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ResourceDiff o) {
|
||||||
|
int kindComp = kind.value() - o.kind.value();
|
||||||
|
if (kindComp == 0) {
|
||||||
|
return getName().compareTo(o.getName());
|
||||||
|
} else {
|
||||||
|
return kindComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private Kind kind;
|
||||||
|
private String name;
|
||||||
|
private byte[] resourceBytes;
|
||||||
|
|
||||||
|
public Builder setKind(Kind kind) {
|
||||||
|
this.kind = kind;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Builder setName(String name) {
|
||||||
|
this.name = Objects.requireNonNull(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Builder setResourceBytes(byte[] resourceBytes) {
|
||||||
|
this.resourceBytes = Objects.requireNonNull(resourceBytes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public ResourceDiff build() {
|
||||||
|
if (kind == null || name == null) {
|
||||||
|
throw new AssertionError("kind and name must be set");
|
||||||
|
}
|
||||||
|
switch (kind) {
|
||||||
|
case ADDED:
|
||||||
|
{
|
||||||
|
break; // null bytes for added is OK.
|
||||||
|
}
|
||||||
|
case MODIFIED: // fall-through
|
||||||
|
case REMOVED:
|
||||||
|
{
|
||||||
|
if (resourceBytes == null) {
|
||||||
|
throw new AssertionError("Original bytes needed for MODIFIED, REMOVED!");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new ResourceDiff(kind, name, resourceBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a list of resource diffs to an output stream
|
||||||
|
*
|
||||||
|
* @param diffs The list of resource diffs to write.
|
||||||
|
* @param out The stream to write the serialized bytes to.
|
||||||
|
*/
|
||||||
|
public static void write(List<ResourceDiff> diffs, OutputStream out) throws IOException {
|
||||||
|
/*
|
||||||
|
* Simple binary format:
|
||||||
|
*
|
||||||
|
* <header>|<items>
|
||||||
|
*
|
||||||
|
* ****************************************
|
||||||
|
* HEADER info
|
||||||
|
* ****************************************
|
||||||
|
*
|
||||||
|
* where <header> is ('|' separation for clarity):
|
||||||
|
*
|
||||||
|
* <int>|<int>
|
||||||
|
*
|
||||||
|
* The first integer is the MAGIC, 0xabba. The second integer is the
|
||||||
|
* total number of items.
|
||||||
|
*
|
||||||
|
* *****************************************
|
||||||
|
* ITEMS info
|
||||||
|
* *****************************************
|
||||||
|
*
|
||||||
|
* Each <item> consists of ('|' separation for clarity):
|
||||||
|
*
|
||||||
|
* <short>|<int>|<name-bytes-utf>|<int>|<resource-bytes>
|
||||||
|
*
|
||||||
|
* Where the individual items are:
|
||||||
|
*
|
||||||
|
* <short>:
|
||||||
|
* The value of the respective ResourceDiff.Kind.
|
||||||
|
* <int>:
|
||||||
|
* The length of the name bytes (in UTF-8).
|
||||||
|
* <name-bytes-utf>:
|
||||||
|
* The resource name bytes in UTF-8.
|
||||||
|
* <int>:
|
||||||
|
* The length of the resource bytes. 0 (zero) if no resource bytes.
|
||||||
|
* A.k.a 'null'.
|
||||||
|
* <resource-bytes>:
|
||||||
|
* The bytes of the resource as stored in the jmod files.
|
||||||
|
*/
|
||||||
|
try (DataOutputStream dataOut = new DataOutputStream(out)) {
|
||||||
|
dataOut.writeInt(MAGIC);
|
||||||
|
dataOut.writeInt(diffs.size());
|
||||||
|
for (ResourceDiff d: diffs) {
|
||||||
|
dataOut.writeShort(d.kind.value());
|
||||||
|
byte[] buf = d.name.getBytes(StandardCharsets.UTF_8);
|
||||||
|
dataOut.writeInt(buf.length);
|
||||||
|
dataOut.write(buf);
|
||||||
|
buf = d.resourceBytes;
|
||||||
|
dataOut.writeInt(buf == null ? 0 : buf.length);
|
||||||
|
if (buf != null) {
|
||||||
|
dataOut.write(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a list of resource diffs from an input stream.
|
||||||
|
*
|
||||||
|
* @param in The input stream to read from
|
||||||
|
* @return The list of resource diffs.
|
||||||
|
*/
|
||||||
|
public static List<ResourceDiff> read(InputStream in) throws IOException {
|
||||||
|
/*
|
||||||
|
* See write() for the details how this is being written
|
||||||
|
*/
|
||||||
|
List<ResourceDiff> diffs = new ArrayList<>();
|
||||||
|
try (DataInputStream din = new DataInputStream(in)) {
|
||||||
|
int magic = din.readInt();
|
||||||
|
if (magic != MAGIC) {
|
||||||
|
throw new IllegalArgumentException("Not a ResourceDiff data stream!");
|
||||||
|
}
|
||||||
|
int numItems = din.readInt();
|
||||||
|
for (int i = 0; i < numItems; i++) {
|
||||||
|
Kind k = Kind.fromShort(din.readShort());
|
||||||
|
int numBytes = din.readInt();
|
||||||
|
byte[] buf = readBytesFromStream(din, numBytes);
|
||||||
|
String name = new String(buf, StandardCharsets.UTF_8);
|
||||||
|
numBytes = din.readInt();
|
||||||
|
byte[] resBytes = null;
|
||||||
|
if (numBytes != 0) {
|
||||||
|
resBytes = readBytesFromStream(din, numBytes);
|
||||||
|
}
|
||||||
|
Builder builder = new Builder();
|
||||||
|
builder.setKind(k)
|
||||||
|
.setName(name);
|
||||||
|
if (resBytes != null) {
|
||||||
|
builder.setResourceBytes(resBytes);
|
||||||
|
}
|
||||||
|
diffs.add(builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(diffs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readBytesFromStream(DataInputStream din, int numBytes) throws IOException {
|
||||||
|
byte[] b = new byte[numBytes];
|
||||||
|
for (int i = 0; i < numBytes; i++) {
|
||||||
|
int data = din.read();
|
||||||
|
if (data == -1) {
|
||||||
|
throw new IOException("Short read!");
|
||||||
|
}
|
||||||
|
b[i] = (byte)data;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printDiffs(List<ResourceDiff> diffs) {
|
||||||
|
for (ResourceDiff diff: diffs.stream().sorted().toList()) {
|
||||||
|
switch (diff.getKind()) {
|
||||||
|
case ADDED:
|
||||||
|
System.out.println("Only added in opt: " + diff.getName());
|
||||||
|
break;
|
||||||
|
case MODIFIED:
|
||||||
|
System.out.println("Modified in opt: " + diff.getName());
|
||||||
|
break;
|
||||||
|
case REMOVED:
|
||||||
|
System.out.println("Removed in opt: " + diff.getName());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.tools.jlink.internal.runtimelink;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator.ImageResource;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePool;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||||
|
|
||||||
|
@SuppressWarnings("try")
|
||||||
|
public class ResourcePoolReader implements ImageResource {
|
||||||
|
|
||||||
|
private final ResourcePool pool;
|
||||||
|
|
||||||
|
public ResourcePoolReader(ResourcePool pool) {
|
||||||
|
this.pool = Objects.requireNonNull(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getEntries() {
|
||||||
|
return pool.entries().map(ResourcePoolEntry::path).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getResourceBytes(String name) {
|
||||||
|
return pool.findEntry(name).orElseThrow().contentBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.tools.jlink.internal.runtimelink;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when linking from the run-time image
|
||||||
|
*/
|
||||||
|
public class RuntimeImageLinkException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1848914673073119403L;
|
||||||
|
|
||||||
|
public static enum Reason {
|
||||||
|
PATCH_MODULE, /* link exception due to patched module */
|
||||||
|
MODIFIED_FILE, /* link exception due to modified file */
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String file;
|
||||||
|
private final Reason reason;
|
||||||
|
|
||||||
|
public RuntimeImageLinkException(String file, Reason reason) {
|
||||||
|
this.file = Objects.requireNonNull(file);
|
||||||
|
this.reason = Objects.requireNonNull(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reason getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return reason + ", file: " + file;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
# Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
#
|
#
|
||||||
# This code is free software; you can redistribute it and/or modify it
|
# This code is free software; you can redistribute it and/or modify it
|
||||||
@ -111,10 +111,22 @@ main.extended.help.footer=\
|
|||||||
\ used, one pattern per line\n\
|
\ used, one pattern per line\n\
|
||||||
\n\
|
\n\
|
||||||
|
|
||||||
|
main.runtime.image.linking.cap.enabled=enabled
|
||||||
|
main.runtime.image.linking.cap.disabled=disabled
|
||||||
|
main.runtime.image.linking.cap.sect.header=Capabilities:
|
||||||
|
main.runtime.image.linking.cap.msg=\ Linking from run-time image {0}
|
||||||
|
|
||||||
error.prefix=Error:
|
error.prefix=Error:
|
||||||
warn.prefix=Warning:
|
warn.prefix=Warning:
|
||||||
|
|
||||||
|
err.runtime.link.not.linkable.runtime=This JDK does not support linking from the current run-time image
|
||||||
|
err.runtime.link.jdk.jlink.prohibited=This JDK does not contain packaged modules\
|
||||||
|
\ and cannot be used to create another image with the jdk.jlink module
|
||||||
|
err.runtime.link.packaged.mods=This JDK has no packaged modules.\
|
||||||
|
\ --keep-packaged-modules is not supported
|
||||||
|
err.runtime.link.modified.file={0} has been modified
|
||||||
|
err.runtime.link.patched.module=File {0} not found in the modules image.\
|
||||||
|
\ --patch-module is not supported when linking from the run-time image
|
||||||
err.empty.module.path=empty module path
|
err.empty.module.path=empty module path
|
||||||
err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3}
|
err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3}
|
||||||
err.automatic.module:automatic module cannot be used with jlink: {0} from {1}
|
err.automatic.module:automatic module cannot be used with jlink: {0} from {1}
|
||||||
@ -123,7 +135,7 @@ err.launcher.main.class.empty:launcher main class name cannot be empty: {0}
|
|||||||
err.launcher.module.name.empty:launcher module name cannot be empty: {0}
|
err.launcher.module.name.empty:launcher module name cannot be empty: {0}
|
||||||
err.launcher.value.format:launcher value should be of form <command>=<module>[/<main-class>]: {0}
|
err.launcher.value.format:launcher value should be of form <command>=<module>[/<main-class>]: {0}
|
||||||
err.output.must.be.specified:--output must be specified
|
err.output.must.be.specified:--output must be specified
|
||||||
err.modulepath.must.be.specified:--module-path is not specified and this runtime image does not contain jmods directory.
|
err.modulepath.must.be.specified:--module-path is not specified and this run-time image does not contain a jmods directory
|
||||||
err.mods.must.be.specified:no modules specified to {0}
|
err.mods.must.be.specified:no modules specified to {0}
|
||||||
err.path.not.found=path not found: {0}
|
err.path.not.found=path not found: {0}
|
||||||
err.path.not.valid=invalid path: {0}
|
err.path.not.valid=invalid path: {0}
|
||||||
@ -157,3 +169,6 @@ warn.provider.notfound=No provider found for service specified to --suggest-prov
|
|||||||
no.suggested.providers=--bind-services option is specified. No additional providers suggested.
|
no.suggested.providers=--bind-services option is specified. No additional providers suggested.
|
||||||
suggested.providers.header=Suggested providers
|
suggested.providers.header=Suggested providers
|
||||||
providers.header=Providers
|
providers.header=Providers
|
||||||
|
|
||||||
|
runtime.link.info=Linking based on the current run-time image
|
||||||
|
runtime.link.jprt.path.extra=(run-time image)
|
||||||
|
@ -86,7 +86,9 @@ requires.properties= \
|
|||||||
vm.flagless \
|
vm.flagless \
|
||||||
container.support \
|
container.support \
|
||||||
systemd.support \
|
systemd.support \
|
||||||
jdk.containerized
|
jdk.containerized \
|
||||||
|
jlink.runtime.linkable \
|
||||||
|
jlink.packagedModules
|
||||||
|
|
||||||
# Minimum jtreg version
|
# Minimum jtreg version
|
||||||
requiredVersion=7.4+1
|
requiredVersion=7.4+1
|
||||||
|
@ -102,7 +102,9 @@ requires.properties= \
|
|||||||
systemd.support \
|
systemd.support \
|
||||||
release.implementor \
|
release.implementor \
|
||||||
jdk.containerized \
|
jdk.containerized \
|
||||||
jdk.foreign.linker
|
jdk.foreign.linker \
|
||||||
|
jlink.runtime.linkable \
|
||||||
|
jlink.packagedModules
|
||||||
|
|
||||||
# Minimum jtreg version
|
# Minimum jtreg version
|
||||||
requiredVersion=7.4+1
|
requiredVersion=7.4+1
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8159927
|
* @bug 8159927
|
||||||
* @modules java.base/jdk.internal.util
|
* @modules java.base/jdk.internal.util
|
||||||
|
* @requires jlink.packagedModules
|
||||||
* @run main JmodExcludedFiles
|
* @run main JmodExcludedFiles
|
||||||
* @summary Test that JDK JMOD files do not include native debug symbols
|
* @summary Test that JDK JMOD files do not include native debug symbols
|
||||||
*/
|
*/
|
||||||
|
@ -34,11 +34,12 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import jdk.tools.jlink.builder.ImageBuilder;
|
||||||
import jdk.tools.jlink.internal.Archive;
|
import jdk.tools.jlink.internal.Archive;
|
||||||
|
import jdk.tools.jlink.internal.ExecutableImage;
|
||||||
import jdk.tools.jlink.internal.ImageFileCreator;
|
import jdk.tools.jlink.internal.ImageFileCreator;
|
||||||
import jdk.tools.jlink.internal.ImagePluginStack;
|
import jdk.tools.jlink.internal.ImagePluginStack;
|
||||||
import jdk.tools.jlink.internal.ExecutableImage;
|
|
||||||
import jdk.tools.jlink.builder.ImageBuilder;
|
|
||||||
import jdk.tools.jlink.plugin.ResourcePool;
|
import jdk.tools.jlink.plugin.ResourcePool;
|
||||||
|
|
||||||
|
|
||||||
@ -223,6 +224,6 @@ public class ImageFileCreatorTest {
|
|||||||
ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(),
|
ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(),
|
||||||
null, false);
|
null, false);
|
||||||
|
|
||||||
ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack);
|
ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack, false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,7 +25,6 @@ import java.io.File;
|
|||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -38,20 +37,18 @@ import java.util.Map;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import jdk.tools.jlink.internal.Jlink;
|
|
||||||
import jdk.tools.jlink.internal.JlinkTask;
|
|
||||||
import jdk.tools.jlink.builder.DefaultImageBuilder;
|
import jdk.tools.jlink.builder.DefaultImageBuilder;
|
||||||
import jdk.tools.jlink.internal.Platform;
|
|
||||||
import jdk.tools.jlink.plugin.ResourcePool;
|
|
||||||
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
|
||||||
import jdk.tools.jlink.plugin.Plugin;
|
|
||||||
import jdk.tools.jlink.internal.ExecutableImage;
|
import jdk.tools.jlink.internal.ExecutableImage;
|
||||||
|
import jdk.tools.jlink.internal.Jlink;
|
||||||
import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
|
import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
|
||||||
import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
|
import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
|
||||||
|
import jdk.tools.jlink.internal.JlinkTask;
|
||||||
|
import jdk.tools.jlink.internal.Platform;
|
||||||
import jdk.tools.jlink.internal.PostProcessor;
|
import jdk.tools.jlink.internal.PostProcessor;
|
||||||
import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
|
import jdk.tools.jlink.plugin.Plugin;
|
||||||
import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin;
|
import jdk.tools.jlink.plugin.ResourcePool;
|
||||||
|
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||||
import tests.Helper;
|
import tests.Helper;
|
||||||
import tests.JImageGenerator;
|
import tests.JImageGenerator;
|
||||||
|
|
||||||
@ -74,8 +71,6 @@ import tests.JImageGenerator;
|
|||||||
*/
|
*/
|
||||||
public class IntegrationTest {
|
public class IntegrationTest {
|
||||||
|
|
||||||
private static final List<Integer> ordered = new ArrayList<>();
|
|
||||||
|
|
||||||
public static class MyPostProcessor implements PostProcessor, Plugin {
|
public static class MyPostProcessor implements PostProcessor, Plugin {
|
||||||
|
|
||||||
public static final String NAME = "mypostprocessor";
|
public static final String NAME = "mypostprocessor";
|
||||||
@ -162,7 +157,7 @@ public class IntegrationTest {
|
|||||||
limits.add("java.management");
|
limits.add("java.management");
|
||||||
JlinkConfiguration config = new Jlink.JlinkConfiguration(output,
|
JlinkConfiguration config = new Jlink.JlinkConfiguration(output,
|
||||||
mods,
|
mods,
|
||||||
JlinkTask.newModuleFinder(modulePaths, limits, mods));
|
JlinkTask.newModuleFinder(modulePaths, limits, mods), false, false, false);
|
||||||
|
|
||||||
List<Plugin> lst = new ArrayList<>();
|
List<Plugin> lst = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -21,14 +21,16 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import jdk.test.lib.compiler.CompilerUtils;
|
|
||||||
import tests.JImageGenerator;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import jdk.test.lib.compiler.CompilerUtils;
|
||||||
|
import jdk.tools.jlink.internal.LinkableRuntimeImage;
|
||||||
|
import tests.JImageGenerator;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @summary Make sure that modules can be linked using jlink
|
* @summary Make sure that modules can be linked using jlink
|
||||||
@ -54,10 +56,6 @@ public class JLinkDedupTestBatchSizeOne {
|
|||||||
private static final Path SRC_DIR = Paths.get(TEST_SRC, "dedup", "src");
|
private static final Path SRC_DIR = Paths.get(TEST_SRC, "dedup", "src");
|
||||||
private static final Path MODS_DIR = Paths.get("mods");
|
private static final Path MODS_DIR = Paths.get("mods");
|
||||||
|
|
||||||
private static final String MODULE_PATH =
|
|
||||||
Paths.get(JAVA_HOME, "jmods").toString() +
|
|
||||||
File.pathSeparator + MODS_DIR.toString();
|
|
||||||
|
|
||||||
// the names of the modules in this test
|
// the names of the modules in this test
|
||||||
private static String[] modules = new String[]{"m1", "m2", "m3", "m4"};
|
private static String[] modules = new String[]{"m1", "m2", "m3", "m4"};
|
||||||
|
|
||||||
@ -69,8 +67,13 @@ public class JLinkDedupTestBatchSizeOne {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void compileAll() throws Throwable {
|
private static String modulePath(boolean linkableRuntime) {
|
||||||
if (!hasJmods()) return;
|
return (linkableRuntime ? "" : (Paths.get(JAVA_HOME, "jmods").toString() +
|
||||||
|
File.pathSeparator)) + MODS_DIR.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compileAll(boolean linkableRuntime) throws Throwable {
|
||||||
|
if (!linkableRuntime && !hasJmods()) return;
|
||||||
|
|
||||||
for (String mn : modules) {
|
for (String mn : modules) {
|
||||||
Path msrc = SRC_DIR.resolve(mn);
|
Path msrc = SRC_DIR.resolve(mn);
|
||||||
@ -80,11 +83,15 @@ public class JLinkDedupTestBatchSizeOne {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
compileAll();
|
boolean linkableRuntime = LinkableRuntimeImage.isLinkableRuntime();
|
||||||
|
System.out.println("Running test on " +
|
||||||
|
(linkableRuntime ? "enabled" : "disabled") +
|
||||||
|
" capability of linking from the run-time image.");
|
||||||
|
compileAll(linkableRuntime);
|
||||||
Path image = Paths.get("bug8311591");
|
Path image = Paths.get("bug8311591");
|
||||||
|
|
||||||
JImageGenerator.getJLinkTask()
|
JImageGenerator.getJLinkTask()
|
||||||
.modulePath(MODULE_PATH)
|
.modulePath(modulePath(linkableRuntime))
|
||||||
.output(image.resolve("out-jlink-dedup"))
|
.output(image.resolve("out-jlink-dedup"))
|
||||||
.addMods("m1")
|
.addMods("m1")
|
||||||
.addMods("m2")
|
.addMods("m2")
|
||||||
|
78
test/jdk/tools/jlink/JLinkHelpCapabilityTest.java
Normal file
78
test/jdk/tools/jlink/JLinkHelpCapabilityTest.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.spi.ToolProvider;
|
||||||
|
|
||||||
|
import jdk.tools.jlink.internal.LinkableRuntimeImage;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test jlink --help for capability output
|
||||||
|
* @modules jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* @requires vm.compMode != "Xcomp"
|
||||||
|
* @run main/othervm -Duser.language=en JLinkHelpCapabilityTest
|
||||||
|
*/
|
||||||
|
public class JLinkHelpCapabilityTest {
|
||||||
|
static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
|
||||||
|
.orElseThrow(() ->
|
||||||
|
new RuntimeException("jlink tool not found")
|
||||||
|
);
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
boolean runtimeLinkCap = LinkableRuntimeImage.isLinkableRuntime();
|
||||||
|
String capabilities = String.format("Linking from run-time image %s",
|
||||||
|
runtimeLinkCap ? "enabled" : "disabled");
|
||||||
|
{
|
||||||
|
// Verify capability in --help output
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
PrintWriter pw = new PrintWriter(writer);
|
||||||
|
JLINK_TOOL.run(pw, pw, "--help");
|
||||||
|
String output = writer.toString().trim();
|
||||||
|
String lines[] = output.split("\n");
|
||||||
|
String capabilitiesMsg = null;
|
||||||
|
boolean seenCap = false;
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].startsWith("Capabilities:")) {
|
||||||
|
seenCap = true;
|
||||||
|
continue; // skip 'Capabilities:'
|
||||||
|
}
|
||||||
|
if (!seenCap) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Line after capabilities is the message we care about
|
||||||
|
capabilitiesMsg = lines[i].trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("DEBUG: Capabilities:");
|
||||||
|
System.out.println("DEBUG: " + capabilitiesMsg);
|
||||||
|
if (!capabilities.equals(capabilitiesMsg)) {
|
||||||
|
System.err.println(output);
|
||||||
|
throw new AssertionError("'--help': Capabilities mismatch. Expected: '" +
|
||||||
|
capabilities +"' but got '" + capabilitiesMsg + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,7 @@
|
|||||||
* @summary jlink should use the version from java.base.jmod to find modules
|
* @summary jlink should use the version from java.base.jmod to find modules
|
||||||
* @bug 8185130
|
* @bug 8185130
|
||||||
* @summary jlink should throw error if target image and current JDK versions don't match
|
* @summary jlink should throw error if target image and current JDK versions don't match
|
||||||
|
* @requires jlink.packagedModules
|
||||||
* @modules java.base/jdk.internal.module
|
* @modules java.base/jdk.internal.module
|
||||||
* @library /test/lib
|
* @library /test/lib
|
||||||
* @build jdk.test.lib.process.* CheckRuntimeVersion
|
* @build jdk.test.lib.process.* CheckRuntimeVersion
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||||
|
import static java.lang.constant.ConstantDescs.CD_int;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.classfile.ClassFile;
|
import java.lang.classfile.ClassFile;
|
||||||
import java.lang.constant.MethodTypeDesc;
|
import java.lang.constant.MethodTypeDesc;
|
||||||
@ -32,16 +35,15 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.testng.Assert;
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.BeforeTest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import jdk.tools.jlink.internal.LinkableRuntimeImage;
|
||||||
import tests.Helper;
|
import tests.Helper;
|
||||||
import tests.JImageGenerator;
|
import tests.JImageGenerator;
|
||||||
import tests.JImageValidator;
|
import tests.JImageValidator;
|
||||||
import tests.Result;
|
import tests.Result;
|
||||||
|
|
||||||
import org.testng.annotations.BeforeTest;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
|
||||||
import static java.lang.constant.ConstantDescs.CD_int;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
@ -63,12 +65,20 @@ public class GenerateJLIClassesPluginTest {
|
|||||||
|
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
public static void setup() throws Exception {
|
public static void setup() throws Exception {
|
||||||
helper = Helper.newHelper();
|
boolean isLinkableRuntime = LinkableRuntimeImage.isLinkableRuntime();
|
||||||
|
System.out.println("DEBUG: Tests run on " +
|
||||||
|
(isLinkableRuntime ? "enabled" : "disabled") +
|
||||||
|
" capability of linking from the run-time image.");
|
||||||
|
System.out.println("DEBUG: default module-path, 'jmods', " +
|
||||||
|
(Helper.jdkHasPackagedModules() ? "" : "NOT ") +
|
||||||
|
"present.");
|
||||||
|
helper = Helper.newHelper(isLinkableRuntime);
|
||||||
if (helper == null) {
|
if (helper == null) {
|
||||||
|
// In case of no linkable run-time image and also no packaged
|
||||||
|
// modules, helper will be null.
|
||||||
System.err.println("Test not run");
|
System.err.println("Test not run");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
helper.generateDefaultModules();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -79,7 +89,6 @@ public class GenerateJLIClassesPluginTest {
|
|||||||
String fileString = "[SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_" + species + " (salvaged)\n";
|
String fileString = "[SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_" + species + " (salvaged)\n";
|
||||||
Files.write(baseFile, fileString.getBytes(Charset.defaultCharset()));
|
Files.write(baseFile, fileString.getBytes(Charset.defaultCharset()));
|
||||||
Result result = JImageGenerator.getJLinkTask()
|
Result result = JImageGenerator.getJLinkTask()
|
||||||
.modulePath(helper.defaultModulePath())
|
|
||||||
.output(helper.createNewImageDir("generate-jli-file"))
|
.output(helper.createNewImageDir("generate-jli-file"))
|
||||||
.option("--generate-jli-classes=@" + baseFile.toString())
|
.option("--generate-jli-classes=@" + baseFile.toString())
|
||||||
.addMods("java.base")
|
.addMods("java.base")
|
||||||
@ -105,7 +114,6 @@ public class GenerateJLIClassesPluginTest {
|
|||||||
fileString = "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeVirtual L_L (success)\n";
|
fileString = "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeVirtual L_L (success)\n";
|
||||||
Files.write(failFile, fileString.getBytes(Charset.defaultCharset()));
|
Files.write(failFile, fileString.getBytes(Charset.defaultCharset()));
|
||||||
Result result = JImageGenerator.getJLinkTask()
|
Result result = JImageGenerator.getJLinkTask()
|
||||||
.modulePath(helper.defaultModulePath())
|
|
||||||
.output(helper.createNewImageDir("invalid-signature"))
|
.output(helper.createNewImageDir("invalid-signature"))
|
||||||
.option("--generate-jli-classes=@" + failFile.toString())
|
.option("--generate-jli-classes=@" + failFile.toString())
|
||||||
.addMods("java.base")
|
.addMods("java.base")
|
||||||
@ -118,7 +126,6 @@ public class GenerateJLIClassesPluginTest {
|
|||||||
@Test
|
@Test
|
||||||
public static void nonExistentTraceFile() throws IOException {
|
public static void nonExistentTraceFile() throws IOException {
|
||||||
Result result = JImageGenerator.getJLinkTask()
|
Result result = JImageGenerator.getJLinkTask()
|
||||||
.modulePath(helper.defaultModulePath())
|
|
||||||
.output(helper.createNewImageDir("non-existent-tracefile"))
|
.output(helper.createNewImageDir("non-existent-tracefile"))
|
||||||
.option("--generate-jli-classes=@NON_EXISTENT_FILE")
|
.option("--generate-jli-classes=@NON_EXISTENT_FILE")
|
||||||
.addMods("java.base")
|
.addMods("java.base")
|
||||||
@ -134,7 +141,6 @@ public class GenerateJLIClassesPluginTest {
|
|||||||
Path invokersTrace = Files.createTempFile("invokers", "trace");
|
Path invokersTrace = Files.createTempFile("invokers", "trace");
|
||||||
Files.writeString(invokersTrace, fileString, Charset.defaultCharset());
|
Files.writeString(invokersTrace, fileString, Charset.defaultCharset());
|
||||||
Result result = JImageGenerator.getJLinkTask()
|
Result result = JImageGenerator.getJLinkTask()
|
||||||
.modulePath(helper.defaultModulePath())
|
|
||||||
.output(helper.createNewImageDir("jli-invokers"))
|
.output(helper.createNewImageDir("jli-invokers"))
|
||||||
.option("--generate-jli-classes=@" + invokersTrace.toString())
|
.option("--generate-jli-classes=@" + invokersTrace.toString())
|
||||||
.addMods("java.base")
|
.addMods("java.base")
|
||||||
@ -183,4 +189,5 @@ public class GenerateJLIClassesPluginTest {
|
|||||||
.map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class")
|
.map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class")
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,25 +22,27 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jdk.tools.jlink.plugin.PluginException;
|
import jdk.tools.jlink.internal.LinkableRuntimeImage;
|
||||||
import jdk.tools.jlink.internal.TaskHelper;
|
import jdk.tools.jlink.internal.TaskHelper;
|
||||||
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
|
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
|
||||||
|
import jdk.tools.jlink.plugin.PluginException;
|
||||||
import tests.Helper;
|
import tests.Helper;
|
||||||
import tests.JImageGenerator;
|
import tests.JImageGenerator;
|
||||||
import tests.JImageValidator;
|
import tests.JImageValidator;
|
||||||
import tests.Result;
|
import tests.Result;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8152143 8152704 8155649 8165804 8185841 8176841 8190918
|
* @bug 8152143 8152704 8155649 8165804 8185841 8176841 8190918
|
||||||
* 8179071 8202537 8221432 8222098 8251317 8258794 8265315
|
* 8179071 8202537 8221432 8222098 8251317 8258794 8265315
|
||||||
* 8296248 8306116 8174269 8333582
|
* 8296248 8306116 8174269
|
||||||
* @summary IncludeLocalesPlugin tests
|
* @summary IncludeLocalesPlugin tests
|
||||||
* @author Naoto Sato
|
* @author Naoto Sato
|
||||||
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
@ -59,14 +61,14 @@ import tests.Result;
|
|||||||
*/
|
*/
|
||||||
public class IncludeLocalesPluginTest {
|
public class IncludeLocalesPluginTest {
|
||||||
|
|
||||||
private final static String moduleName = "IncludeLocalesTest";
|
private static final String moduleName = "IncludeLocalesTest";
|
||||||
private static Helper helper;
|
private static Helper helper;
|
||||||
private final static int INCLUDE_LOCALES_OPTION = 0;
|
private static final int INCLUDE_LOCALES_OPTION = 0;
|
||||||
private final static int ADDMODS_OPTION = 1;
|
private static final int ADDMODS_OPTION = 1;
|
||||||
private final static int EXPECTED_LOCATIONS = 2;
|
private static final int EXPECTED_LOCATIONS = 2;
|
||||||
private final static int UNEXPECTED_PATHS = 3;
|
private static final int UNEXPECTED_PATHS = 3;
|
||||||
private final static int AVAILABLE_LOCALES = 4;
|
private static final int AVAILABLE_LOCALES = 4;
|
||||||
private final static int ERROR_MESSAGE = 5;
|
private static final int ERROR_MESSAGE = 5;
|
||||||
|
|
||||||
private static int errors;
|
private static int errors;
|
||||||
|
|
||||||
@ -413,11 +415,18 @@ public class IncludeLocalesPluginTest {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
helper = Helper.newHelper();
|
boolean isLinkableRuntime = LinkableRuntimeImage.isLinkableRuntime();
|
||||||
|
System.out.println("Running test on " +
|
||||||
|
(isLinkableRuntime ? "enabled" : "disabled") +
|
||||||
|
" capability of linking from the run-time image.");
|
||||||
|
System.out.println("Default module-path, 'jmods', " +
|
||||||
|
(Helper.jdkHasPackagedModules() ? "" : "NOT ") +
|
||||||
|
"present.");
|
||||||
|
|
||||||
|
helper = Helper.newHelper(isLinkableRuntime);
|
||||||
if (helper == null) {
|
if (helper == null) {
|
||||||
throw new RuntimeException("Helper could not be initialized");
|
throw new RuntimeException("Helper could not be initialized");
|
||||||
}
|
}
|
||||||
helper.generateDefaultModules();
|
|
||||||
|
|
||||||
for (Object[] data : testData) {
|
for (Object[] data : testData) {
|
||||||
// create image for each test data
|
// create image for each test data
|
||||||
@ -425,14 +434,12 @@ public class IncludeLocalesPluginTest {
|
|||||||
if (data[INCLUDE_LOCALES_OPTION].toString().isEmpty()) {
|
if (data[INCLUDE_LOCALES_OPTION].toString().isEmpty()) {
|
||||||
System.out.println("Invoking jlink with no --include-locales option");
|
System.out.println("Invoking jlink with no --include-locales option");
|
||||||
result = JImageGenerator.getJLinkTask()
|
result = JImageGenerator.getJLinkTask()
|
||||||
.modulePath(helper.defaultModulePath())
|
|
||||||
.output(helper.createNewImageDir(moduleName))
|
.output(helper.createNewImageDir(moduleName))
|
||||||
.addMods((String) data[ADDMODS_OPTION])
|
.addMods((String) data[ADDMODS_OPTION])
|
||||||
.call();
|
.call();
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Invoking jlink with \"" + data[INCLUDE_LOCALES_OPTION] + "\"");
|
System.out.println("Invoking jlink with \"" + data[INCLUDE_LOCALES_OPTION] + "\"");
|
||||||
result = JImageGenerator.getJLinkTask()
|
result = JImageGenerator.getJLinkTask()
|
||||||
.modulePath(helper.defaultModulePath())
|
|
||||||
.output(helper.createNewImageDir(moduleName))
|
.output(helper.createNewImageDir(moduleName))
|
||||||
.addMods((String) data[ADDMODS_OPTION])
|
.addMods((String) data[ADDMODS_OPTION])
|
||||||
.option((String) data[INCLUDE_LOCALES_OPTION])
|
.option((String) data[INCLUDE_LOCALES_OPTION])
|
||||||
|
@ -0,0 +1,705 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.FileVisitor;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
import jdk.tools.jlink.internal.LinkableRuntimeImage;
|
||||||
|
import tests.Helper;
|
||||||
|
import tests.JImageGenerator;
|
||||||
|
import tests.JImageGenerator.JLinkTask;
|
||||||
|
import tests.JImageValidator;
|
||||||
|
|
||||||
|
public abstract class AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
protected static final boolean DEBUG = true;
|
||||||
|
|
||||||
|
public void run() throws Exception {
|
||||||
|
boolean isLinkableRuntime = LinkableRuntimeImage.isLinkableRuntime();
|
||||||
|
Helper helper = Helper.newHelper(isLinkableRuntime);
|
||||||
|
if (helper == null) {
|
||||||
|
System.err.println(AbstractLinkableRuntimeTest.class.getSimpleName() +
|
||||||
|
": Test not run");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runTest(helper, isLinkableRuntime);
|
||||||
|
System.out.println(getClass().getSimpleName() + " PASSED!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main test entry point that actual tests ought to override.
|
||||||
|
*
|
||||||
|
* @param helper The jlink helper
|
||||||
|
* @param isLinkableRuntime {@code true} iff the JDK build under test already
|
||||||
|
* includes the linkable runtime capability in jlink.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
abstract void runTest(Helper helper, boolean isLinkableRuntime) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure 'java --list-modules' lists the correct set of modules in the given
|
||||||
|
* image.
|
||||||
|
*
|
||||||
|
* @param jlinkImage
|
||||||
|
* @param expectedModules
|
||||||
|
*/
|
||||||
|
protected void verifyListModules(Path image,
|
||||||
|
List<String> expectedModules) throws Exception {
|
||||||
|
OutputAnalyzer out = runJavaCmd(image, List.of("--list-modules"));
|
||||||
|
List<String> actual = parseListMods(out.getStdout());
|
||||||
|
Collections.sort(actual);
|
||||||
|
if (!expectedModules.equals(actual)) {
|
||||||
|
throw new AssertionError("Different modules! Expected " + expectedModules + " got: " + actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OutputAnalyzer runJavaCmd(Path image, List<String> options) throws Exception {
|
||||||
|
Path targetJava = image.resolve("bin").resolve(getJava());
|
||||||
|
List<String> cmd = new ArrayList<>();
|
||||||
|
cmd.add(targetJava.toString());
|
||||||
|
for (String opt: options) {
|
||||||
|
cmd.add(opt);
|
||||||
|
}
|
||||||
|
List<String> javaCmd = Collections.unmodifiableList(cmd);
|
||||||
|
OutputAnalyzer out;
|
||||||
|
try {
|
||||||
|
out = ProcessTools.executeCommand(javaCmd.toArray(new String[0]));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new Exception("Process failed to execute", e);
|
||||||
|
}
|
||||||
|
if (out.getExitValue() != 0) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.err.println("Process stdout was: ");
|
||||||
|
System.err.println(out.getStdout());
|
||||||
|
System.err.println("Process stderr was: ");
|
||||||
|
System.err.println(out.getStderr());
|
||||||
|
}
|
||||||
|
throw new AssertionError("'" + javaCmd.stream().collect(Collectors.joining(" ")) + "'"
|
||||||
|
+ " expected to succeed!");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path createJavaImageRuntimeLink(BaseJlinkSpec baseSpec) throws Exception {
|
||||||
|
return createJavaImageRuntimeLink(baseSpec, Collections.emptySet() /* exclude all jmods */);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path createJavaImageRuntimeLink(BaseJlinkSpec baseSpec, Set<String> excludedJmods) throws Exception {
|
||||||
|
// Be sure we have a JDK without JMODs
|
||||||
|
Path runtimeJlinkImage = createRuntimeLinkImage(baseSpec, excludedJmods);
|
||||||
|
|
||||||
|
// On Windows jvm.dll is in 'bin' after the jlink
|
||||||
|
Path libjvm = Path.of((isWindows() ? "bin" : "lib"), "server", System.mapLibraryName("jvm"));
|
||||||
|
JlinkSpecBuilder builder = new JlinkSpecBuilder();
|
||||||
|
// And expect libjvm (not part of the jimage) to be present in the resulting image
|
||||||
|
builder.expectedFile(libjvm.toString())
|
||||||
|
.helper(baseSpec.getHelper())
|
||||||
|
.name(baseSpec.getName())
|
||||||
|
.validatingModule(baseSpec.getValidatingModule())
|
||||||
|
.imagePath(runtimeJlinkImage)
|
||||||
|
.expectedLocation("/java.base/java/lang/String.class");
|
||||||
|
for (String m: baseSpec.getModules()) {
|
||||||
|
builder.addModule(m);
|
||||||
|
}
|
||||||
|
for (String extra: baseSpec.getExtraOptions()) {
|
||||||
|
builder.extraJlinkOpt(extra);
|
||||||
|
}
|
||||||
|
return jlinkUsingImage(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path jlinkUsingImage(JlinkSpec spec) throws Exception {
|
||||||
|
return jlinkUsingImage(spec, new RuntimeLinkOutputAnalyzerHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path jlinkUsingImage(JlinkSpec spec, OutputAnalyzerHandler handler) throws Exception {
|
||||||
|
return jlinkUsingImage(spec, handler, new DefaultSuccessExitPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path jlinkUsingImage(JlinkSpec spec, OutputAnalyzerHandler handler, Predicate<OutputAnalyzer> exitChecker) throws Exception {
|
||||||
|
String generatedImage = "target-run-time-" + spec.getName();
|
||||||
|
Path targetImageDir = spec.getHelper().createNewImageDir(generatedImage);
|
||||||
|
Path targetJlink = spec.getImageToUse().resolve("bin").resolve(getJlink());
|
||||||
|
String[] jlinkCmdArray = new String[] {
|
||||||
|
targetJlink.toString(),
|
||||||
|
"--output", targetImageDir.toString(),
|
||||||
|
"--verbose",
|
||||||
|
"--add-modules", spec.getModules().stream().collect(Collectors.joining(","))
|
||||||
|
};
|
||||||
|
List<String> jlinkCmd = new ArrayList<>();
|
||||||
|
jlinkCmd.addAll(Arrays.asList(jlinkCmdArray));
|
||||||
|
if (spec.getExtraJlinkOpts() != null && !spec.getExtraJlinkOpts().isEmpty()) {
|
||||||
|
jlinkCmd.addAll(spec.getExtraJlinkOpts());
|
||||||
|
}
|
||||||
|
if (spec.getModulePath() != null) {
|
||||||
|
for (String mp: spec.getModulePath()) {
|
||||||
|
jlinkCmd.add("--module-path");
|
||||||
|
jlinkCmd.add(mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jlinkCmd = Collections.unmodifiableList(jlinkCmd); // freeze
|
||||||
|
System.out.println("DEBUG: run-time image based jlink command: " +
|
||||||
|
jlinkCmd.stream().collect(Collectors.joining(" ")));
|
||||||
|
OutputAnalyzer analyzer = null;
|
||||||
|
try {
|
||||||
|
analyzer = ProcessTools.executeProcess(jlinkCmd.toArray(new String[0]));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new AssertionError("Executing process failed!", t);
|
||||||
|
}
|
||||||
|
if (!exitChecker.test(analyzer)) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.err.println("Process stdout was: ");
|
||||||
|
System.err.println(analyzer.getStdout());
|
||||||
|
System.err.println("Process stderr was: ");
|
||||||
|
System.err.println(analyzer.getStderr());
|
||||||
|
}
|
||||||
|
// if the exit checker failed, we expected the other outcome
|
||||||
|
// i.e. fail for success and success for fail.
|
||||||
|
boolean successExit = analyzer.getExitValue() == 0;
|
||||||
|
String msg = String.format("Expected jlink to %s given a jmodless image. Exit code was: %d",
|
||||||
|
(successExit ? "fail" : "pass"), analyzer.getExitValue());
|
||||||
|
throw new AssertionError(msg);
|
||||||
|
}
|
||||||
|
handler.handleAnalyzer(analyzer); // Give tests a chance to process in/output
|
||||||
|
|
||||||
|
// validate the resulting image; Includes running 'java -version', only do this
|
||||||
|
// if the jlink succeeded.
|
||||||
|
if (analyzer.getExitValue() == 0) {
|
||||||
|
JImageValidator validator = new JImageValidator(spec.getValidatingModule(), spec.getExpectedLocations(),
|
||||||
|
targetImageDir.toFile(), spec.getUnexpectedLocations(), Collections.emptyList(), spec.getExpectedFiles());
|
||||||
|
validator.validate(); // This doesn't validate locations
|
||||||
|
if (!spec.getExpectedLocations().isEmpty() || !spec.getUnexpectedLocations().isEmpty()) {
|
||||||
|
JImageValidator.validate(targetImageDir.resolve("lib").resolve("modules"), spec.getExpectedLocations(), spec.getUnexpectedLocations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return targetImageDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the test for execution. This assumes the current runtime
|
||||||
|
* supports linking from it. However, since the 'jmods' dir might be present
|
||||||
|
* (default jmods module path), the 'jmods' directory needs to get removed
|
||||||
|
* to provoke actual linking from the run-time image.
|
||||||
|
*
|
||||||
|
* @param baseSpec
|
||||||
|
* @return A path to a JDK that is capable for linking from the run-time
|
||||||
|
* image.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected Path createRuntimeLinkImage(BaseJlinkSpec baseSpec) throws Exception {
|
||||||
|
return createRuntimeLinkImage(baseSpec, Collections.emptySet() /* exclude all jmods */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the test for execution. Creates a JDK with a jlink that has the
|
||||||
|
* capability to link from the run-time image (if needed). It further
|
||||||
|
* ensures that if packaged modules ('jmods' dir) are present, to remove
|
||||||
|
* them entirely or as specified in the {@link excludedJmodFiles} set. If
|
||||||
|
* that set is empty, all packaged modules will be removed. Note that with
|
||||||
|
* packaged modules present no run-time image based linking would be done.
|
||||||
|
*
|
||||||
|
* @param baseSpec
|
||||||
|
* The specification for the custom - run-time image link capable
|
||||||
|
* - JDK to create via jlink (if any)
|
||||||
|
* @param excludedJmods
|
||||||
|
* The set of jmod files to exclude in the base JDK. Empty set if
|
||||||
|
* all JMODs should be removed.
|
||||||
|
* @return A path to a JDK, including jdk.jlink, that has the run-time image
|
||||||
|
* link capability.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected Path createRuntimeLinkImage(BaseJlinkSpec baseSpec,
|
||||||
|
Set<String> excludedJmodFiles) throws Exception {
|
||||||
|
// Depending on the shape of the JDK under test, we either only filter
|
||||||
|
// jmod files or create a run-time image link capable JDK on-the-fly.
|
||||||
|
Path from = null;
|
||||||
|
Path runtimeJlinkImage = null;
|
||||||
|
String finalName = baseSpec.getName() + "-jlink";
|
||||||
|
if (baseSpec.isLinkableRuntime()) {
|
||||||
|
// The build is already run-time image link capable
|
||||||
|
String javaHome = System.getProperty("java.home");
|
||||||
|
from = Path.of(javaHome);
|
||||||
|
} else {
|
||||||
|
// Create a run-time image capable JDK using --generate-linkable-runtime
|
||||||
|
Path tempRuntimeImage = Path.of(finalName + "-tmp");
|
||||||
|
JLinkTask task = JImageGenerator.getJLinkTask();
|
||||||
|
task.output(tempRuntimeImage)
|
||||||
|
.addMods("jdk.jlink") // jdk.jlink module is always needed for the test
|
||||||
|
.option("--generate-linkable-runtime");
|
||||||
|
if (baseJDKhasPackagedModules()) {
|
||||||
|
Path jmodsPath = tempRuntimeImage.resolve("jmods");
|
||||||
|
task.option("--keep-packaged-modules=" + jmodsPath);
|
||||||
|
}
|
||||||
|
for (String module: baseSpec.getModules()) {
|
||||||
|
task.addMods(module);
|
||||||
|
}
|
||||||
|
task.call().assertSuccess();
|
||||||
|
from = tempRuntimeImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the target directory
|
||||||
|
runtimeJlinkImage = baseSpec.getHelper().createNewImageDir(finalName);
|
||||||
|
|
||||||
|
// Remove JMODs as needed for the test
|
||||||
|
copyJDKTreeWithoutSpecificJmods(from, runtimeJlinkImage, excludedJmodFiles);
|
||||||
|
// Verify the base image is actually without desired packaged modules
|
||||||
|
if (excludedJmodFiles.isEmpty()) {
|
||||||
|
if (Files.exists(runtimeJlinkImage.resolve("jmods"))) {
|
||||||
|
throw new AssertionError("Must not contain 'jmods' directory");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Path basePath = runtimeJlinkImage.resolve("jmods");
|
||||||
|
for (String jmodFile: excludedJmodFiles) {
|
||||||
|
Path unexpectedFile = basePath.resolve(Path.of(jmodFile));
|
||||||
|
if (Files.exists(unexpectedFile)) {
|
||||||
|
throw new AssertionError("Must not contain jmod: " + unexpectedFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runtimeJlinkImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean baseJDKhasPackagedModules() {
|
||||||
|
Path jmodsPath = Path.of(System.getProperty("java.home"), "jmods");
|
||||||
|
return jmodsPath.toFile().exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyJDKTreeWithoutSpecificJmods(Path from,
|
||||||
|
Path to,
|
||||||
|
Set<String> excludedJmods) throws Exception {
|
||||||
|
if (Files.exists(to)) {
|
||||||
|
throw new AssertionError("Expected target dir '" + to + "' to exist");
|
||||||
|
}
|
||||||
|
FileVisitor<Path> fileVisitor = null;
|
||||||
|
if (excludedJmods.isEmpty()) {
|
||||||
|
fileVisitor = new ExcludeAllJmodsFileVisitor(from, to);
|
||||||
|
} else {
|
||||||
|
fileVisitor = new FileExcludingFileVisitor(excludedJmods,
|
||||||
|
from,
|
||||||
|
to);
|
||||||
|
}
|
||||||
|
Files.walkFileTree(from, fileVisitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> parseListMods(String output) throws Exception {
|
||||||
|
List<String> outputLines = new ArrayList<>();
|
||||||
|
try (Scanner lineScan = new Scanner(output)) {
|
||||||
|
while (lineScan.hasNextLine()) {
|
||||||
|
outputLines.add(lineScan.nextLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputLines.stream()
|
||||||
|
.map(a -> { return a.split("@", 2)[0];})
|
||||||
|
.filter(a -> !a.isBlank())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getJlink() {
|
||||||
|
return getBinary("jlink");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getJava() {
|
||||||
|
return getBinary("java");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBinary(String binary) {
|
||||||
|
return isWindows() ? binary + ".exe" : binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isWindows() {
|
||||||
|
return System.getProperty("os.name").startsWith("Windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ExcludeAllJmodsFileVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
private final Path root;
|
||||||
|
private final Path destination;
|
||||||
|
|
||||||
|
private ExcludeAllJmodsFileVisitor(Path root,
|
||||||
|
Path destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult preVisitDirectory(Path dir,
|
||||||
|
BasicFileAttributes attrs) throws IOException {
|
||||||
|
Objects.requireNonNull(dir);
|
||||||
|
Path relative = root.relativize(dir);
|
||||||
|
if (relative.getFileName().equals(Path.of("jmods"))) {
|
||||||
|
return FileVisitResult.SKIP_SUBTREE;
|
||||||
|
}
|
||||||
|
// Create dir in destination location
|
||||||
|
Path targetDir = destination.resolve(relative);
|
||||||
|
if (!Files.exists(targetDir)) {
|
||||||
|
Files.createDirectory(targetDir);
|
||||||
|
}
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||||
|
throws IOException {
|
||||||
|
Path relative = root.relativize(file);
|
||||||
|
Files.copy(file, destination.resolve(relative), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FileExcludingFileVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
|
||||||
|
private final Set<String> filesToExclude;
|
||||||
|
private final Path root;
|
||||||
|
private final Path destination;
|
||||||
|
|
||||||
|
private FileExcludingFileVisitor(Set<String> filesToExclude,
|
||||||
|
Path root,
|
||||||
|
Path destination) {
|
||||||
|
this.filesToExclude = filesToExclude;
|
||||||
|
this.destination = destination;
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult preVisitDirectory(Path dir,
|
||||||
|
BasicFileAttributes attrs) throws IOException {
|
||||||
|
Objects.requireNonNull(dir);
|
||||||
|
Path relative = root.relativize(dir);
|
||||||
|
// Create dir in destination location
|
||||||
|
Path targetDir = destination.resolve(relative);
|
||||||
|
if (!Files.exists(targetDir)) {
|
||||||
|
Files.createDirectory(targetDir);
|
||||||
|
}
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||||
|
throws IOException {
|
||||||
|
Path relative = root.relativize(file);
|
||||||
|
// Skip files as determined by the exclude set
|
||||||
|
String fileName = file.getFileName().toString();
|
||||||
|
if (!filesToExclude.contains(fileName)) {
|
||||||
|
Files.copy(file, destination.resolve(relative), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BaseJlinkSpec {
|
||||||
|
final Helper helper;
|
||||||
|
final String name;
|
||||||
|
final String validatingModule;
|
||||||
|
final List<String> modules;
|
||||||
|
final List<String> extraOptions;
|
||||||
|
final boolean isLinkableRuntime;
|
||||||
|
|
||||||
|
BaseJlinkSpec(Helper helper, String name, String validatingModule,
|
||||||
|
List<String> modules, List<String> extraOptions, boolean isLinkableRuntime) {
|
||||||
|
this.helper = helper;
|
||||||
|
this.name = name;
|
||||||
|
this.modules = modules;
|
||||||
|
this.extraOptions = extraOptions;
|
||||||
|
this.validatingModule = validatingModule;
|
||||||
|
this.isLinkableRuntime = isLinkableRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidatingModule() {
|
||||||
|
return validatingModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Helper getHelper() {
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getModules() {
|
||||||
|
return modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExtraOptions() {
|
||||||
|
return extraOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLinkableRuntime() {
|
||||||
|
return isLinkableRuntime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BaseJlinkSpecBuilder {
|
||||||
|
Helper helper;
|
||||||
|
String name;
|
||||||
|
String validatingModule;
|
||||||
|
List<String> modules = new ArrayList<>();
|
||||||
|
List<String> extraOptions = new ArrayList<>();
|
||||||
|
boolean isLinkableRuntime;
|
||||||
|
|
||||||
|
BaseJlinkSpecBuilder addModule(String module) {
|
||||||
|
modules.add(module);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseJlinkSpecBuilder addExtraOption(String option) {
|
||||||
|
extraOptions.add(option);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseJlinkSpecBuilder setLinkableRuntime() {
|
||||||
|
isLinkableRuntime = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseJlinkSpecBuilder helper(Helper helper) {
|
||||||
|
this.helper = helper;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseJlinkSpecBuilder name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseJlinkSpecBuilder validatingModule(String module) {
|
||||||
|
this.validatingModule = module;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseJlinkSpec build() {
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalStateException("Name must be set");
|
||||||
|
}
|
||||||
|
if (helper == null) {
|
||||||
|
throw new IllegalStateException("helper must be set");
|
||||||
|
}
|
||||||
|
if (modules.isEmpty()) {
|
||||||
|
throw new IllegalStateException("modules must be set");
|
||||||
|
}
|
||||||
|
if (validatingModule == null) {
|
||||||
|
throw new IllegalStateException("the module which should get validated must be set");
|
||||||
|
}
|
||||||
|
return new BaseJlinkSpec(helper, name, validatingModule, modules, extraOptions, isLinkableRuntime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class JlinkSpec {
|
||||||
|
final Path imageToUse;
|
||||||
|
final Helper helper;
|
||||||
|
final String name;
|
||||||
|
final List<String> modules;
|
||||||
|
final String validatingModule;
|
||||||
|
final List<String> expectedLocations;
|
||||||
|
final List<String> unexpectedLocations;
|
||||||
|
final String[] expectedFiles;
|
||||||
|
final List<String> extraJlinkOpts;
|
||||||
|
final List<String> modulePath;
|
||||||
|
|
||||||
|
JlinkSpec(Path imageToUse, Helper helper, String name, List<String> modules,
|
||||||
|
String validatingModule, List<String> expectedLocations,
|
||||||
|
List<String> unexpectedLocations, String[] expectedFiles,
|
||||||
|
List<String> extraJlinkOpts,
|
||||||
|
List<String> modulePath) {
|
||||||
|
this.imageToUse = imageToUse;
|
||||||
|
this.helper = helper;
|
||||||
|
this.name = name;
|
||||||
|
this.modules = modules;
|
||||||
|
this.validatingModule = validatingModule;
|
||||||
|
this.expectedLocations = expectedLocations;
|
||||||
|
this.unexpectedLocations = unexpectedLocations;
|
||||||
|
this.expectedFiles = expectedFiles;
|
||||||
|
this.extraJlinkOpts = extraJlinkOpts;
|
||||||
|
this.modulePath = modulePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getImageToUse() {
|
||||||
|
return imageToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Helper getHelper() {
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getModules() {
|
||||||
|
return modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidatingModule() {
|
||||||
|
return validatingModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExpectedLocations() {
|
||||||
|
return expectedLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnexpectedLocations() {
|
||||||
|
return unexpectedLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getExpectedFiles() {
|
||||||
|
return expectedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExtraJlinkOpts() {
|
||||||
|
return extraJlinkOpts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getModulePath() {
|
||||||
|
return modulePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class JlinkSpecBuilder {
|
||||||
|
Path imageToUse;
|
||||||
|
Helper helper;
|
||||||
|
String name;
|
||||||
|
List<String> modules = new ArrayList<>();
|
||||||
|
String validatingModule;
|
||||||
|
List<String> expectedLocations = new ArrayList<>();
|
||||||
|
List<String> unexpectedLocations = new ArrayList<>();
|
||||||
|
List<String> expectedFiles = new ArrayList<>();
|
||||||
|
List<String> extraJlinkOpts = new ArrayList<>();
|
||||||
|
List<String> modulePath = new ArrayList<>();
|
||||||
|
|
||||||
|
JlinkSpec build() {
|
||||||
|
if (imageToUse == null) {
|
||||||
|
throw new IllegalStateException("No image to use for jlink specified!");
|
||||||
|
}
|
||||||
|
if (helper == null) {
|
||||||
|
throw new IllegalStateException("No helper specified!");
|
||||||
|
}
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalStateException("No name for the image location specified!");
|
||||||
|
}
|
||||||
|
if (validatingModule == null) {
|
||||||
|
throw new IllegalStateException("No module specified for after generation validation!");
|
||||||
|
}
|
||||||
|
return new JlinkSpec(imageToUse,
|
||||||
|
helper,
|
||||||
|
name,
|
||||||
|
modules,
|
||||||
|
validatingModule,
|
||||||
|
expectedLocations,
|
||||||
|
unexpectedLocations,
|
||||||
|
expectedFiles.toArray(new String[0]),
|
||||||
|
extraJlinkOpts,
|
||||||
|
modulePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder imagePath(Path image) {
|
||||||
|
this.imageToUse = image;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder helper(Helper helper) {
|
||||||
|
this.helper = helper;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder addModule(String module) {
|
||||||
|
modules.add(module);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder validatingModule(String module) {
|
||||||
|
this.validatingModule = module;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder addModulePath(String modulePath) {
|
||||||
|
this.modulePath.add(modulePath);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder expectedLocation(String location) {
|
||||||
|
expectedLocations.add(location);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder unexpectedLocation(String location) {
|
||||||
|
unexpectedLocations.add(location);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder expectedFile(String file) {
|
||||||
|
expectedFiles.add(file);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JlinkSpecBuilder extraJlinkOpt(String opt) {
|
||||||
|
extraJlinkOpts.add(opt);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static abstract class OutputAnalyzerHandler {
|
||||||
|
|
||||||
|
public abstract void handleAnalyzer(OutputAnalyzer out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RuntimeLinkOutputAnalyzerHandler extends OutputAnalyzerHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleAnalyzer(OutputAnalyzer out) {
|
||||||
|
out.shouldContain("Linking based on the current run-time image");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultSuccessExitPredicate implements Predicate<OutputAnalyzer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(OutputAnalyzer t) {
|
||||||
|
return t.getExitValue() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
86
test/jdk/tools/jlink/runtimeImage/AddOptionsTest.java
Normal file
86
test/jdk/tools/jlink/runtimeImage/AddOptionsTest.java
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test --add-options jlink plugin when linking from the run-time image
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g AddOptionsTest
|
||||||
|
*/
|
||||||
|
public class AddOptionsTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
AddOptionsTest test = new AddOptionsTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.addExtraOption("--add-options")
|
||||||
|
.addExtraOption("-Xlog:gc=info:stderr -XX:+UseParallelGC")
|
||||||
|
.name("java-base-with-opts")
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.helper(helper);
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path finalImage = createJavaImageRuntimeLink(builder.build());
|
||||||
|
verifyListModules(finalImage, List.of("java.base"));
|
||||||
|
verifyParallelGCInUse(finalImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyParallelGCInUse(Path finalImage) throws Exception {
|
||||||
|
OutputAnalyzer analyzer = runJavaCmd(finalImage, List.of("--version"));
|
||||||
|
boolean foundMatch = false;
|
||||||
|
try (Scanner lineScan = new Scanner(analyzer.getStderr())) {
|
||||||
|
while (lineScan.hasNextLine()) {
|
||||||
|
String line = lineScan.nextLine();
|
||||||
|
if (line.endsWith("Using Parallel")) {
|
||||||
|
foundMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundMatch) {
|
||||||
|
throw new AssertionError("Expected Parallel GC in place for jlinked image");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test basic linking from the run-time image with java.base.jmod missing
|
||||||
|
* but java.xml.jmod present. It should link from the run-time image without errors.
|
||||||
|
* @requires (jlink.packagedModules & vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g BasicJlinkMissingJavaBase
|
||||||
|
*/
|
||||||
|
public class BasicJlinkMissingJavaBase extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
Path finalImage = createJavaXMLRuntimeLink(helper, "java-xml", isLinkableRuntime);
|
||||||
|
verifyListModules(finalImage, List.of("java.base", "java.xml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path createJavaXMLRuntimeLink(Helper helper, String name, boolean isLinkableRuntime) throws Exception {
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder();
|
||||||
|
builder.helper(helper)
|
||||||
|
.name(name)
|
||||||
|
.addModule("java.xml")
|
||||||
|
.validatingModule("java.xml");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Set<String> excludedJmods = Set.of("java.base.jmod");
|
||||||
|
return createJavaImageRuntimeLink(builder.build(), excludedJmods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
BasicJlinkMissingJavaBase test = new BasicJlinkMissingJavaBase();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
test/jdk/tools/jlink/runtimeImage/BasicJlinkTest.java
Normal file
69
test/jdk/tools/jlink/runtimeImage/BasicJlinkTest.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test basic linking from the run-time image
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g BasicJlinkTest false
|
||||||
|
*/
|
||||||
|
public class BasicJlinkTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
Path finalImage = createJavaBaseRuntimeLink(helper, "java-base", isLinkableRuntime);
|
||||||
|
verifyListModules(finalImage, List.of("java.base"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path createJavaBaseRuntimeLink(Helper helper, String name, boolean isLinkableRuntime) throws Exception {
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder();
|
||||||
|
builder.helper(helper)
|
||||||
|
.name(name)
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
return createJavaImageRuntimeLink(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
BasicJlinkTest test = new BasicJlinkTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
test/jdk/tools/jlink/runtimeImage/CapturingHandler.java
Normal file
42
test/jdk/tools/jlink/runtimeImage/CapturingHandler.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
class CapturingHandler extends AbstractLinkableRuntimeTest.OutputAnalyzerHandler {
|
||||||
|
|
||||||
|
private OutputAnalyzer output;
|
||||||
|
|
||||||
|
public String stdErr() {
|
||||||
|
return output.getStderr();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputAnalyzer analyzer() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleAnalyzer(OutputAnalyzer out) {
|
||||||
|
this.output = out;
|
||||||
|
}
|
||||||
|
}
|
84
test/jdk/tools/jlink/runtimeImage/CustomModuleJlinkTest.java
Normal file
84
test/jdk/tools/jlink/runtimeImage/CustomModuleJlinkTest.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test jmod-less jlink with a custom module
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g CustomModuleJlinkTest
|
||||||
|
*/
|
||||||
|
public class CustomModuleJlinkTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
CustomModuleJlinkTest test = new CustomModuleJlinkTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
String customModule = "leaf1";
|
||||||
|
helper.generateDefaultJModule(customModule);
|
||||||
|
|
||||||
|
// create a base image for linking from the run-time image
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.name("cmod-jlink")
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path jlinkImage = createRuntimeLinkImage(builder.build());
|
||||||
|
|
||||||
|
// Next jlink using the run-time image for java.base, but take
|
||||||
|
// the custom module from the module path.
|
||||||
|
Path finalImage = jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.imagePath(jlinkImage)
|
||||||
|
.helper(helper)
|
||||||
|
.name(customModule)
|
||||||
|
.addModulePath(helper.defaultModulePath(false))
|
||||||
|
.expectedLocation(String.format("/%s/%s/com/foo/bar/X.class", customModule, customModule))
|
||||||
|
.addModule(customModule)
|
||||||
|
.validatingModule(customModule)
|
||||||
|
.build());
|
||||||
|
// Expected only the transitive closure of "leaf1" module in the --list-modules
|
||||||
|
// output of the java launcher.
|
||||||
|
List<String> expectedModules = List.of("java.base", customModule);
|
||||||
|
verifyListModules(finalImage, expectedModules);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Verify JLI class generation in run-time image link mode
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g GenerateJLIClassesTest
|
||||||
|
*/
|
||||||
|
public class GenerateJLIClassesTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
GenerateJLIClassesTest test = new GenerateJLIClassesTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* java.lang.invoke.BoundMethodHandle$Species_* classes get generated
|
||||||
|
* by the GenerateJLiClassesPlugin. This test ensures that potentially
|
||||||
|
* generated JLI classes from the run-time image don't populate to the
|
||||||
|
* target image in the run-time image based link mode.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
Path baseFile = Files.createTempFile("base", "trace");
|
||||||
|
String species = "LLLLLLLLLLLLLLLLLLL";
|
||||||
|
String fileString = "[SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_" + species + " (salvaged)\n";
|
||||||
|
Files.write(baseFile, fileString.getBytes(StandardCharsets.UTF_8));
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.addModule("java.base")
|
||||||
|
.name("jlink.jli-jmodless")
|
||||||
|
.validatingModule("java.base");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
|
||||||
|
Path runtimeLinkableImage = createRuntimeLinkImage(builder.build());
|
||||||
|
// Finally attempt another jmodless link reducing modules to java.base only,
|
||||||
|
// and asking for specific jli classes.
|
||||||
|
jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.imagePath(runtimeLinkableImage)
|
||||||
|
.name("java.base-jli-derived")
|
||||||
|
.addModule("java.base")
|
||||||
|
.extraJlinkOpt("--generate-jli-classes=@" + baseFile.toString())
|
||||||
|
.expectedLocation("/java.base/java/lang/invoke/BoundMethodHandle$Species_" + species + ".class")
|
||||||
|
.expectedLocation("/java.base/java/lang/invoke/BoundMethodHandle$Species_L.class")
|
||||||
|
.unexpectedLocation("/java.base/java/lang/invoke/BoundMethodHandle$Species_" + species.substring(1) + ".class")
|
||||||
|
.unexpectedLocation("/java.base/java/lang/invoke/BoundMethodHandle$Species_LL.class")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
65
test/jdk/tools/jlink/runtimeImage/JImageHelper.java
Normal file
65
test/jdk/tools/jlink/runtimeImage/JImageHelper.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.internal.jimage.BasicImageReader;
|
||||||
|
import jdk.internal.jimage.ImageLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* JDK Modular image iterator
|
||||||
|
*/
|
||||||
|
public class JImageHelper {
|
||||||
|
|
||||||
|
private JImageHelper() {
|
||||||
|
// Don't instantiate
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> listContents(Path jimage) throws IOException {
|
||||||
|
try(BasicImageReader reader = BasicImageReader.open(jimage)) {
|
||||||
|
List<String> entries = new ArrayList<>();
|
||||||
|
for (String s : reader.getEntryNames()) {
|
||||||
|
entries.add(s);
|
||||||
|
}
|
||||||
|
Collections.sort(entries);
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getLocationBytes(String location, Path jimage) throws IOException {
|
||||||
|
try(BasicImageReader reader = BasicImageReader.open(jimage)) {
|
||||||
|
ImageLocation il = reader.findLocation(location);
|
||||||
|
byte[] r = reader.getResource(il);
|
||||||
|
if (r == null) {
|
||||||
|
throw new IllegalStateException(String.format("bytes for %s not found!", location));
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test reproducibility of linking an java.se image using the run-time
|
||||||
|
* image.
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g JavaSEReproducibleTest
|
||||||
|
*/
|
||||||
|
public class JavaSEReproducibleTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
JavaSEReproducibleTest test = new JavaSEReproducibleTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
String javaSeModule = "java.se";
|
||||||
|
// create a java.se using jmod-less approach
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.addModule(javaSeModule)
|
||||||
|
.validatingModule(javaSeModule);
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
builder.name("java-se-repro1");
|
||||||
|
Path javaSEJmodLess1 = createJavaImageRuntimeLink(builder.build());
|
||||||
|
|
||||||
|
// create another java.se version using jmod-less approach
|
||||||
|
builder.name("java-se-repro2");
|
||||||
|
Path javaSEJmodLess2 = createJavaImageRuntimeLink(builder.build());
|
||||||
|
if (Files.mismatch(javaSEJmodLess1.resolve("lib").resolve("modules"),
|
||||||
|
javaSEJmodLess2.resolve("lib").resolve("modules")) != -1L) {
|
||||||
|
throw new RuntimeException("jlink producing inconsistent result for " + javaSeModule + " (jmod-less)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Verify that jlink with an empty module path, but trying to use
|
||||||
|
* --keep-packaged-modules fails as expected.
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g KeepPackagedModulesFailTest
|
||||||
|
*/
|
||||||
|
public class KeepPackagedModulesFailTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
KeepPackagedModulesFailTest test = new KeepPackagedModulesFailTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
// create a base image for linking from the run-time image
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.name("jlink-fail")
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path baseImage = createRuntimeLinkImage(builder.build());
|
||||||
|
|
||||||
|
CapturingHandler handler = new CapturingHandler();
|
||||||
|
Predicate<OutputAnalyzer> exitFailPred = new Predicate<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(OutputAnalyzer t) {
|
||||||
|
return t.getExitValue() != 0; // expect failure
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Attempt a jlink using the run-time image and also using option
|
||||||
|
// --keep-packaged-modules, which should fail.
|
||||||
|
jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.imagePath(baseImage)
|
||||||
|
.name("java-base-jlink-keep-packaged-target")
|
||||||
|
.addModule("java.base")
|
||||||
|
.extraJlinkOpt("--keep-packaged-modules=foo")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.build(), handler, exitFailPred);
|
||||||
|
OutputAnalyzer analyzer = handler.analyzer();
|
||||||
|
if (analyzer.getExitValue() == 0) {
|
||||||
|
throw new AssertionError("Expected jlink to have failed!");
|
||||||
|
}
|
||||||
|
analyzer.stdoutShouldContain("Error");
|
||||||
|
analyzer.stdoutShouldContain("--keep-packaged-modules");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
85
test/jdk/tools/jlink/runtimeImage/ModifiedFilesExitTest.java
Normal file
85
test/jdk/tools/jlink/runtimeImage/ModifiedFilesExitTest.java
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Verify jlink fails by default when linking from the run-time image
|
||||||
|
* and files have been modified
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g ModifiedFilesExitTest
|
||||||
|
*/
|
||||||
|
public class ModifiedFilesExitTest extends ModifiedFilesTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ModifiedFilesExitTest test = new ModifiedFilesExitTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String initialImageName() {
|
||||||
|
return "java-base-jlink-with-mod-exit";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void testAndAssert(Path modifiedFile, Helper helper, Path initialImage)
|
||||||
|
throws Exception {
|
||||||
|
CapturingHandler handler = new CapturingHandler();
|
||||||
|
Predicate<OutputAnalyzer> exitFailPred = new Predicate<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(OutputAnalyzer t) {
|
||||||
|
return t.getExitValue() != 0; // expect failure
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.imagePath(initialImage)
|
||||||
|
.name("java-base-jlink-with-mod-exit-target")
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.build(), handler, exitFailPred);
|
||||||
|
OutputAnalyzer analyzer = handler.analyzer();
|
||||||
|
if (analyzer.getExitValue() == 0) {
|
||||||
|
throw new AssertionError("Expected jlink to fail due to modified file!");
|
||||||
|
}
|
||||||
|
analyzer.stdoutShouldContain(modifiedFile.toString() + " has been modified");
|
||||||
|
// Verify the error message is reasonable
|
||||||
|
analyzer.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException");
|
||||||
|
analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
72
test/jdk/tools/jlink/runtimeImage/ModifiedFilesTest.java
Normal file
72
test/jdk/tools/jlink/runtimeImage/ModifiedFilesTest.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
public abstract class ModifiedFilesTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
abstract String initialImageName();
|
||||||
|
abstract void testAndAssert(Path modifiedFile, Helper helper, Path initialImage) throws Exception;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.name(initialImageName())
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.helper(helper);
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path initialImage = createRuntimeLinkImage(builder.build());
|
||||||
|
|
||||||
|
Path netPropertiesFile = modifyFileInImage(initialImage);
|
||||||
|
|
||||||
|
testAndAssert(netPropertiesFile, helper, initialImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path modifyFileInImage(Path jmodLessImg)
|
||||||
|
throws IOException, AssertionError {
|
||||||
|
// modify net.properties config file
|
||||||
|
Path netPropertiesFile = jmodLessImg.resolve("conf").resolve("net.properties");
|
||||||
|
Properties props = new Properties();
|
||||||
|
try (InputStream is = Files.newInputStream(netPropertiesFile)) {
|
||||||
|
props.load(is);
|
||||||
|
}
|
||||||
|
String prevVal = (String)props.put("java.net.useSystemProxies", Boolean.TRUE.toString());
|
||||||
|
if (prevVal == null || Boolean.getBoolean(prevVal) != false) {
|
||||||
|
throw new AssertionError("Expected previous value to be false!");
|
||||||
|
}
|
||||||
|
try (OutputStream out = Files.newOutputStream(netPropertiesFile)) {
|
||||||
|
props.store(out, "Modified net.properties file!");
|
||||||
|
}
|
||||||
|
return netPropertiesFile;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Verify warnings are being produced when linking from the run-time
|
||||||
|
* image and files have been modified
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g ModifiedFilesWarningTest
|
||||||
|
*/
|
||||||
|
public class ModifiedFilesWarningTest extends ModifiedFilesTest {
|
||||||
|
|
||||||
|
protected static final String IGNORE_MODIFIED_RUNTIME_OPT = "--ignore-modified-runtime";
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ModifiedFilesWarningTest test = new ModifiedFilesWarningTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String initialImageName() {
|
||||||
|
return "java-base-jlink-with-mod-warn";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void testAndAssert(Path modifiedFile, Helper helper, Path initialImage) throws Exception {
|
||||||
|
CapturingHandler handler = new CapturingHandler();
|
||||||
|
jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.imagePath(initialImage)
|
||||||
|
.name("java-base-jlink-with-mod-warn-target")
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.extraJlinkOpt(IGNORE_MODIFIED_RUNTIME_OPT) // only generate a warning
|
||||||
|
.build(), handler);
|
||||||
|
OutputAnalyzer out = handler.analyzer();
|
||||||
|
// verify we get the warning message
|
||||||
|
out.stdoutShouldMatch("Warning: .* has been modified");
|
||||||
|
out.stdoutShouldNotContain("java.lang.IllegalArgumentException");
|
||||||
|
out.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException");
|
||||||
|
}
|
||||||
|
}
|
93
test/jdk/tools/jlink/runtimeImage/MultiHopTest.java
Normal file
93
test/jdk/tools/jlink/runtimeImage/MultiHopTest.java
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Verify that a jlink using the run-time image cannot include jdk.jlink
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g MultiHopTest
|
||||||
|
*/
|
||||||
|
public class MultiHopTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
Path jdkJlinkJmodless = createJDKJlinkJmodLess(helper, "jdk.jlink-multi-hop1", isLinkableRuntime);
|
||||||
|
CapturingHandler handler = new CapturingHandler();
|
||||||
|
Predicate<OutputAnalyzer> exitFailPred = new Predicate<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(OutputAnalyzer a) {
|
||||||
|
return a.getExitValue() != 0; // expect failure
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.imagePath(jdkJlinkJmodless)
|
||||||
|
.name("jdk-jlink-multi-hop1-target")
|
||||||
|
.addModule("jdk.jlink")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.build(), handler, exitFailPred);
|
||||||
|
OutputAnalyzer analyzer = handler.analyzer();
|
||||||
|
if (analyzer.getExitValue() == 0) {
|
||||||
|
throw new AssertionError("Expected jlink to fail due to including jdk.jlink");
|
||||||
|
}
|
||||||
|
String expectedMsg = "This JDK does not contain packaged modules " +
|
||||||
|
"and cannot be used to create another image with " +
|
||||||
|
"the jdk.jlink module";
|
||||||
|
analyzer.stdoutShouldContain(expectedMsg);
|
||||||
|
analyzer.stdoutShouldNotContain("Exception"); // ensure error message is sane
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path createJDKJlinkJmodLess(Helper helper, String name, boolean isLinkableRuntime) throws Exception {
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder();
|
||||||
|
builder.helper(helper)
|
||||||
|
.name(name)
|
||||||
|
.addModule("jdk.jlink")
|
||||||
|
.validatingModule("java.base");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
return createRuntimeLinkImage(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
MultiHopTest test = new MultiHopTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
import tests.JImageGenerator;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Compare packaged-modules jlink with a run-time image based jlink to
|
||||||
|
* produce the same result
|
||||||
|
* @requires (jlink.packagedModules & vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g PackagedModulesVsRuntimeImageLinkTest
|
||||||
|
*/
|
||||||
|
public class PackagedModulesVsRuntimeImageLinkTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
PackagedModulesVsRuntimeImageLinkTest test = new PackagedModulesVsRuntimeImageLinkTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
// create a java.se using jmod-less approach
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.name("java-se-jmodless")
|
||||||
|
.addModule("java.se")
|
||||||
|
.validatingModule("java.se");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path javaSEruntimeLink = createJavaImageRuntimeLink(builder.build());
|
||||||
|
|
||||||
|
// create a java.se using packaged modules (jmod-full)
|
||||||
|
Path javaSEJmodFull = JImageGenerator.getJLinkTask()
|
||||||
|
.output(helper.createNewImageDir("java-se-jmodfull"))
|
||||||
|
.addMods("java.se").call().assertSuccess();
|
||||||
|
|
||||||
|
compareRecursively(javaSEruntimeLink, javaSEJmodFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit all files in the given directories checking that they're byte-by-byte identical
|
||||||
|
private static void compareRecursively(Path javaSEJmodLess,
|
||||||
|
Path javaSEJmodFull) throws IOException, AssertionError {
|
||||||
|
FilesCapturingVisitor jmodFullVisitor = new FilesCapturingVisitor(javaSEJmodFull);
|
||||||
|
FilesCapturingVisitor jmodLessVisitor = new FilesCapturingVisitor(javaSEJmodLess);
|
||||||
|
Files.walkFileTree(javaSEJmodFull, jmodFullVisitor);
|
||||||
|
Files.walkFileTree(javaSEJmodLess, jmodLessVisitor);
|
||||||
|
List<String> jmodFullFiles = jmodFullVisitor.filesVisited();
|
||||||
|
List<String> jmodLessFiles = jmodLessVisitor.filesVisited();
|
||||||
|
Collections.sort(jmodFullFiles);
|
||||||
|
Collections.sort(jmodLessFiles);
|
||||||
|
|
||||||
|
if (jmodFullFiles.size() != jmodLessFiles.size()) {
|
||||||
|
throw new AssertionError(String.format("Size of files different for jmod-less (%d) vs jmod-full (%d) java.se jlink", jmodLessFiles.size(), jmodFullFiles.size()));
|
||||||
|
}
|
||||||
|
String jimageFile = Path.of("lib").resolve("modules").toString();
|
||||||
|
// Compare all files except the modules image
|
||||||
|
for (int i = 0; i < jmodFullFiles.size(); i++) {
|
||||||
|
String jmodFullPath = jmodFullFiles.get(i);
|
||||||
|
String jmodLessPath = jmodLessFiles.get(i);
|
||||||
|
if (!jmodFullPath.equals(jmodLessPath)) {
|
||||||
|
throw new AssertionError(String.format("jmod-full path (%s) != jmod-less path (%s)", jmodFullPath, jmodLessPath));
|
||||||
|
}
|
||||||
|
if (jmodFullPath.equals(jimageFile)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Path a = javaSEJmodFull.resolve(Path.of(jmodFullPath));
|
||||||
|
Path b = javaSEJmodLess.resolve(Path.of(jmodLessPath));
|
||||||
|
if (Files.mismatch(a, b) != -1L) {
|
||||||
|
handleFileMismatch(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Compare jimage contents by iterating its entries and comparing their
|
||||||
|
// paths and content bytes
|
||||||
|
//
|
||||||
|
// Note: The files aren't byte-by-byte comparable (probably due to string hashing
|
||||||
|
// and offset differences in container bytes)
|
||||||
|
Path jimageJmodLess = javaSEJmodLess.resolve(Path.of("lib")).resolve(Path.of("modules"));
|
||||||
|
Path jimageJmodFull = javaSEJmodFull.resolve(Path.of("lib")).resolve(Path.of("modules"));
|
||||||
|
List<String> jimageContentJmodLess = JImageHelper.listContents(jimageJmodLess);
|
||||||
|
List<String> jimageContentJmodFull = JImageHelper.listContents(jimageJmodFull);
|
||||||
|
if (jimageContentJmodLess.size() != jimageContentJmodFull.size()) {
|
||||||
|
throw new AssertionError(String.format("Size of jimage content differs for jmod-less (%d) v. jmod-full (%d)", jimageContentJmodLess.size(), jimageContentJmodFull.size()));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < jimageContentJmodFull.size(); i++) {
|
||||||
|
if (!jimageContentJmodFull.get(i).equals(jimageContentJmodLess.get(i))) {
|
||||||
|
throw new AssertionError(String.format("Jimage content differs at index %d: jmod-full was: '%s' jmod-less was: '%s'",
|
||||||
|
i,
|
||||||
|
jimageContentJmodFull.get(i),
|
||||||
|
jimageContentJmodLess.get(i)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
String loc = jimageContentJmodFull.get(i);
|
||||||
|
if (isTreeInfoResource(loc)) {
|
||||||
|
// Skip container bytes as those are offsets to the content
|
||||||
|
// of the container which might be different between jlink runs.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
byte[] resBytesFull = JImageHelper.getLocationBytes(loc, jimageJmodFull);
|
||||||
|
byte[] resBytesLess = JImageHelper.getLocationBytes(loc, jimageJmodLess);
|
||||||
|
if (resBytesFull.length != resBytesLess.length || Arrays.mismatch(resBytesFull, resBytesLess) != -1) {
|
||||||
|
throw new AssertionError("Content bytes mismatch for " + loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTreeInfoResource(String path) {
|
||||||
|
return path.startsWith("/packages") || path.startsWith("/modules");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleFileMismatch(Path a, Path b) {
|
||||||
|
throw new AssertionError("Files mismatch: " + a + " vs. " + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FilesCapturingVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
private final Path basePath;
|
||||||
|
private final List<String> filePaths = new ArrayList<>();
|
||||||
|
public FilesCapturingVisitor(Path basePath) {
|
||||||
|
this.basePath = basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
|
||||||
|
Path relative = basePath.relativize(path);
|
||||||
|
filePaths.add(relative.toString());
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> filesVisited() {
|
||||||
|
return filePaths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
124
test/jdk/tools/jlink/runtimeImage/PatchedJDKModuleJlinkTest.java
Normal file
124
test/jdk/tools/jlink/runtimeImage/PatchedJDKModuleJlinkTest.java
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.spi.ToolProvider;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import tests.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test run-time link with --patch-module. Expect failure.
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g PatchedJDKModuleJlinkTest
|
||||||
|
*/
|
||||||
|
public class PatchedJDKModuleJlinkTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
String imageName = "java-base-patched";
|
||||||
|
Path runtimeLinkImage = createRuntimeLinkImage(helper, imageName + "-base", isLinkableRuntime);
|
||||||
|
|
||||||
|
// Prepare patched module content
|
||||||
|
Path patchSource = Path.of("java-base-patch-src");
|
||||||
|
Path pkg = patchSource.resolve("java", "lang");
|
||||||
|
Path extraClass = pkg.resolve("MyJlinkPatchInteger.java");
|
||||||
|
String source = """
|
||||||
|
package java.lang;
|
||||||
|
public class MyJlinkPatchInteger {
|
||||||
|
public int add(int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
Files.createDirectories(pkg);
|
||||||
|
Files.writeString(extraClass, source);
|
||||||
|
Path patchClasses = Path.of("java-base-patch-classes");
|
||||||
|
Files.createDirectories(patchClasses);
|
||||||
|
ToolProvider javac = ToolProvider.findFirst("javac")
|
||||||
|
.orElseThrow(() -> new AssertionError("javac not found"));
|
||||||
|
javac.run(System.out, System.err, new String[] {
|
||||||
|
"-d", patchClasses.toString(),
|
||||||
|
"--patch-module=java.base=" + patchSource.toAbsolutePath().toString(),
|
||||||
|
extraClass.toAbsolutePath().toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Perform a run-time image link expecting a failure
|
||||||
|
CapturingHandler handler = new CapturingHandler();
|
||||||
|
Predicate<OutputAnalyzer> exitFailPred = new Predicate<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(OutputAnalyzer t) {
|
||||||
|
return t.getExitValue() != 0; // expect failure
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jlinkUsingImage(new JlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.imagePath(runtimeLinkImage)
|
||||||
|
.name(imageName + "-derived")
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.extraJlinkOpt("-J--patch-module=java.base=" +
|
||||||
|
patchClasses.toAbsolutePath().toString())
|
||||||
|
.build(), handler, exitFailPred);
|
||||||
|
OutputAnalyzer analyzer = handler.analyzer();
|
||||||
|
if (analyzer.getExitValue() == 0) {
|
||||||
|
throw new AssertionError("Expected jlink to fail due to patched module!");
|
||||||
|
}
|
||||||
|
analyzer.stdoutShouldContain("MyJlinkPatchInteger.class not found in the modules image.");
|
||||||
|
analyzer.stdoutShouldContain("--patch-module is not supported");
|
||||||
|
// Verify the error message is reasonable
|
||||||
|
analyzer.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException");
|
||||||
|
analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path createRuntimeLinkImage(Helper helper, String name, boolean isLinkableRuntime) throws Exception {
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.name(name)
|
||||||
|
.addModule("java.base")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.helper(helper);
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
return createRuntimeLinkImage(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
PatchedJDKModuleJlinkTest test = new PatchedJDKModuleJlinkTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
test/jdk/tools/jlink/runtimeImage/SystemModulesTest.java
Normal file
81
test/jdk/tools/jlink/runtimeImage/SystemModulesTest.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
import tests.JImageValidator;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test appropriate handling of generated SystemModules* classes in run-time image link mode
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g SystemModulesTest
|
||||||
|
*/
|
||||||
|
public class SystemModulesTest extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SystemModulesTest test = new SystemModulesTest();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SystemModule classes are module specific. If the jlink is based on the
|
||||||
|
* modules image, then earlier generated SystemModule classes shall not get
|
||||||
|
* propagated.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
// create an image with a module containing a main entrypoint (jdk.httpserver),
|
||||||
|
// thus producing the SystemModules$0.class. Add jdk.jdwp.agent as a module which
|
||||||
|
// isn't resolved by default, so as to generate SystemModules$default.class
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.name("httpserver-jlink-jmodless-derived")
|
||||||
|
.addModule("jdk.httpserver")
|
||||||
|
.addModule("jdk.jdwp.agent")
|
||||||
|
.validatingModule("java.base");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path javaseJmodless = createJavaImageRuntimeLink(builder.build());
|
||||||
|
// Verify that SystemModules$0.class etc. are there, due to httpserver and jdwp.agent
|
||||||
|
JImageValidator.validate(javaseJmodless.resolve("lib").resolve("modules"),
|
||||||
|
List.of("/java.base/jdk/internal/module/SystemModules$default.class",
|
||||||
|
"/java.base/jdk/internal/module/SystemModules$0.class",
|
||||||
|
"/java.base/jdk/internal/module/SystemModules$all.class"),
|
||||||
|
Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
76
test/jdk/tools/jlink/runtimeImage/SystemModulesTest2.java
Normal file
76
test/jdk/tools/jlink/runtimeImage/SystemModulesTest2.java
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Red Hat, Inc.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import tests.Helper;
|
||||||
|
import tests.JImageValidator;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test disabled SystemModulesPlugin in run-time image link mode. Expect
|
||||||
|
* generated classes to not be there.
|
||||||
|
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||||
|
* @library ../../lib /test/lib
|
||||||
|
* @enablePreview
|
||||||
|
* @modules java.base/jdk.internal.jimage
|
||||||
|
* jdk.jlink/jdk.tools.jlink.internal
|
||||||
|
* jdk.jlink/jdk.tools.jlink.plugin
|
||||||
|
* jdk.jlink/jdk.tools.jimage
|
||||||
|
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||||
|
* jdk.test.lib.process.ProcessTools
|
||||||
|
* @run main/othervm -Xmx1g SystemModulesTest2
|
||||||
|
*/
|
||||||
|
public class SystemModulesTest2 extends AbstractLinkableRuntimeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SystemModulesTest2 test = new SystemModulesTest2();
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void runTest(Helper helper, boolean isLinkableRuntime) throws Exception {
|
||||||
|
// See SystemModulesTest which enables the system-modules plugin. With
|
||||||
|
// it disabled, we expect for the generated classes to not be there.
|
||||||
|
BaseJlinkSpecBuilder builder = new BaseJlinkSpecBuilder()
|
||||||
|
.helper(helper)
|
||||||
|
.name("jlink-jmodless-sysmod2")
|
||||||
|
.addModule("jdk.httpserver")
|
||||||
|
.validatingModule("java.base")
|
||||||
|
.addExtraOption("--disable-plugin")
|
||||||
|
.addExtraOption("system-modules");
|
||||||
|
if (isLinkableRuntime) {
|
||||||
|
builder.setLinkableRuntime();
|
||||||
|
}
|
||||||
|
Path runtimeImageLinkTarget = createJavaImageRuntimeLink(builder.build());
|
||||||
|
JImageValidator.validate(runtimeImageLinkTarget.resolve("lib").resolve("modules"),
|
||||||
|
Collections.emptyList(),
|
||||||
|
List.of("/java.base/jdk/internal/module/SystemModules$all.class",
|
||||||
|
"/java.base/jdk/internal/module/SystemModules$default.class",
|
||||||
|
"/java.base/jdk/internal/module/SystemModules$0.class"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -64,20 +64,30 @@ public class Helper {
|
|||||||
private final Map<String, List<String>> moduleDependencies = new HashMap<>();
|
private final Map<String, List<String>> moduleDependencies = new HashMap<>();
|
||||||
private final List<String> bootClasses;
|
private final List<String> bootClasses;
|
||||||
private final FileSystem fs;
|
private final FileSystem fs;
|
||||||
|
private final boolean linkableRuntime;
|
||||||
|
private static final Path JDK_HOME = Paths.get(System.getProperty("test.jdk"));
|
||||||
|
|
||||||
public static Helper newHelper() throws IOException {
|
public static Helper newHelper() throws IOException {
|
||||||
Path jdkHome = Paths.get(System.getProperty("test.jdk"));
|
return newHelper(false);
|
||||||
if (!Files.exists(jdkHome.resolve("jmods"))) {
|
}
|
||||||
|
|
||||||
|
public static Helper newHelper(boolean linkableRuntime) throws IOException {
|
||||||
|
if (!linkableRuntime && !jdkHasPackagedModules()) {
|
||||||
// Skip test if the jmods directory is missing (e.g. exploded image)
|
// Skip test if the jmods directory is missing (e.g. exploded image)
|
||||||
System.err.println("Test not run, NO jmods directory");
|
System.err.println("Test not run, NO jmods directory");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new Helper(jdkHome);
|
return new Helper(JDK_HOME, linkableRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Helper(Path jdkHome) throws IOException {
|
public static boolean jdkHasPackagedModules() {
|
||||||
|
return Files.exists(JDK_HOME.resolve("jmods"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Helper(Path jdkHome, boolean linkableRuntime) throws IOException {
|
||||||
|
this.linkableRuntime = linkableRuntime;
|
||||||
this.stdjmods = jdkHome.resolve("jmods").normalize();
|
this.stdjmods = jdkHome.resolve("jmods").normalize();
|
||||||
if (!Files.exists(stdjmods)) {
|
if (!linkableRuntime && !Files.exists(stdjmods)) {
|
||||||
throw new IOException("Standard jMods do not exist.");
|
throw new IOException("Standard jMods do not exist.");
|
||||||
}
|
}
|
||||||
this.fs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
this.fs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||||
@ -140,7 +150,8 @@ public class Helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String defaultModulePath(boolean includeStdMods) {
|
public String defaultModulePath(boolean includeStdMods) {
|
||||||
return (includeStdMods? stdjmods.toAbsolutePath().toString() : "") + File.pathSeparator
|
String standardMods = linkableRuntime ? "" : stdjmods.toAbsolutePath().toString() + File.pathSeparator;
|
||||||
|
return (includeStdMods? standardMods : "")
|
||||||
+ jmods.toAbsolutePath().toString() + File.pathSeparator
|
+ jmods.toAbsolutePath().toString() + File.pathSeparator
|
||||||
+ jars.toAbsolutePath().toString() + File.pathSeparator
|
+ jars.toAbsolutePath().toString() + File.pathSeparator
|
||||||
+ explodedmodsclasses.toAbsolutePath().toString();
|
+ explodedmodsclasses.toAbsolutePath().toString();
|
||||||
@ -184,7 +195,7 @@ public class Helper {
|
|||||||
generateGarbage(jmodsclasses.resolve(moduleName));
|
generateGarbage(jmodsclasses.resolve(moduleName));
|
||||||
|
|
||||||
Path jmodFile = jmods.resolve(moduleName + ".jmod");
|
Path jmodFile = jmods.resolve(moduleName + ".jmod");
|
||||||
JModTask task = JImageGenerator.getJModTask()
|
JModTask task = JImageGenerator.getJModTask(linkableRuntime)
|
||||||
.jmod(jmodFile)
|
.jmod(jmodFile)
|
||||||
.addJmods(stdjmods)
|
.addJmods(stdjmods)
|
||||||
.addJmods(jmods.toAbsolutePath())
|
.addJmods(jmods.toAbsolutePath())
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -159,7 +159,11 @@ public class JImageGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static JModTask getJModTask() {
|
public static JModTask getJModTask() {
|
||||||
return new JModTask();
|
return getJModTask(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JModTask getJModTask(boolean linkableRuntime) {
|
||||||
|
return new JModTask(linkableRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JLinkTask getJLinkTask() {
|
public static JLinkTask getJLinkTask() {
|
||||||
@ -350,11 +354,16 @@ public class JImageGenerator {
|
|||||||
private final List<Path> jars = new ArrayList<>();
|
private final List<Path> jars = new ArrayList<>();
|
||||||
private final List<Path> jmods = new ArrayList<>();
|
private final List<Path> jmods = new ArrayList<>();
|
||||||
private final List<String> options = new ArrayList<>();
|
private final List<String> options = new ArrayList<>();
|
||||||
|
private final boolean linkableRuntime;
|
||||||
private Path output;
|
private Path output;
|
||||||
private String hashModules;
|
private String hashModules;
|
||||||
private String mainClass;
|
private String mainClass;
|
||||||
private String moduleVersion;
|
private String moduleVersion;
|
||||||
|
|
||||||
|
private JModTask(boolean linkableRuntime) {
|
||||||
|
this.linkableRuntime = linkableRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
public JModTask addNativeLibraries(Path cp) {
|
public JModTask addNativeLibraries(Path cp) {
|
||||||
this.libs.add(cp);
|
this.libs.add(cp);
|
||||||
return this;
|
return this;
|
||||||
@ -414,7 +423,7 @@ public class JImageGenerator {
|
|||||||
// This is expect FIRST jmods THEN jars, if you change this, some tests could fail
|
// This is expect FIRST jmods THEN jars, if you change this, some tests could fail
|
||||||
String jmods = toPath(this.jmods);
|
String jmods = toPath(this.jmods);
|
||||||
String jars = toPath(this.jars);
|
String jars = toPath(this.jars);
|
||||||
return jmods + File.pathSeparator + jars;
|
return linkableRuntime ? jars : jmods + File.pathSeparator + jars;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toPath(List<Path> paths) {
|
private String toPath(List<Path> paths) {
|
||||||
|
@ -138,6 +138,7 @@ public class VMProps implements Callable<Map<String, String>> {
|
|||||||
map.put("jdk.containerized", this::jdkContainerized);
|
map.put("jdk.containerized", this::jdkContainerized);
|
||||||
map.put("vm.flagless", this::isFlagless);
|
map.put("vm.flagless", this::isFlagless);
|
||||||
map.put("jdk.foreign.linker", this::jdkForeignLinker);
|
map.put("jdk.foreign.linker", this::jdkForeignLinker);
|
||||||
|
map.put("jlink.packagedModules", this::packagedModules);
|
||||||
vmGC(map); // vm.gc.X = true/false
|
vmGC(map); // vm.gc.X = true/false
|
||||||
vmGCforCDS(map); // may set vm.gc
|
vmGCforCDS(map); // may set vm.gc
|
||||||
vmOptFinalFlags(map);
|
vmOptFinalFlags(map);
|
||||||
@ -715,6 +716,21 @@ public class VMProps implements Callable<Map<String, String>> {
|
|||||||
return "" + "true".equalsIgnoreCase(isEnabled);
|
return "" + "true".equalsIgnoreCase(isEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String packagedModules() {
|
||||||
|
// Some jlink tests require packaged modules being present (jmods).
|
||||||
|
// For a runtime linkable image build packaged modules aren't present
|
||||||
|
try {
|
||||||
|
Path jmodsDir = Path.of(System.getProperty("java.home"), "jmods");
|
||||||
|
if (jmodsDir.toFile().exists()) {
|
||||||
|
return Boolean.TRUE.toString();
|
||||||
|
} else {
|
||||||
|
return Boolean.FALSE.toString();
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return Boolean.FALSE.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if we are in <i>almost</i> out-of-box configuration, i.e. the flags
|
* Checks if we are in <i>almost</i> out-of-box configuration, i.e. the flags
|
||||||
* which JVM is started with don't affect its behavior "significantly".
|
* which JVM is started with don't affect its behavior "significantly".
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
* jdk.compiler/com.sun.tools.javac.main
|
* jdk.compiler/com.sun.tools.javac.main
|
||||||
* jdk.jlink
|
* jdk.jlink
|
||||||
* @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask
|
* @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask
|
||||||
* @run main AutostartPlugins
|
* @run main/othervm AutostartPlugins
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
* jdk.compiler/com.sun.tools.javac.main
|
* jdk.compiler/com.sun.tools.javac.main
|
||||||
* jdk.jlink
|
* jdk.jlink
|
||||||
* @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask
|
* @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask
|
||||||
* @run main InternalAPI
|
* @run main/othervm InternalAPI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
Loading…
Reference in New Issue
Block a user