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
|
||||
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, \
|
||||
WARN := Creating jdk image, \
|
||||
DEPS := $(JDK_JMODS) $(BASE_RELEASE_FILE) \
|
||||
|
@ -586,13 +586,42 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JMOD_OPTIONS],
|
||||
################################################################################
|
||||
#
|
||||
# jlink options.
|
||||
# We always keep packaged modules in JDK image.
|
||||
#
|
||||
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,
|
||||
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])
|
||||
AC_SUBST(JLINK_KEEP_PACKAGED_MODULES)
|
||||
])
|
||||
|
@ -706,6 +706,7 @@ NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) $(JAVADOC_MAIN_CLASS)
|
||||
|
||||
JMOD_COMPRESS := @JMOD_COMPRESS@
|
||||
JLINK_KEEP_PACKAGED_MODULES := @JLINK_KEEP_PACKAGED_MODULES@
|
||||
JLINK_PRODUCE_LINKABLE_RUNTIME := @JLINK_PRODUCE_LINKABLE_RUNTIME@
|
||||
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,29 +24,44 @@
|
||||
*/
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.tools.jlink.internal.Archive.Entry;
|
||||
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.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.ResourcePool;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolModule;
|
||||
|
||||
/**
|
||||
* An image (native endian.)
|
||||
@ -68,38 +83,61 @@ import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
* }</pre>
|
||||
*/
|
||||
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 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.generateRuntimeImage = generateRuntimeImage;
|
||||
this.helper = taskHelper;
|
||||
}
|
||||
|
||||
public static ExecutableImage create(Set<Archive> archives,
|
||||
ImagePluginStack plugins)
|
||||
throws IOException {
|
||||
return ImageFileCreator.create(archives, ByteOrder.nativeOrder(),
|
||||
plugins);
|
||||
}
|
||||
|
||||
public static ExecutableImage create(Set<Archive> archives,
|
||||
ByteOrder byteOrder)
|
||||
throws IOException {
|
||||
return ImageFileCreator.create(archives, byteOrder,
|
||||
new ImagePluginStack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an executable image based on a set of input archives and a given
|
||||
* plugin stack for a given byte order. It optionally generates a runtime
|
||||
* that can be used for linking from the run-time image if
|
||||
* {@code generateRuntimeImage} is set to {@code true}.
|
||||
*
|
||||
* @param archives The set of input archives
|
||||
* @param byteOrder The desired byte order of the result
|
||||
* @param plugins The plugin stack to apply to the input
|
||||
* @param generateRuntimeImage if a runtime suitable for linking from the
|
||||
* run-time image should get created.
|
||||
* @return The executable image.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ExecutableImage create(Set<Archive> archives,
|
||||
ByteOrder byteOrder,
|
||||
ImagePluginStack plugins)
|
||||
ImagePluginStack plugins,
|
||||
boolean generateRuntimeImage,
|
||||
TaskHelper taskHelper)
|
||||
throws IOException
|
||||
{
|
||||
ImageFileCreator image = new ImageFileCreator(plugins);
|
||||
ImageFileCreator image = new ImageFileCreator(plugins,
|
||||
generateRuntimeImage,
|
||||
taskHelper);
|
||||
try {
|
||||
image.readAllEntries(archives);
|
||||
// write to modular image
|
||||
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 {
|
||||
//Close all archives
|
||||
// Close all archives
|
||||
for (Archive a : archives) {
|
||||
a.close();
|
||||
}
|
||||
@ -125,7 +163,8 @@ public final class ImageFileCreator {
|
||||
|
||||
public static void recreateJimage(Path jimageFile,
|
||||
Set<Archive> archives,
|
||||
ImagePluginStack pluginSupport)
|
||||
ImagePluginStack pluginSupport,
|
||||
boolean generateRuntimeImage)
|
||||
throws IOException {
|
||||
try {
|
||||
Map<String, List<Entry>> entriesForModule
|
||||
@ -142,7 +181,7 @@ public final class ImageFileCreator {
|
||||
try (OutputStream fos = Files.newOutputStream(jimageFile);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
DataOutputStream out = new DataOutputStream(bos)) {
|
||||
generateJImage(pool, writer, pluginSupport, out);
|
||||
generateJImage(pool, writer, pluginSupport, out, generateRuntimeImage);
|
||||
}
|
||||
} finally {
|
||||
//Close all archives
|
||||
@ -158,9 +197,14 @@ public final class ImageFileCreator {
|
||||
BasicImageWriter writer = new BasicImageWriter(byteOrder);
|
||||
ResourcePoolManager allContent = createPoolManager(archives,
|
||||
entriesForModule, byteOrder, writer);
|
||||
ResourcePool result;
|
||||
ResourcePool result = null;
|
||||
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.
|
||||
@ -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,
|
||||
BasicImageWriter writer,
|
||||
ImagePluginStack pluginSupport,
|
||||
DataOutputStream out
|
||||
DataOutputStream out,
|
||||
boolean generateRuntimeImage
|
||||
) throws IOException {
|
||||
ResourcePool resultResources;
|
||||
try {
|
||||
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) {
|
||||
if (JlinkTask.DEBUG) {
|
||||
pe.printStackTrace();
|
||||
@ -198,7 +281,7 @@ public final class ImageFileCreator {
|
||||
|
||||
List<ResourcePoolEntry> content = 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
|
||||
resultResources.entries().forEach(res -> {
|
||||
if (res.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
|
||||
@ -248,11 +331,225 @@ public final class ImageFileCreator {
|
||||
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,
|
||||
Map<String, List<Entry>> entriesForModule,
|
||||
ByteOrder byteOrder,
|
||||
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
|
||||
public int addString(String str) {
|
||||
@ -264,14 +561,25 @@ public final class ImageFileCreator {
|
||||
return writer.getString(id);
|
||||
}
|
||||
});
|
||||
archives.stream()
|
||||
.map(Archive::moduleName)
|
||||
.sorted()
|
||||
.flatMap(mn ->
|
||||
entriesForModule.get(mn).stream()
|
||||
.map(e -> new ArchiveEntryResourcePoolEntry(mn,
|
||||
e.getResourcePoolEntryName(), e)))
|
||||
.forEach(resources::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ResourcePoolManager from existing resources so that more
|
||||
* resources can be appended.
|
||||
*
|
||||
* @param resultResources The existing resources to initially 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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* 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.ModuleFinder;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -148,6 +147,9 @@ public final class Jlink {
|
||||
private final Path output;
|
||||
private final Set<String> modules;
|
||||
private final ModuleFinder finder;
|
||||
private final boolean linkFromRuntimeImage;
|
||||
private final boolean ignoreModifiedRuntime;
|
||||
private final boolean generateRuntimeImage;
|
||||
|
||||
/**
|
||||
* jlink configuration,
|
||||
@ -158,10 +160,16 @@ public final class Jlink {
|
||||
*/
|
||||
public JlinkConfiguration(Path output,
|
||||
Set<String> modules,
|
||||
ModuleFinder finder) {
|
||||
ModuleFinder finder,
|
||||
boolean linkFromRuntimeImage,
|
||||
boolean ignoreModifiedRuntime,
|
||||
boolean generateRuntimeImage) {
|
||||
this.output = output;
|
||||
this.modules = Objects.requireNonNull(modules);
|
||||
this.finder = finder;
|
||||
this.linkFromRuntimeImage = linkFromRuntimeImage;
|
||||
this.ignoreModifiedRuntime = ignoreModifiedRuntime;
|
||||
this.generateRuntimeImage = generateRuntimeImage;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,6 +194,18 @@ public final class Jlink {
|
||||
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,
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,6 +24,8 @@
|
||||
*/
|
||||
package jdk.tools.jlink.internal;
|
||||
|
||||
import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -39,14 +41,15 @@ import java.lang.module.ResolutionException;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
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.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@ -60,18 +63,18 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.internal.module.ModuleReferenceImpl;
|
||||
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
|
||||
import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
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.PluginsConfiguration;
|
||||
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
|
||||
import jdk.tools.jlink.internal.TaskHelper.Option;
|
||||
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.internal.opt.CommandLine;
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
|
||||
/**
|
||||
* Implementation for the jlink tool.
|
||||
@ -86,7 +89,6 @@ public class JlinkTask {
|
||||
|
||||
private static final TaskHelper taskHelper
|
||||
= new TaskHelper(JLINK_BUNDLE);
|
||||
|
||||
private static final Option<?>[] recognizedOptions = {
|
||||
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||
task.options.help = true;
|
||||
@ -182,7 +184,17 @@ public class JlinkTask {
|
||||
}, true, "--full-version"),
|
||||
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||
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 final OptionsValues options = new OptionsValues();
|
||||
@ -222,6 +234,8 @@ public class JlinkTask {
|
||||
boolean ignoreSigning = false;
|
||||
boolean bindServices = false;
|
||||
boolean suggestProviders = false;
|
||||
boolean ignoreModifiedRuntime = false;
|
||||
boolean generateLinkableRuntime = false;
|
||||
}
|
||||
|
||||
public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options";
|
||||
@ -252,7 +266,7 @@ public class JlinkTask {
|
||||
.showUsage(true);
|
||||
}
|
||||
if (options.help) {
|
||||
optionsHelper.showHelp(PROGNAME);
|
||||
optionsHelper.showHelp(PROGNAME, LinkableRuntimeImage.isLinkableRuntime());
|
||||
return EXIT_OK;
|
||||
}
|
||||
if (optionsHelper.shouldListPlugins()) {
|
||||
@ -270,11 +284,6 @@ public class JlinkTask {
|
||||
if (jmods != null) {
|
||||
options.modulePath.add(jmods);
|
||||
}
|
||||
|
||||
if (options.modulePath.isEmpty()) {
|
||||
throw taskHelper.newBadArgs("err.modulepath.must.be.specified")
|
||||
.showUsage(true);
|
||||
}
|
||||
}
|
||||
|
||||
JlinkConfiguration config = initJlinkConfig();
|
||||
@ -300,7 +309,7 @@ public class JlinkTask {
|
||||
}
|
||||
cleanupOutput(outputPath);
|
||||
return EXIT_ERROR;
|
||||
} catch (IllegalArgumentException | ResolutionException e) {
|
||||
} catch (IllegalArgumentException | ResolutionException | RuntimeImageLinkException e) {
|
||||
log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
|
||||
if (DEBUG) {
|
||||
e.printStackTrace(log);
|
||||
@ -356,6 +365,7 @@ public class JlinkTask {
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
new OptionsValues(),
|
||||
null);
|
||||
|
||||
// Then create the Plugin Stack
|
||||
@ -370,7 +380,7 @@ public class JlinkTask {
|
||||
private JlinkConfiguration initJlinkConfig() throws BadArgs {
|
||||
Set<String> roots = new HashSet<>();
|
||||
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());
|
||||
// all observable modules are roots
|
||||
finder.findAll()
|
||||
@ -392,9 +402,75 @@ public class JlinkTask {
|
||||
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,
|
||||
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 {
|
||||
@ -413,6 +489,7 @@ public class JlinkTask {
|
||||
options.bindServices,
|
||||
options.endian,
|
||||
options.verbose,
|
||||
options,
|
||||
log);
|
||||
|
||||
// Then create the Plugin Stack
|
||||
@ -433,10 +510,10 @@ public class JlinkTask {
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a module finder of the given module path that limits
|
||||
* the observable modules to those in the transitive closure of
|
||||
* the modules specified in {@code limitMods} plus other modules
|
||||
* specified in the {@code roots} set.
|
||||
* Returns a module finder of the given module path or the system modules
|
||||
* if the module path is empty that limits the observable modules to those
|
||||
* in the transitive closure of the modules specified in {@code limitMods}
|
||||
* plus other modules specified in the {@code roots} set.
|
||||
*
|
||||
* @throws IllegalArgumentException if java.base module is present
|
||||
* but its descriptor has no version
|
||||
@ -445,14 +522,10 @@ public class JlinkTask {
|
||||
Set<String> limitMods,
|
||||
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();
|
||||
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()) {
|
||||
// use the version of java.base module, if present, as
|
||||
// the release version for multi-release JAR files
|
||||
@ -505,8 +578,9 @@ public class JlinkTask {
|
||||
|
||||
private static Path toPathLocation(ResolvedModule m) {
|
||||
Optional<URI> ouri = m.reference().location();
|
||||
if (ouri.isEmpty())
|
||||
if (ouri.isEmpty()) {
|
||||
throw new InternalError(m + " does not have a location");
|
||||
}
|
||||
URI uri = ouri.get();
|
||||
return Paths.get(uri);
|
||||
}
|
||||
@ -518,6 +592,7 @@ public class JlinkTask {
|
||||
boolean bindService,
|
||||
ByteOrder endian,
|
||||
boolean verbose,
|
||||
OptionsValues opts,
|
||||
PrintWriter log)
|
||||
throws IOException
|
||||
{
|
||||
@ -534,12 +609,35 @@ public class JlinkTask {
|
||||
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) {
|
||||
// print modules to be linked in
|
||||
cf.modules().stream()
|
||||
.sorted(Comparator.comparing(ResolvedModule::name))
|
||||
.forEach(rm -> log.format("%s %s%n",
|
||||
rm.name(), rm.reference().location().get()));
|
||||
.forEach(rm -> log.format("%s %s%s%n",
|
||||
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
|
||||
Set<ModuleReference> references = cf.modules().stream()
|
||||
@ -559,14 +657,15 @@ public class JlinkTask {
|
||||
.map(ModuleDescriptor::name)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
if (!"".equals(im))
|
||||
if (!"".equals(im)) {
|
||||
log.println("WARNING: Using incubator modules: " + im);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Path> mods = cf.modules().stream()
|
||||
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
|
||||
// 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
|
||||
// endianness
|
||||
if (endian != null && endian != targetPlatform.arch().byteOrder()) {
|
||||
@ -580,7 +679,92 @@ public class JlinkTask {
|
||||
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");
|
||||
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
|
||||
// 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
|
||||
@ -720,8 +906,9 @@ public class JlinkTask {
|
||||
String header,
|
||||
Set<ModuleReference> modules,
|
||||
Map<String, Set<String>> serviceToUses) {
|
||||
if (modules.isEmpty())
|
||||
if (modules.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a map of a service type to the provider modules
|
||||
Map<String, Set<ModuleDescriptor>> providers = new HashMap<>();
|
||||
@ -845,95 +1032,14 @@ public class JlinkTask {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static class ImageHelper implements ImageProvider {
|
||||
final Platform targetPlatform;
|
||||
final Path packagedModulesPath;
|
||||
final boolean ignoreSigning;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static record ImageHelper(Set<Archive> archives,
|
||||
Platform targetPlatform,
|
||||
Path packagedModulesPath,
|
||||
boolean generateRuntimeImage) implements ImageProvider {
|
||||
@Override
|
||||
public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
|
||||
ExecutableImage image = ImageFileCreator.create(archives,
|
||||
targetPlatform.arch().byteOrder(), stack);
|
||||
targetPlatform.arch().byteOrder(), stack, generateRuntimeImage, taskHelper);
|
||||
if (packagedModulesPath != null) {
|
||||
// copy the packaged modules to the given path
|
||||
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.
|
||||
*
|
||||
* 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.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
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.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.MissingResourceException;
|
||||
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.ImageBuilder;
|
||||
@ -55,7 +53,6 @@ import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin;
|
||||
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
|
||||
import jdk.tools.jlink.plugin.Plugin;
|
||||
import jdk.tools.jlink.plugin.Plugin.Category;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -584,7 +581,7 @@ public final class TaskHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void showHelp(String progName) {
|
||||
public void showHelp(String progName, boolean linkableRuntimeEnabled) {
|
||||
log.println(bundleHelper.getMessage("main.usage", progName));
|
||||
Stream.concat(options.stream(), pluginOptions.mainOptions.stream())
|
||||
.filter(option -> !option.isHidden())
|
||||
@ -594,6 +591,17 @@ public final class TaskHelper {
|
||||
});
|
||||
|
||||
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() {
|
||||
|
@ -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.
|
||||
#
|
||||
# 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\
|
||||
\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:
|
||||
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.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}
|
||||
@ -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.value.format:launcher value should be of form <command>=<module>[/<main-class>]: {0}
|
||||
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.path.not.found=path not found: {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.
|
||||
suggested.providers.header=Suggested 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 \
|
||||
container.support \
|
||||
systemd.support \
|
||||
jdk.containerized
|
||||
jdk.containerized \
|
||||
jlink.runtime.linkable \
|
||||
jlink.packagedModules
|
||||
|
||||
# Minimum jtreg version
|
||||
requiredVersion=7.4+1
|
||||
|
@ -102,7 +102,9 @@ requires.properties= \
|
||||
systemd.support \
|
||||
release.implementor \
|
||||
jdk.containerized \
|
||||
jdk.foreign.linker
|
||||
jdk.foreign.linker \
|
||||
jlink.runtime.linkable \
|
||||
jlink.packagedModules
|
||||
|
||||
# Minimum jtreg version
|
||||
requiredVersion=7.4+1
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @bug 8159927
|
||||
* @modules java.base/jdk.internal.util
|
||||
* @requires jlink.packagedModules
|
||||
* @run main JmodExcludedFiles
|
||||
* @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.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.tools.jlink.builder.ImageBuilder;
|
||||
import jdk.tools.jlink.internal.Archive;
|
||||
import jdk.tools.jlink.internal.ExecutableImage;
|
||||
import jdk.tools.jlink.internal.ImageFileCreator;
|
||||
import jdk.tools.jlink.internal.ImagePluginStack;
|
||||
import jdk.tools.jlink.internal.ExecutableImage;
|
||||
import jdk.tools.jlink.builder.ImageBuilder;
|
||||
import jdk.tools.jlink.plugin.ResourcePool;
|
||||
|
||||
|
||||
@ -223,6 +224,6 @@ public class ImageFileCreatorTest {
|
||||
ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(),
|
||||
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.
|
||||
*
|
||||
* 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.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -38,20 +37,18 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
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.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.Jlink;
|
||||
import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
|
||||
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.plugins.DefaultCompressPlugin;
|
||||
import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin;
|
||||
|
||||
import jdk.tools.jlink.plugin.Plugin;
|
||||
import jdk.tools.jlink.plugin.ResourcePool;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||
import tests.Helper;
|
||||
import tests.JImageGenerator;
|
||||
|
||||
@ -74,8 +71,6 @@ import tests.JImageGenerator;
|
||||
*/
|
||||
public class IntegrationTest {
|
||||
|
||||
private static final List<Integer> ordered = new ArrayList<>();
|
||||
|
||||
public static class MyPostProcessor implements PostProcessor, Plugin {
|
||||
|
||||
public static final String NAME = "mypostprocessor";
|
||||
@ -162,7 +157,7 @@ public class IntegrationTest {
|
||||
limits.add("java.management");
|
||||
JlinkConfiguration config = new Jlink.JlinkConfiguration(output,
|
||||
mods,
|
||||
JlinkTask.newModuleFinder(modulePaths, limits, mods));
|
||||
JlinkTask.newModuleFinder(modulePaths, limits, mods), false, false, false);
|
||||
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -21,14 +21,16 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import tests.JImageGenerator;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.tools.jlink.internal.LinkableRuntimeImage;
|
||||
import tests.JImageGenerator;
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @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 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
|
||||
private static String[] modules = new String[]{"m1", "m2", "m3", "m4"};
|
||||
|
||||
@ -69,8 +67,13 @@ public class JLinkDedupTestBatchSizeOne {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void compileAll() throws Throwable {
|
||||
if (!hasJmods()) return;
|
||||
private static String modulePath(boolean linkableRuntime) {
|
||||
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) {
|
||||
Path msrc = SRC_DIR.resolve(mn);
|
||||
@ -80,11 +83,15 @@ public class JLinkDedupTestBatchSizeOne {
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
JImageGenerator.getJLinkTask()
|
||||
.modulePath(MODULE_PATH)
|
||||
.modulePath(modulePath(linkableRuntime))
|
||||
.output(image.resolve("out-jlink-dedup"))
|
||||
.addMods("m1")
|
||||
.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.
|
||||
*
|
||||
* 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
|
||||
* @bug 8185130
|
||||
* @summary jlink should throw error if target image and current JDK versions don't match
|
||||
* @requires jlink.packagedModules
|
||||
* @modules java.base/jdk.internal.module
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.process.* CheckRuntimeVersion
|
||||
|
@ -21,6 +21,9 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
@ -32,16 +35,15 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.JImageGenerator;
|
||||
import tests.JImageValidator;
|
||||
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
|
||||
@ -63,12 +65,20 @@ public class GenerateJLIClassesPluginTest {
|
||||
|
||||
@BeforeTest
|
||||
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) {
|
||||
// In case of no linkable run-time image and also no packaged
|
||||
// modules, helper will be null.
|
||||
System.err.println("Test not run");
|
||||
return;
|
||||
}
|
||||
helper.generateDefaultModules();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -79,7 +89,6 @@ public class GenerateJLIClassesPluginTest {
|
||||
String fileString = "[SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_" + species + " (salvaged)\n";
|
||||
Files.write(baseFile, fileString.getBytes(Charset.defaultCharset()));
|
||||
Result result = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(helper.createNewImageDir("generate-jli-file"))
|
||||
.option("--generate-jli-classes=@" + baseFile.toString())
|
||||
.addMods("java.base")
|
||||
@ -105,7 +114,6 @@ public class GenerateJLIClassesPluginTest {
|
||||
fileString = "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeVirtual L_L (success)\n";
|
||||
Files.write(failFile, fileString.getBytes(Charset.defaultCharset()));
|
||||
Result result = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(helper.createNewImageDir("invalid-signature"))
|
||||
.option("--generate-jli-classes=@" + failFile.toString())
|
||||
.addMods("java.base")
|
||||
@ -118,7 +126,6 @@ public class GenerateJLIClassesPluginTest {
|
||||
@Test
|
||||
public static void nonExistentTraceFile() throws IOException {
|
||||
Result result = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(helper.createNewImageDir("non-existent-tracefile"))
|
||||
.option("--generate-jli-classes=@NON_EXISTENT_FILE")
|
||||
.addMods("java.base")
|
||||
@ -134,7 +141,6 @@ public class GenerateJLIClassesPluginTest {
|
||||
Path invokersTrace = Files.createTempFile("invokers", "trace");
|
||||
Files.writeString(invokersTrace, fileString, Charset.defaultCharset());
|
||||
Result result = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(helper.createNewImageDir("jli-invokers"))
|
||||
.option("--generate-jli-classes=@" + invokersTrace.toString())
|
||||
.addMods("java.base")
|
||||
@ -183,4 +189,5 @@ public class GenerateJLIClassesPluginTest {
|
||||
.map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class")
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,25 +22,27 @@
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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.plugins.PluginsResourceBundle;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
import tests.Helper;
|
||||
import tests.JImageGenerator;
|
||||
import tests.JImageValidator;
|
||||
import tests.Result;
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8152143 8152704 8155649 8165804 8185841 8176841 8190918
|
||||
* 8179071 8202537 8221432 8222098 8251317 8258794 8265315
|
||||
* 8296248 8306116 8174269 8333582
|
||||
* 8296248 8306116 8174269
|
||||
* @summary IncludeLocalesPlugin tests
|
||||
* @author Naoto Sato
|
||||
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||
@ -59,14 +61,14 @@ import tests.Result;
|
||||
*/
|
||||
public class IncludeLocalesPluginTest {
|
||||
|
||||
private final static String moduleName = "IncludeLocalesTest";
|
||||
private static final String moduleName = "IncludeLocalesTest";
|
||||
private static Helper helper;
|
||||
private final static int INCLUDE_LOCALES_OPTION = 0;
|
||||
private final static int ADDMODS_OPTION = 1;
|
||||
private final static int EXPECTED_LOCATIONS = 2;
|
||||
private final static int UNEXPECTED_PATHS = 3;
|
||||
private final static int AVAILABLE_LOCALES = 4;
|
||||
private final static int ERROR_MESSAGE = 5;
|
||||
private static final int INCLUDE_LOCALES_OPTION = 0;
|
||||
private static final int ADDMODS_OPTION = 1;
|
||||
private static final int EXPECTED_LOCATIONS = 2;
|
||||
private static final int UNEXPECTED_PATHS = 3;
|
||||
private static final int AVAILABLE_LOCALES = 4;
|
||||
private static final int ERROR_MESSAGE = 5;
|
||||
|
||||
private static int errors;
|
||||
|
||||
@ -413,11 +415,18 @@ public class IncludeLocalesPluginTest {
|
||||
};
|
||||
|
||||
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) {
|
||||
throw new RuntimeException("Helper could not be initialized");
|
||||
}
|
||||
helper.generateDefaultModules();
|
||||
|
||||
for (Object[] data : testData) {
|
||||
// create image for each test data
|
||||
@ -425,14 +434,12 @@ public class IncludeLocalesPluginTest {
|
||||
if (data[INCLUDE_LOCALES_OPTION].toString().isEmpty()) {
|
||||
System.out.println("Invoking jlink with no --include-locales option");
|
||||
result = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(helper.createNewImageDir(moduleName))
|
||||
.addMods((String) data[ADDMODS_OPTION])
|
||||
.call();
|
||||
} else {
|
||||
System.out.println("Invoking jlink with \"" + data[INCLUDE_LOCALES_OPTION] + "\"");
|
||||
result = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(helper.createNewImageDir(moduleName))
|
||||
.addMods((String) data[ADDMODS_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.
|
||||
*
|
||||
* 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 List<String> bootClasses;
|
||||
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 {
|
||||
Path jdkHome = Paths.get(System.getProperty("test.jdk"));
|
||||
if (!Files.exists(jdkHome.resolve("jmods"))) {
|
||||
return newHelper(false);
|
||||
}
|
||||
|
||||
public static Helper newHelper(boolean linkableRuntime) throws IOException {
|
||||
if (!linkableRuntime && !jdkHasPackagedModules()) {
|
||||
// Skip test if the jmods directory is missing (e.g. exploded image)
|
||||
System.err.println("Test not run, NO jmods directory");
|
||||
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();
|
||||
if (!Files.exists(stdjmods)) {
|
||||
if (!linkableRuntime && !Files.exists(stdjmods)) {
|
||||
throw new IOException("Standard jMods do not exist.");
|
||||
}
|
||||
this.fs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
@ -140,7 +150,8 @@ public class Helper {
|
||||
}
|
||||
|
||||
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
|
||||
+ jars.toAbsolutePath().toString() + File.pathSeparator
|
||||
+ explodedmodsclasses.toAbsolutePath().toString();
|
||||
@ -184,7 +195,7 @@ public class Helper {
|
||||
generateGarbage(jmodsclasses.resolve(moduleName));
|
||||
|
||||
Path jmodFile = jmods.resolve(moduleName + ".jmod");
|
||||
JModTask task = JImageGenerator.getJModTask()
|
||||
JModTask task = JImageGenerator.getJModTask(linkableRuntime)
|
||||
.jmod(jmodFile)
|
||||
.addJmods(stdjmods)
|
||||
.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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -159,7 +159,11 @@ public class JImageGenerator {
|
||||
}
|
||||
|
||||
public static JModTask getJModTask() {
|
||||
return new JModTask();
|
||||
return getJModTask(false);
|
||||
}
|
||||
|
||||
public static JModTask getJModTask(boolean linkableRuntime) {
|
||||
return new JModTask(linkableRuntime);
|
||||
}
|
||||
|
||||
public static JLinkTask getJLinkTask() {
|
||||
@ -350,11 +354,16 @@ public class JImageGenerator {
|
||||
private final List<Path> jars = new ArrayList<>();
|
||||
private final List<Path> jmods = new ArrayList<>();
|
||||
private final List<String> options = new ArrayList<>();
|
||||
private final boolean linkableRuntime;
|
||||
private Path output;
|
||||
private String hashModules;
|
||||
private String mainClass;
|
||||
private String moduleVersion;
|
||||
|
||||
private JModTask(boolean linkableRuntime) {
|
||||
this.linkableRuntime = linkableRuntime;
|
||||
}
|
||||
|
||||
public JModTask addNativeLibraries(Path cp) {
|
||||
this.libs.add(cp);
|
||||
return this;
|
||||
@ -414,7 +423,7 @@ public class JImageGenerator {
|
||||
// This is expect FIRST jmods THEN jars, if you change this, some tests could fail
|
||||
String jmods = toPath(this.jmods);
|
||||
String jars = toPath(this.jars);
|
||||
return jmods + File.pathSeparator + jars;
|
||||
return linkableRuntime ? jars : jmods + File.pathSeparator + jars;
|
||||
}
|
||||
|
||||
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("vm.flagless", this::isFlagless);
|
||||
map.put("jdk.foreign.linker", this::jdkForeignLinker);
|
||||
map.put("jlink.packagedModules", this::packagedModules);
|
||||
vmGC(map); // vm.gc.X = true/false
|
||||
vmGCforCDS(map); // may set vm.gc
|
||||
vmOptFinalFlags(map);
|
||||
@ -715,6 +716,21 @@ public class VMProps implements Callable<Map<String, String>> {
|
||||
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
|
||||
* which JVM is started with don't affect its behavior "significantly".
|
||||
|
@ -30,7 +30,7 @@
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.jlink
|
||||
* @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask
|
||||
* @run main AutostartPlugins
|
||||
* @run main/othervm AutostartPlugins
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -31,7 +31,7 @@
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.jlink
|
||||
* @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask
|
||||
* @run main InternalAPI
|
||||
* @run main/othervm InternalAPI
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
Loading…
Reference in New Issue
Block a user