mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-28 11:53:09 +00:00
8356219: jpackage places libapplauncher.so in incorrect location in the app image
Reviewed-by: almatvee
This commit is contained in:
parent
064b036ebd
commit
9c43871fd1
@ -26,14 +26,16 @@ package jdk.jpackage.internal;
|
||||
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.util.PathGroup;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Application directory layout.
|
||||
*/
|
||||
public final class ApplicationLayout implements PathGroup.Facade<ApplicationLayout> {
|
||||
public final class ApplicationLayout {
|
||||
enum PathRole {
|
||||
/**
|
||||
* Java run-time directory.
|
||||
@ -84,16 +86,34 @@ public final class ApplicationLayout implements PathGroup.Facade<ApplicationLayo
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathGroup pathGroup() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationLayout resolveAt(Path root) {
|
||||
return new ApplicationLayout(pathGroup().resolveAt(root));
|
||||
}
|
||||
|
||||
public List<Path> roots() {
|
||||
return data.roots();
|
||||
}
|
||||
|
||||
public long sizeInBytes() throws IOException {
|
||||
return data.sizeInBytes();
|
||||
}
|
||||
|
||||
public void copy(ApplicationLayout other) throws IOException {
|
||||
data.copy(other.data);
|
||||
}
|
||||
|
||||
public void move(ApplicationLayout other) throws IOException {
|
||||
data.move(other.data);
|
||||
}
|
||||
|
||||
public void transform(ApplicationLayout other, PathGroup.TransformHandler handler) throws IOException {
|
||||
data.transform(other.data, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to launchers directory.
|
||||
*/
|
||||
|
||||
@ -24,36 +24,69 @@
|
||||
*/
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
/**
|
||||
* Group of paths.
|
||||
* Each path in the group is assigned a unique id.
|
||||
* Group of paths. Each path in the group is assigned a unique id.
|
||||
*/
|
||||
public final class PathGroup {
|
||||
|
||||
/**
|
||||
* Creates path group with the initial paths.
|
||||
*
|
||||
* @param paths the initial paths
|
||||
*/
|
||||
public PathGroup(Map<Object, Path> paths) {
|
||||
paths.keySet().forEach(Objects::requireNonNull);
|
||||
paths.values().forEach(Objects::requireNonNull);
|
||||
entries = new HashMap<>(paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path associated with the given identifier in this path group.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @return the path corresponding to the given identifier in this path group or
|
||||
* <code>null</code> if there is no such path
|
||||
*/
|
||||
public Path getPath(Object id) {
|
||||
if (id == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
Objects.requireNonNull(id);
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified path value to the given identifier in this path group.
|
||||
* If the given identifier doesn't exist in this path group, it is added,
|
||||
* otherwise, the current value associated with the identifier is replaced with
|
||||
* the given path value. If the path value is <code>null</code> the given
|
||||
* identifier is removed from this path group if it existed; otherwise, no
|
||||
* action is taken.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @param path the path to associate with the identifier or <code>null</code>
|
||||
*/
|
||||
public void setPath(Object id, Path path) {
|
||||
Objects.requireNonNull(id);
|
||||
if (path != null) {
|
||||
entries.put(id, path);
|
||||
} else {
|
||||
@ -62,196 +95,379 @@ public final class PathGroup {
|
||||
}
|
||||
|
||||
/**
|
||||
* All configured entries.
|
||||
* Adds a path associated with the new unique identifier to this path group.
|
||||
*
|
||||
* @param path the path to associate the new unique identifier in this path
|
||||
* group
|
||||
*/
|
||||
public void ghostPath(Path path) {
|
||||
Objects.requireNonNull(path);
|
||||
setPath(new Object(), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all identifiers of this path group.
|
||||
* <p>
|
||||
* The order of identifiers in the returned list is undefined.
|
||||
*
|
||||
* @return all identifiers of this path group
|
||||
*/
|
||||
public Set<Object> keys() {
|
||||
return entries.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets paths associated with all identifiers in this path group.
|
||||
* <p>
|
||||
* The order of paths in the returned list is undefined.
|
||||
*
|
||||
* @return paths associated with all identifiers in this path group
|
||||
*/
|
||||
public List<Path> paths() {
|
||||
return entries.values().stream().toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Root entries.
|
||||
* Gets root paths in this path group.
|
||||
* <p>
|
||||
* If multiple identifiers are associated with the same path value in the group,
|
||||
* the path value is added to the returned list only once. Paths that are
|
||||
* descendants of other paths in the group are not added to the returned list.
|
||||
* <p>
|
||||
* The order of paths in the returned list is undefined.
|
||||
*
|
||||
* @return unique root paths in this path group
|
||||
*/
|
||||
public List<Path> roots() {
|
||||
// Sort by the number of path components in ascending order.
|
||||
List<Map.Entry<Path, Path>> sorted = normalizedPaths().stream().sorted(
|
||||
(a, b) -> a.getKey().getNameCount() - b.getKey().getNameCount()).toList();
|
||||
if (entries.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// Returns `true` if `a` is a parent of `b`
|
||||
BiFunction<Map.Entry<Path, Path>, Map.Entry<Path, Path>, Boolean> isParentOrSelf = (a, b) -> {
|
||||
return a == b || b.getKey().startsWith(a.getKey());
|
||||
};
|
||||
// Sort by the number of path components in descending order.
|
||||
final var sorted = entries.entrySet().stream().map(e -> {
|
||||
return Map.entry(e.getValue().normalize(), e.getValue());
|
||||
}).sorted(Comparator.comparingInt(e -> e.getValue().getNameCount() * -1)).distinct().toList();
|
||||
|
||||
return sorted.stream().filter(
|
||||
v -> v == sorted.stream().sequential().filter(
|
||||
v2 -> isParentOrSelf.apply(v2, v)).findFirst().get()).map(
|
||||
v -> v.getValue()).toList();
|
||||
final var shortestNormalizedPath = sorted.getLast().getKey();
|
||||
if (shortestNormalizedPath.getNameCount() == 1 && shortestNormalizedPath.getFileName().toString().isEmpty()) {
|
||||
return List.of(sorted.getLast().getValue());
|
||||
}
|
||||
|
||||
final List<Path> roots = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < sorted.size(); ++i) {
|
||||
final var path = sorted.get(i).getKey();
|
||||
boolean pathIsRoot = true;
|
||||
for (int j = i + 1; j < sorted.size(); ++j) {
|
||||
final var maybeParent = sorted.get(j).getKey();
|
||||
if (path.getNameCount() > maybeParent.getNameCount() && path.startsWith(maybeParent)) {
|
||||
pathIsRoot = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pathIsRoot) {
|
||||
roots.add(sorted.get(i).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes in root paths of this path group. The method sums
|
||||
* the size of all root path entries in the group. If the path entry is a
|
||||
* directory it calculates the total size of the files in the directory. If the
|
||||
* path entry is a file, it takes its size.
|
||||
*
|
||||
* @return the total number of bytes in root paths of this path group
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public long sizeInBytes() throws IOException {
|
||||
long reply = 0;
|
||||
for (Path dir : roots().stream().filter(f -> Files.isDirectory(f)).collect(
|
||||
Collectors.toList())) {
|
||||
try (Stream<Path> stream = Files.walk(dir)) {
|
||||
reply += stream.filter(p -> Files.isRegularFile(p)).mapToLong(
|
||||
f -> f.toFile().length()).sum();
|
||||
final var roots = roots();
|
||||
try {
|
||||
for (Path dir : roots.stream().filter(Files::isDirectory).toList()) {
|
||||
try (Stream<Path> stream = Files.walk(dir)) {
|
||||
reply += stream.mapToLong(PathGroup::sizeInBytes).sum();
|
||||
}
|
||||
}
|
||||
reply += roots.stream().mapToLong(PathGroup::sizeInBytes).sum();
|
||||
} catch (UncheckedIOException ex) {
|
||||
throw ex.getCause();
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
private static long sizeInBytes(Path path) throws UncheckedIOException {
|
||||
if (Files.isRegularFile(path)) {
|
||||
try {
|
||||
return Files.size(path);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this path group with all paths resolved against the given
|
||||
* root. Taken action is equivalent to creating a copy of this path group and
|
||||
* calling <code>root.resolve()</code> on every path in the copy.
|
||||
*
|
||||
* @param root the root against which to resolve paths
|
||||
*
|
||||
* @return a new path group resolved against the given root path
|
||||
*/
|
||||
public PathGroup resolveAt(Path root) {
|
||||
return new PathGroup(entries.entrySet().stream().collect(
|
||||
Collectors.toMap(e -> e.getKey(),
|
||||
e -> root.resolve(e.getValue()))));
|
||||
Objects.requireNonNull(root);
|
||||
return new PathGroup(entries.entrySet().stream()
|
||||
.collect(toMap(Map.Entry::getKey, e -> root.resolve(e.getValue()))));
|
||||
}
|
||||
|
||||
public void copy(PathGroup dst) throws IOException {
|
||||
copy(this, dst, null, false);
|
||||
/**
|
||||
* Copies files/directories from the locations in the path group into the
|
||||
* locations of the given path group. For every identifier found in this and the
|
||||
* given group, copy the associated file or directory from the location
|
||||
* specified by the path value associated with the identifier in this group into
|
||||
* the location associated with the identifier in the given group.
|
||||
*
|
||||
* @param dst the destination path group
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void copy(PathGroup dst, CopyOption ...options) throws IOException {
|
||||
final var handler = new Copy(false, options);
|
||||
copy(this, dst, handler, handler.followSymlinks());
|
||||
}
|
||||
|
||||
public void move(PathGroup dst) throws IOException {
|
||||
copy(this, dst, null, true);
|
||||
/**
|
||||
* Similar to {@link #copy(PathGroup)} but moves files/directories instead of
|
||||
* copying.
|
||||
*
|
||||
* @param dst the destination path group
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void move(PathGroup dst, CopyOption ...options) throws IOException {
|
||||
final var handler = new Copy(true, options);
|
||||
copy(this, dst, handler, handler.followSymlinks());
|
||||
deleteEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #copy(PathGroup)} but uses the given handler to transform
|
||||
* paths instead of coping.
|
||||
*
|
||||
* @param dst the destination path group
|
||||
* @param handler the path transformation handler
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void transform(PathGroup dst, TransformHandler handler) throws IOException {
|
||||
copy(this, dst, handler, false);
|
||||
}
|
||||
|
||||
public static interface Facade<T> {
|
||||
PathGroup pathGroup();
|
||||
|
||||
default Collection<Path> paths() {
|
||||
return pathGroup().paths();
|
||||
}
|
||||
|
||||
default List<Path> roots() {
|
||||
return pathGroup().roots();
|
||||
}
|
||||
|
||||
default long sizeInBytes() throws IOException {
|
||||
return pathGroup().sizeInBytes();
|
||||
}
|
||||
|
||||
T resolveAt(Path root);
|
||||
|
||||
default void copy(Facade<T> dst) throws IOException {
|
||||
pathGroup().copy(dst.pathGroup());
|
||||
}
|
||||
|
||||
default void move(Facade<T> dst) throws IOException {
|
||||
pathGroup().move(dst.pathGroup());
|
||||
}
|
||||
|
||||
default void transform(Facade<T> dst, TransformHandler handler) throws
|
||||
IOException {
|
||||
pathGroup().transform(dst.pathGroup(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler of file copying and directory creating.
|
||||
*
|
||||
* @see #transform
|
||||
*/
|
||||
public static interface TransformHandler {
|
||||
void copyFile(Path src, Path dst) throws IOException;
|
||||
void createDirectory(Path dir) throws IOException;
|
||||
|
||||
/**
|
||||
* Request to copy a file from the given source location into the given
|
||||
* destination location.
|
||||
*
|
||||
* @implNote Default implementation takes no action
|
||||
*
|
||||
* @param src the source file location
|
||||
* @param dst the destination file location
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
default void copyFile(Path src, Path dst) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to create a directory at the given location.
|
||||
*
|
||||
* @implNote Default implementation takes no action
|
||||
*
|
||||
* @param dir the path where the directory is requested to be created
|
||||
* @throws IOException
|
||||
*/
|
||||
default void createDirectory(Path dir) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to copy a symbolic link from the given source location into the given
|
||||
* destination location.
|
||||
*
|
||||
* @implNote Default implementation calls {@link #copyFile}.
|
||||
*
|
||||
* @param src the source symbolic link location
|
||||
* @param dst the destination symbolic link location
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
default void copySymbolicLink(Path src, Path dst) throws IOException {
|
||||
copyFile(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copy(PathGroup src, PathGroup dst,
|
||||
TransformHandler handler, boolean move) throws IOException {
|
||||
List<Map.Entry<Path, Path>> copyItems = new ArrayList<>();
|
||||
List<Path> excludeItems = new ArrayList<>();
|
||||
|
||||
for (var id: src.entries.keySet()) {
|
||||
Path srcPath = src.entries.get(id);
|
||||
if (dst.entries.containsKey(id)) {
|
||||
copyItems.add(Map.entry(srcPath, dst.entries.get(id)));
|
||||
private void deleteEntries() throws IOException {
|
||||
for (final var file : entries.values()) {
|
||||
if (Files.isDirectory(file)) {
|
||||
FileUtils.deleteRecursive(file);
|
||||
} else {
|
||||
excludeItems.add(srcPath);
|
||||
Files.deleteIfExists(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record CopySpec(Path basepath, Path from, Path to) {
|
||||
CopySpec {
|
||||
Objects.requireNonNull(basepath);
|
||||
Objects.requireNonNull(to);
|
||||
if (!from.startsWith(basepath)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
copy(move, copyItems, excludeItems, handler);
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if ((obj == null) || (getClass() != obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
CopySpec other = (CopySpec) obj;
|
||||
return Objects.equals(from, other.from) && Objects.equals(to, other.to);
|
||||
}
|
||||
|
||||
Path fromNormalized() {
|
||||
return from().normalize();
|
||||
}
|
||||
|
||||
Path toNormalized() {
|
||||
return to().normalize();
|
||||
}
|
||||
|
||||
CopySpec(Path from, Path to) {
|
||||
this(from, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copy(boolean move, List<Map.Entry<Path, Path>> entries,
|
||||
List<Path> excludePaths, TransformHandler handler) throws
|
||||
IOException {
|
||||
private static void copy(PathGroup src, PathGroup dst, TransformHandler handler, boolean followSymlinks) throws IOException {
|
||||
List<CopySpec> copySpecs = new ArrayList<>();
|
||||
List<Path> excludePaths = new ArrayList<>();
|
||||
|
||||
if (handler == null) {
|
||||
handler = new TransformHandler() {
|
||||
@Override
|
||||
public void copyFile(Path src, Path dst) throws IOException {
|
||||
Files.createDirectories(dst.getParent());
|
||||
if (move) {
|
||||
Files.move(src, dst);
|
||||
} else {
|
||||
Files.copy(src, dst);
|
||||
for (final var e : src.entries.entrySet()) {
|
||||
final var srcPath = e.getValue();
|
||||
final var dstPath = dst.entries.get(e.getKey());
|
||||
if (dstPath != null) {
|
||||
copySpecs.add(new CopySpec(srcPath, dstPath));
|
||||
} else {
|
||||
excludePaths.add(srcPath.normalize());
|
||||
}
|
||||
}
|
||||
|
||||
copy(copySpecs, excludePaths, handler, followSymlinks);
|
||||
}
|
||||
|
||||
private record Copy(boolean move, boolean followSymlinks, CopyOption ... options) implements TransformHandler {
|
||||
|
||||
Copy(boolean move, CopyOption ... options) {
|
||||
this(move, !Set.of(options).contains(LinkOption.NOFOLLOW_LINKS), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFile(Path src, Path dst) throws IOException {
|
||||
Files.createDirectories(dst.getParent());
|
||||
if (move) {
|
||||
Files.move(src, dst, options);
|
||||
} else {
|
||||
Files.copy(src, dst, options);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(Path dir) throws IOException {
|
||||
Files.createDirectories(dir);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean match(Path what, List<Path> paths) {
|
||||
return paths.stream().anyMatch(what::startsWith);
|
||||
}
|
||||
|
||||
private static void copy(List<CopySpec> copySpecs, List<Path> excludePaths,
|
||||
TransformHandler handler, boolean followSymlinks) throws IOException {
|
||||
Objects.requireNonNull(excludePaths);
|
||||
Objects.requireNonNull(handler);
|
||||
|
||||
|
||||
final var filteredCopySpecs = copySpecs.stream().<CopySpec>mapMulti((copySpec, consumer) -> {
|
||||
final var src = copySpec.from();
|
||||
|
||||
if (!Files.exists(src) || match(src, excludePaths)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Files.isDirectory(copySpec.from())) {
|
||||
final var dst = copySpec.to;
|
||||
final var walkMode = followSymlinks ? new FileVisitOption[] { FileVisitOption.FOLLOW_LINKS } : new FileVisitOption[0];
|
||||
try (final var files = Files.walk(src, walkMode)) {
|
||||
files.filter(file -> {
|
||||
return !match(file, excludePaths);
|
||||
}).map(file -> {
|
||||
return new CopySpec(src, file, dst.resolve(src.relativize(file)));
|
||||
}).toList().forEach(consumer::accept);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
} else {
|
||||
consumer.accept(copySpec);
|
||||
}
|
||||
}).collect(groupingBy(CopySpec::fromNormalized, collectingAndThen(toSet(), copySpecGroup -> {
|
||||
return copySpecGroup.stream().filter(copySpec -> {
|
||||
for (final var otherCopySpec : copySpecGroup) {
|
||||
if (otherCopySpec != copySpec && !otherCopySpec.basepath().equals(copySpec.basepath())
|
||||
&& otherCopySpec.basepath().startsWith(copySpec.basepath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
}))).values().stream().flatMap(Collection::stream).toList();
|
||||
|
||||
@Override
|
||||
public void createDirectory(Path dir) throws IOException {
|
||||
Files.createDirectories(dir);
|
||||
filteredCopySpecs.stream().collect(toMap(CopySpec::toNormalized, x -> x, (x, y) -> {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Duplicate source files [%s] and [%s] for [%s] destination file", x.from(), y.from(), x.to()));
|
||||
}));
|
||||
|
||||
try {
|
||||
filteredCopySpecs.stream().forEach(copySpec -> {
|
||||
try {
|
||||
if (Files.isSymbolicLink(copySpec.from())) {
|
||||
handler.copySymbolicLink(copySpec.from(), copySpec.to());
|
||||
} else if (Files.isDirectory(copySpec.from())) {
|
||||
handler.createDirectory(copySpec.to());
|
||||
} else {
|
||||
handler.copyFile(copySpec.from(), copySpec.to());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
};
|
||||
});
|
||||
} catch (UncheckedIOException ex) {
|
||||
throw ex.getCause();
|
||||
}
|
||||
|
||||
// destination -> source file mapping
|
||||
Map<Path, Path> actions = new HashMap<>();
|
||||
for (var action: entries) {
|
||||
Path src = action.getKey();
|
||||
Path dst = action.getValue();
|
||||
if (Files.isDirectory(src)) {
|
||||
try (Stream<Path> stream = Files.walk(src)) {
|
||||
stream.sequential().forEach(path -> actions.put(dst.resolve(
|
||||
src.relativize(path)).normalize(), path));
|
||||
}
|
||||
} else {
|
||||
actions.put(dst.normalize(), src);
|
||||
}
|
||||
}
|
||||
|
||||
for (var action : actions.entrySet()) {
|
||||
Path dst = action.getKey();
|
||||
Path src = action.getValue();
|
||||
|
||||
if (excludePaths.stream().anyMatch(src::startsWith)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (src.equals(dst) || !src.toFile().exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Files.isDirectory(src)) {
|
||||
handler.createDirectory(dst);
|
||||
} else {
|
||||
handler.copyFile(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
if (move) {
|
||||
// Delete source dirs.
|
||||
for (var entry: entries) {
|
||||
Path srcFile = entry.getKey();
|
||||
if (Files.isDirectory(srcFile)) {
|
||||
FileUtils.deleteRecursive(srcFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map.Entry<Path, Path> normalizedPath(Path v) {
|
||||
final Path normalized;
|
||||
if (!v.isAbsolute()) {
|
||||
normalized = Path.of("./").resolve(v.normalize());
|
||||
} else {
|
||||
normalized = v.normalize();
|
||||
}
|
||||
|
||||
return Map.entry(normalized, v);
|
||||
}
|
||||
|
||||
private List<Map.Entry<Path, Path>> normalizedPaths() {
|
||||
return entries.values().stream().map(PathGroup::normalizedPath).collect(
|
||||
Collectors.toList());
|
||||
}
|
||||
|
||||
private final Map<Object, Path> entries;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, 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
|
||||
@ -29,7 +29,7 @@ import java.util.Optional;
|
||||
|
||||
public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
Path runtimeDirectory, Path runtimeHomeDirectory, Path appModsDirectory,
|
||||
Path destktopIntegrationDirectory, Path contentDirectory) {
|
||||
Path desktopIntegrationDirectory, Path contentDirectory, Path libapplauncher) {
|
||||
|
||||
public ApplicationLayout resolveAt(Path root) {
|
||||
return new ApplicationLayout(
|
||||
@ -38,8 +38,9 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
resolve(root, runtimeDirectory),
|
||||
resolve(root, runtimeHomeDirectory),
|
||||
resolve(root, appModsDirectory),
|
||||
resolve(root, destktopIntegrationDirectory),
|
||||
resolve(root, contentDirectory));
|
||||
resolve(root, desktopIntegrationDirectory),
|
||||
resolve(root, contentDirectory),
|
||||
resolve(root, libapplauncher));
|
||||
}
|
||||
|
||||
public static ApplicationLayout linuxAppImage() {
|
||||
@ -50,7 +51,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
Path.of("lib/runtime"),
|
||||
Path.of("lib/app/mods"),
|
||||
Path.of("lib"),
|
||||
Path.of("lib")
|
||||
Path.of("lib"),
|
||||
Path.of("lib/libapplauncher.so")
|
||||
);
|
||||
}
|
||||
|
||||
@ -62,7 +64,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
Path.of("runtime"),
|
||||
Path.of("app/mods"),
|
||||
Path.of(""),
|
||||
Path.of("")
|
||||
Path.of(""),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@ -74,7 +77,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
Path.of("Contents/runtime/Contents/Home"),
|
||||
Path.of("Contents/app/mods"),
|
||||
Path.of("Contents/Resources"),
|
||||
Path.of("Contents")
|
||||
Path.of("Contents"),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@ -102,6 +106,7 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
@ -116,7 +121,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory,
|
||||
lib.resolve("runtime"),
|
||||
lib.resolve("app/mods"),
|
||||
lib,
|
||||
lib
|
||||
lib,
|
||||
lib.resolve("lib/libapplauncher.so")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1008,9 +1008,9 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
|
||||
final Path lookupPath = AppImageFile.getPathInAppImage(appImageDir);
|
||||
if (!expectAppImageFile()) {
|
||||
assertFileInAppImage(lookupPath, null);
|
||||
assertFileNotInAppImage(lookupPath);
|
||||
} else {
|
||||
assertFileInAppImage(lookupPath, lookupPath);
|
||||
assertFileInAppImage(lookupPath);
|
||||
|
||||
if (TKit.isOSX()) {
|
||||
final Path rootDir = isImagePackageType() ? outputBundle() :
|
||||
@ -1035,22 +1035,39 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
final Path lookupPath = PackageFile.getPathInAppImage(Path.of(""));
|
||||
|
||||
if (isRuntime() || isImagePackageType() || TKit.isLinux()) {
|
||||
assertFileInAppImage(lookupPath, null);
|
||||
assertFileNotInAppImage(lookupPath);
|
||||
} else {
|
||||
if (TKit.isOSX() && hasArgument("--app-image")) {
|
||||
String appImage = getArgumentValue("--app-image");
|
||||
if (AppImageFile.load(Path.of(appImage)).macSigned()) {
|
||||
assertFileInAppImage(lookupPath, null);
|
||||
assertFileNotInAppImage(lookupPath);
|
||||
} else {
|
||||
assertFileInAppImage(lookupPath, lookupPath);
|
||||
assertFileInAppImage(lookupPath);
|
||||
}
|
||||
} else {
|
||||
assertFileInAppImage(lookupPath, lookupPath);
|
||||
assertFileInAppImage(lookupPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void assertFileInAppImage(Path expectedPath) {
|
||||
assertFileInAppImage(expectedPath.getFileName(), expectedPath);
|
||||
}
|
||||
|
||||
public void assertFileNotInAppImage(Path filename) {
|
||||
assertFileInAppImage(filename, null);
|
||||
}
|
||||
|
||||
private void assertFileInAppImage(Path filename, Path expectedPath) {
|
||||
if (expectedPath != null) {
|
||||
if (expectedPath.isAbsolute()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (!expectedPath.getFileName().equals(filename.getFileName())) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
if (filename.getNameCount() > 1) {
|
||||
assertFileInAppImage(filename.getFileName(), expectedPath);
|
||||
return;
|
||||
|
||||
@ -57,7 +57,7 @@ public final class LauncherIconVerifier {
|
||||
label = String.format("[%s]", launcherName);
|
||||
}
|
||||
|
||||
Path iconPath = cmd.appLayout().destktopIntegrationDirectory().resolve(
|
||||
Path iconPath = cmd.appLayout().desktopIntegrationDirectory().resolve(
|
||||
curLauncherName + TKit.ICON_SUFFIX);
|
||||
|
||||
if (TKit.isWindows()) {
|
||||
|
||||
@ -82,7 +82,7 @@ public final class LinuxHelper {
|
||||
String desktopFileName = String.format("%s-%s.desktop", getPackageName(
|
||||
cmd), Optional.ofNullable(launcherName).orElseGet(
|
||||
() -> cmd.name()).replaceAll("\\s+", "_"));
|
||||
return cmd.appLayout().destktopIntegrationDirectory().resolve(
|
||||
return cmd.appLayout().desktopIntegrationDirectory().resolve(
|
||||
desktopFileName);
|
||||
}
|
||||
|
||||
@ -393,7 +393,7 @@ public final class LinuxHelper {
|
||||
|
||||
test.addInstallVerifier(cmd -> {
|
||||
// Verify .desktop files.
|
||||
try (var files = Files.list(cmd.appLayout().destktopIntegrationDirectory())) {
|
||||
try (var files = Files.list(cmd.appLayout().desktopIntegrationDirectory())) {
|
||||
List<Path> desktopFiles = files
|
||||
.filter(path -> path.getFileName().toString().endsWith(".desktop"))
|
||||
.toList();
|
||||
@ -470,7 +470,7 @@ public final class LinuxHelper {
|
||||
|
||||
for (var e : List.<Map.Entry<Map.Entry<String, Optional<String>>, Function<ApplicationLayout, Path>>>of(
|
||||
Map.entry(Map.entry("Exec", Optional.of(launcherPath)), ApplicationLayout::launchersDirectory),
|
||||
Map.entry(Map.entry("Icon", Optional.empty()), ApplicationLayout::destktopIntegrationDirectory))) {
|
||||
Map.entry(Map.entry("Icon", Optional.empty()), ApplicationLayout::desktopIntegrationDirectory))) {
|
||||
var path = e.getKey().getValue().or(() -> Optional.of(data.get(
|
||||
e.getKey().getKey()))).map(Path::of).get();
|
||||
TKit.assertFileExists(cmd.pathToUnpackedPackageFile(path));
|
||||
@ -537,7 +537,7 @@ public final class LinuxHelper {
|
||||
|
||||
Path systemDesktopFile = getSystemDesktopFilesFolder().resolve(
|
||||
desktopFileName);
|
||||
Path appDesktopFile = cmd.appLayout().destktopIntegrationDirectory().resolve(
|
||||
Path appDesktopFile = cmd.appLayout().desktopIntegrationDirectory().resolve(
|
||||
desktopFileName);
|
||||
|
||||
TKit.assertFileExists(systemDesktopFile);
|
||||
@ -569,7 +569,7 @@ public final class LinuxHelper {
|
||||
final Path mimeTypeIconFileName = fa.getLinuxIconFileName();
|
||||
if (mimeTypeIconFileName != null) {
|
||||
// Verify there are xdg registration commands for mime icon file.
|
||||
Path mimeTypeIcon = cmd.appLayout().destktopIntegrationDirectory().resolve(
|
||||
Path mimeTypeIcon = cmd.appLayout().desktopIntegrationDirectory().resolve(
|
||||
mimeTypeIconFileName);
|
||||
|
||||
Map<Scriptlet, List<String>> scriptlets = getScriptlets(cmd);
|
||||
|
||||
@ -23,16 +23,9 @@
|
||||
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@ -40,6 +33,25 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@ -113,6 +125,69 @@ public class PathGroupTest {
|
||||
assertEquals(Path.of("foo"), roots.get(0));
|
||||
}
|
||||
|
||||
public record TestRootsSpec(Collection<Path> paths, Set<Path> expectedRoots) {
|
||||
public TestRootsSpec {
|
||||
paths.forEach(Objects::requireNonNull);
|
||||
expectedRoots.forEach(Objects::requireNonNull);
|
||||
}
|
||||
|
||||
void test() {
|
||||
final var pg = new PathGroup(paths.stream().collect(toMap(x -> new Object(), x -> x)));
|
||||
final var actualRoots = pg.roots().stream().collect(toSet());
|
||||
assertEquals(expectedRoots, actualRoots);
|
||||
}
|
||||
|
||||
static TestRootsSpec create(Collection<String> paths, Set<String> expectedRoots) {
|
||||
return new TestRootsSpec(paths.stream().map(Path::of).toList(), expectedRoots.stream().map(Path::of).collect(toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("testRootsValues")
|
||||
public void testRoots(TestRootsSpec testSpec) {
|
||||
testSpec.test();
|
||||
}
|
||||
|
||||
private static Collection<TestRootsSpec> testRootsValues() {
|
||||
return List.of(
|
||||
TestRootsSpec.create(List.of(""), Set.of("")),
|
||||
TestRootsSpec.create(List.of("/", "a/b/c", "a/b", "a/b", "a/b/"), Set.of("a/b", "/"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void testSizeInBytes(List<Path> paths, @TempDir Path tempDir) throws IOException {
|
||||
final var files = Set.of("AA.txt", "a/b/c/BB.txt", "a/b/c/DD.txt", "d/foo.txt").stream().map(Path::of).toList();
|
||||
|
||||
int counter = 0;
|
||||
for (var file : files) {
|
||||
file = tempDir.resolve(file);
|
||||
Files.createDirectories(file.getParent());
|
||||
Files.writeString(file, "x".repeat(++counter * 100));
|
||||
}
|
||||
|
||||
final var expectedSize = Stream.of(walkFiles(tempDir))
|
||||
.map(tempDir::resolve)
|
||||
.filter(Files::isRegularFile)
|
||||
.map(toFunction(Files::size))
|
||||
.mapToLong(Long::longValue).sum();
|
||||
|
||||
final var pg = new PathGroup(paths.stream().collect(toMap(x -> new Object(), x -> x))).resolveAt(tempDir);
|
||||
|
||||
assertEquals(expectedSize, pg.sizeInBytes());
|
||||
}
|
||||
|
||||
private static Collection<List<Path>> testSizeInBytes() {
|
||||
return Stream.of(
|
||||
List.of(""),
|
||||
List.of("AA.txt", "a/b", "d", "non-existant", "non-existant/foo/bar"),
|
||||
List.of("AA.txt", "a/b", "d", "a/b/c/BB.txt", "a/b/c/BB.txt")
|
||||
).map(v -> {
|
||||
return v.stream().map(Path::of).toList();
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveAt() {
|
||||
final PathGroup pg = new PathGroup(Map.of(
|
||||
@ -140,7 +215,7 @@ public class PathGroupTest {
|
||||
assertEquals(aPath, pg2.roots().get(0));
|
||||
}
|
||||
|
||||
enum TransformType { COPY, MOVE, HANDLER };
|
||||
enum TransformType { COPY, MOVE, HANDLER }
|
||||
|
||||
private static Stream<Object[]> testTransform() {
|
||||
return Stream.of(TransformType.values()).flatMap(transform -> {
|
||||
@ -265,6 +340,253 @@ public class PathGroupTest {
|
||||
}
|
||||
}
|
||||
|
||||
private enum PathRole {
|
||||
DESKTOP,
|
||||
LINUX_APPLAUNCHER_LIB,
|
||||
}
|
||||
|
||||
private static PathGroup linuxAppImage() {
|
||||
return new PathGroup(Map.of(
|
||||
PathRole.DESKTOP, Path.of("lib"),
|
||||
PathRole.LINUX_APPLAUNCHER_LIB, Path.of("lib/libapplauncher.so")
|
||||
));
|
||||
}
|
||||
|
||||
private static PathGroup linuxUsrTreePackageImage(Path prefix, String packageName) {
|
||||
final Path lib = prefix.resolve(Path.of("lib", packageName));
|
||||
return new PathGroup(Map.of(
|
||||
PathRole.DESKTOP, lib,
|
||||
PathRole.LINUX_APPLAUNCHER_LIB, lib.resolve("lib/libapplauncher.so")
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLinuxLibapplauncher(@TempDir Path tempDir) throws IOException {
|
||||
final Path srcDir = tempDir.resolve("src");
|
||||
final Path dstDir = tempDir.resolve("dst");
|
||||
|
||||
final Map<PathRole, List<Path>> files = Map.of(
|
||||
PathRole.LINUX_APPLAUNCHER_LIB, List.of(Path.of("")),
|
||||
PathRole.DESKTOP, List.of(Path.of("UsrUsrTreeTest.png"))
|
||||
);
|
||||
|
||||
final var srcAppLayout = linuxAppImage().resolveAt(srcDir);
|
||||
final var dstAppLayout = linuxUsrTreePackageImage(dstDir.resolve("usr"), "foo");
|
||||
|
||||
for (final var e : files.entrySet()) {
|
||||
final var pathRole = e.getKey();
|
||||
final var basedir = srcAppLayout.getPath(pathRole);
|
||||
for (var file : e.getValue().stream().map(basedir::resolve).toList()) {
|
||||
Files.createDirectories(file.getParent());
|
||||
Files.writeString(file, "foo");
|
||||
}
|
||||
}
|
||||
|
||||
srcAppLayout.copy(dstAppLayout);
|
||||
|
||||
Stream.of(walkFiles(dstDir)).filter(p -> {
|
||||
return p.getFileName().equals(dstAppLayout.getPath(PathRole.LINUX_APPLAUNCHER_LIB).getFileName());
|
||||
}).reduce((x, y) -> {
|
||||
throw new AssertionError(String.format("Multiple libapplauncher: [%s], [%s]", x, y));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public enum TestFileContent {
|
||||
A,
|
||||
B,
|
||||
C;
|
||||
|
||||
void assertFileContent(Path path) {
|
||||
try {
|
||||
final var expected = name();
|
||||
final var actual = Files.readString(path);
|
||||
assertEquals(expected, actual);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void createFile(Path path) {
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.writeString(path, name());
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record PathGroupCopy(Path from, Path to) {
|
||||
public PathGroupCopy {
|
||||
Objects.requireNonNull(from);
|
||||
}
|
||||
|
||||
public PathGroupCopy(Path from) {
|
||||
this(from, null);
|
||||
}
|
||||
}
|
||||
|
||||
public record TestFile(Path path, TestFileContent content) {
|
||||
public TestFile {
|
||||
Objects.requireNonNull(content);
|
||||
if (path.isAbsolute()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
void assertFileContent(Path basedir) {
|
||||
content.assertFileContent(basedir.resolve(path));
|
||||
}
|
||||
|
||||
void create(Path basedir) {
|
||||
content.createFile(basedir.resolve(path));
|
||||
}
|
||||
}
|
||||
|
||||
public record TestCopySpec(List<PathGroupCopy> pathGroupSpecs, Collection<TestFile> src, Collection<TestFile> dst) {
|
||||
public TestCopySpec {
|
||||
pathGroupSpecs.forEach(Objects::requireNonNull);
|
||||
src.forEach(Objects::requireNonNull);
|
||||
dst.forEach(Objects::requireNonNull);
|
||||
}
|
||||
|
||||
PathGroup from() {
|
||||
return createPathGroup(PathGroupCopy::from);
|
||||
}
|
||||
|
||||
PathGroup to() {
|
||||
return createPathGroup(PathGroupCopy::to);
|
||||
}
|
||||
|
||||
void test(Path tempDir) throws IOException {
|
||||
final Path srcDir = tempDir.resolve("src");
|
||||
final Path dstDir = tempDir.resolve("dst");
|
||||
|
||||
src.stream().forEach(testFile -> {
|
||||
testFile.create(srcDir);
|
||||
});
|
||||
|
||||
from().resolveAt(srcDir).copy(to().resolveAt(dstDir));
|
||||
|
||||
dst.stream().forEach(testFile -> {
|
||||
testFile.assertFileContent(dstDir);
|
||||
});
|
||||
|
||||
Files.createDirectories(dstDir);
|
||||
final var actualFiles = Stream.of(walkFiles(dstDir)).filter(path -> {
|
||||
return Files.isRegularFile(dstDir.resolve(path));
|
||||
}).collect(toSet());
|
||||
final var expectedFiles = dst.stream().map(TestFile::path).collect(toSet());
|
||||
|
||||
assertEquals(expectedFiles, actualFiles);
|
||||
}
|
||||
|
||||
static Builder build() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
Builder addPath(String from, String to) {
|
||||
pathGroupSpecs.add(new PathGroupCopy(Path.of(from), Optional.ofNullable(to).map(Path::of).orElse(null)));
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder file(TestFileContent content, String srcPath, String ...dstPaths) {
|
||||
srcFiles.putAll(Map.of(Path.of(srcPath), content));
|
||||
dstFiles.putAll(Stream.of(dstPaths).collect(toMap(Path::of, x -> content)));
|
||||
return this;
|
||||
}
|
||||
|
||||
TestCopySpec create() {
|
||||
return new TestCopySpec(pathGroupSpecs, convert(srcFiles), convert(dstFiles));
|
||||
}
|
||||
|
||||
private static Collection<TestFile> convert(Map<Path, TestFileContent> map) {
|
||||
return map.entrySet().stream().map(e -> {
|
||||
return new TestFile(e.getKey(), e.getValue());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private final List<PathGroupCopy> pathGroupSpecs = new ArrayList<>();
|
||||
private final Map<Path, TestFileContent> srcFiles = new HashMap<>();
|
||||
private final Map<Path, TestFileContent> dstFiles = new HashMap<>();
|
||||
}
|
||||
|
||||
private PathGroup createPathGroup(Function<PathGroupCopy, Path> keyFunc) {
|
||||
return new PathGroup(IntStream.range(0, pathGroupSpecs.size()).mapToObj(Integer::valueOf).map(i -> {
|
||||
return Optional.ofNullable(keyFunc.apply(pathGroupSpecs.get(i))).map(path -> {
|
||||
return Map.entry(i, path);
|
||||
});
|
||||
}).filter(Optional::isPresent).map(Optional::orElseThrow).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void testCopy(TestCopySpec testSpec, @TempDir Path tempDir) throws IOException {
|
||||
testSpec.test(tempDir);
|
||||
}
|
||||
|
||||
private static Collection<TestCopySpec> testCopy() {
|
||||
return List.of(
|
||||
TestCopySpec.build().create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "a")
|
||||
.file(TestFileContent.A, "a/b/c/j/k/foo", "a/j/k/foo")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "a")
|
||||
.addPath("a/b/c", "d")
|
||||
.file(TestFileContent.A, "a/b/c/j/k/foo", "a/j/k/foo", "d/j/k/foo")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "")
|
||||
.addPath("a/b/c", "d")
|
||||
.file(TestFileContent.A, "a/b/c/j/k/foo", "j/k/foo", "d/j/k/foo")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "cc")
|
||||
.addPath("a/b/c", "dd")
|
||||
.addPath("a/b/c/foo", null)
|
||||
.file(TestFileContent.A, "a/b/c/foo")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "cc")
|
||||
.addPath("a/b/c", "dd")
|
||||
.addPath("a/b/c/foo", null)
|
||||
.file(TestFileContent.A, "a/b/c/foo")
|
||||
.file(TestFileContent.B, "a/b/c/bar", "cc/bar", "dd/bar")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "cc")
|
||||
.addPath("a/b/c", "dd")
|
||||
.addPath("a/b/c/bar", "dd/buz")
|
||||
.addPath("a/b/c/foo", null)
|
||||
.file(TestFileContent.A, "a/b/c/foo")
|
||||
.file(TestFileContent.B, "a/b/c/bar", "dd/buz")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b/c", "cc")
|
||||
.addPath("a/b/c", "dd")
|
||||
.addPath("a/b/c/bar", "dd/buz")
|
||||
.addPath("a/b/c/bar", "cc/rab")
|
||||
.addPath("a/b/c/foo", null)
|
||||
.file(TestFileContent.A, "a/b/c/foo")
|
||||
.file(TestFileContent.B, "a/b/c/bar", "cc/rab", "dd/buz")
|
||||
.create(),
|
||||
TestCopySpec.build()
|
||||
.addPath("a/b", null)
|
||||
.addPath("a/b/c", "cc")
|
||||
.addPath("a/b/c", "dd")
|
||||
.addPath("a/b/c/bar", "dd/buz")
|
||||
.addPath("a/b/c/bar", "cc/rab")
|
||||
.file(TestFileContent.A, "a/b/c/foo")
|
||||
.file(TestFileContent.B, "a/b/c/bar")
|
||||
.create()
|
||||
);
|
||||
}
|
||||
|
||||
private static Path[] walkFiles(Path root) throws IOException {
|
||||
try (var files = Files.walk(root)) {
|
||||
return files.map(root::relativize).sorted().toArray(Path[]::new);
|
||||
|
||||
@ -21,15 +21,18 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static jdk.jpackage.test.ApplicationLayout.linuxAppImage;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.jpackage.test.TKit;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.LinuxHelper;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
import jdk.jpackage.test.PackageType;
|
||||
import jdk.jpackage.test.LinuxHelper;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.TKit;
|
||||
|
||||
|
||||
/**
|
||||
@ -90,20 +93,13 @@ public class UsrTreeTest {
|
||||
"Check there is%spackage name [%s] in common path [%s] between [%s] and [%s]",
|
||||
expectedImageSplit ? " no " : " ", packageName,
|
||||
commonPath, launcherPath, launcherCfgPath));
|
||||
|
||||
List<Path> packageFiles = LinuxHelper.getPackageFiles(cmd).collect(
|
||||
Collectors.toList());
|
||||
|
||||
Consumer<Path> packageFileVerifier = file -> {
|
||||
TKit.assertTrue(packageFiles.stream().filter(
|
||||
path -> path.equals(file)).findFirst().orElse(
|
||||
null) != null, String.format(
|
||||
"Check file [%s] is in [%s] package", file,
|
||||
packageName));
|
||||
};
|
||||
|
||||
packageFileVerifier.accept(launcherPath);
|
||||
packageFileVerifier.accept(launcherCfgPath);
|
||||
})
|
||||
.addInstallVerifier(cmd -> {
|
||||
Stream.of(
|
||||
cmd.appLauncherPath(),
|
||||
cmd.appLauncherCfgPath(null),
|
||||
cmd.appLayout().libapplauncher()
|
||||
).map(cmd::pathToPackageFile).map(cmd.appInstallationDirectory()::relativize).forEachOrdered(cmd::assertFileInAppImage);
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user