8325089: jpackage utility creates an "infinite", undeleteable directory tree
Reviewed-by: almatvee
This commit is contained in:
parent
7c36fa7e17
commit
568b07a09b
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, 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
|
||||
@ -27,14 +27,20 @@ package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import static jdk.jpackage.internal.OverridableResource.createResource;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.ICON;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.SOURCE_DIR;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.APP_CONTENT;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.OUTPUT_DIR;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT;
|
||||
import jdk.jpackage.internal.resources.ResourceLocator;
|
||||
|
||||
/*
|
||||
@ -73,8 +79,21 @@ public abstract class AbstractAppImageBuilder {
|
||||
throws IOException {
|
||||
Path inputPath = SOURCE_DIR.fetchFrom(params);
|
||||
if (inputPath != null) {
|
||||
IOUtils.copyRecursive(SOURCE_DIR.fetchFrom(params),
|
||||
appLayout.appDirectory());
|
||||
inputPath = inputPath.toAbsolutePath();
|
||||
|
||||
List<Path> excludes = new ArrayList<>();
|
||||
|
||||
for (var path : List.of(TEMP_ROOT.fetchFrom(params), OUTPUT_DIR.fetchFrom(params), root)) {
|
||||
if (Files.isDirectory(path)) {
|
||||
path = path.toAbsolutePath();
|
||||
if (path.startsWith(inputPath) && !Files.isSameFile(path, inputPath)) {
|
||||
excludes.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IOUtils.copyRecursive(inputPath,
|
||||
appLayout.appDirectory().toAbsolutePath(), excludes);
|
||||
}
|
||||
|
||||
AppImageFile.save(root, params);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -89,9 +89,6 @@ public class Arguments {
|
||||
|
||||
private List<CLIOptions> allOptions = null;
|
||||
|
||||
private String input = null;
|
||||
private Path output = null;
|
||||
|
||||
private boolean hasMainJar = false;
|
||||
private boolean hasMainClass = false;
|
||||
private boolean hasMainModule = false;
|
||||
@ -135,9 +132,6 @@ public class Arguments {
|
||||
allOptions = new ArrayList<>();
|
||||
|
||||
addLaunchers = new ArrayList<>();
|
||||
|
||||
output = Paths.get("").toAbsolutePath();
|
||||
deployParams.setOutput(output);
|
||||
}
|
||||
|
||||
// CLIOptions is public for DeployParamsTest
|
||||
@ -147,13 +141,12 @@ public class Arguments {
|
||||
}),
|
||||
|
||||
INPUT ("input", "i", OptionCategories.PROPERTY, () -> {
|
||||
context().input = popArg();
|
||||
setOptionValue("input", context().input);
|
||||
setOptionValue("input", popArg());
|
||||
}),
|
||||
|
||||
OUTPUT ("dest", "d", OptionCategories.PROPERTY, () -> {
|
||||
context().output = Path.of(popArg());
|
||||
context().deployParams.setOutput(context().output);
|
||||
var path = Path.of(popArg());
|
||||
setOptionValue("dest", path);
|
||||
}),
|
||||
|
||||
DESCRIPTION ("description", OptionCategories.PROPERTY),
|
||||
@ -711,7 +704,8 @@ public class Arguments {
|
||||
Map<String, ? super Object> localParams = new HashMap<>(params);
|
||||
try {
|
||||
bundler.validate(localParams);
|
||||
Path result = bundler.execute(localParams, deployParams.outdir);
|
||||
Path result = bundler.execute(localParams,
|
||||
StandardBundlerParam.OUTPUT_DIR.fetchFrom(params));
|
||||
if (result == null) {
|
||||
throw new PackagerException("MSG_BundlerFailed",
|
||||
bundler.getID(), bundler.getName());
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 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
|
||||
@ -49,15 +49,9 @@ public class DeployParams {
|
||||
|
||||
String targetFormat = null; // means default type for this platform
|
||||
|
||||
Path outdir = null;
|
||||
|
||||
// raw arguments to the bundler
|
||||
Map<String, ? super Object> bundlerArguments = new LinkedHashMap<>();
|
||||
|
||||
public void setOutput(Path output) {
|
||||
outdir = output;
|
||||
}
|
||||
|
||||
static class Template {
|
||||
Path in;
|
||||
Path out;
|
||||
|
@ -121,29 +121,52 @@ public class IOUtils {
|
||||
}
|
||||
|
||||
public static void copyRecursive(Path src, Path dest,
|
||||
final List<String> excludes, CopyOption... options)
|
||||
final List<Path> excludes, CopyOption... options)
|
||||
throws IOException {
|
||||
|
||||
List<CopyAction> copyActions = new ArrayList<>();
|
||||
|
||||
Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(final Path dir,
|
||||
final BasicFileAttributes attrs) throws IOException {
|
||||
if (excludes.contains(dir.toFile().getName())) {
|
||||
final BasicFileAttributes attrs) {
|
||||
if (isPathMatch(dir, excludes)) {
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
} else {
|
||||
Files.createDirectories(dest.resolve(src.relativize(dir)));
|
||||
copyActions.add(new CopyAction(null, dest.resolve(src.
|
||||
relativize(dir))));
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(final Path file,
|
||||
final BasicFileAttributes attrs) throws IOException {
|
||||
if (!excludes.contains(file.toFile().getName())) {
|
||||
Files.copy(file, dest.resolve(src.relativize(file)), options);
|
||||
final BasicFileAttributes attrs) {
|
||||
if (!isPathMatch(file, excludes)) {
|
||||
copyActions.add(new CopyAction(file, dest.resolve(src.
|
||||
relativize(file))));
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
|
||||
for (var copyAction : copyActions) {
|
||||
copyAction.apply(options);
|
||||
}
|
||||
}
|
||||
|
||||
private static record CopyAction(Path src, Path dest) {
|
||||
void apply(CopyOption... options) throws IOException {
|
||||
if (src == null) {
|
||||
Files.createDirectories(dest);
|
||||
} else {
|
||||
Files.copy(src, dest, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPathMatch(Path what, List<Path> paths) {
|
||||
return paths.stream().anyMatch(what::endsWith);
|
||||
}
|
||||
|
||||
public static void copyFile(Path sourceFile, Path destFile)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2023, 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
|
||||
@ -43,7 +43,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@ -100,6 +99,14 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
|
||||
(s, p) -> Path.of(s)
|
||||
);
|
||||
|
||||
static final StandardBundlerParam<Path> OUTPUT_DIR =
|
||||
new StandardBundlerParam<>(
|
||||
Arguments.CLIOptions.OUTPUT.getId(),
|
||||
Path.class,
|
||||
p -> Path.of("").toAbsolutePath(),
|
||||
(s, p) -> Path.of(s)
|
||||
);
|
||||
|
||||
// note that each bundler is likely to replace this one with
|
||||
// their own converter
|
||||
static final StandardBundlerParam<Path> MAIN_JAR =
|
||||
@ -596,7 +603,7 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
|
||||
}
|
||||
|
||||
// copy whole runtime, need to skip jmods and src.zip
|
||||
final List<String> excludes = Arrays.asList("jmods", "src.zip");
|
||||
final List<Path> excludes = Arrays.asList(Path.of("jmods"), Path.of("src.zip"));
|
||||
IOUtils.copyRecursive(topImage, appLayout.runtimeHomeDirectory(),
|
||||
excludes, LinkOption.NOFOLLOW_LINKS);
|
||||
|
||||
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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.jpackage.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.test.Annotations.Parameters;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import static jdk.jpackage.test.DirectoryContentVerifierTest.AssertType.CONTAINS;
|
||||
import static jdk.jpackage.test.DirectoryContentVerifierTest.AssertType.MATCH;
|
||||
import jdk.jpackage.test.TKit.DirectoryContentVerifier;
|
||||
import static jdk.jpackage.test.TKit.assertAssert;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test TKit.DirectoryContentVerifier from jpackage test library
|
||||
* @library /test/jdk/tools/jpackage/helpers
|
||||
* @build jdk.jpackage.test.*
|
||||
* @modules jdk.jpackage/jdk.jpackage.internal
|
||||
* @compile DirectoryContentVerifierTest.java
|
||||
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-run=jdk.jpackage.test.DirectoryContentVerifierTest
|
||||
*/
|
||||
public class DirectoryContentVerifierTest {
|
||||
|
||||
enum AssertType {
|
||||
MATCH(DirectoryContentVerifier::match),
|
||||
CONTAINS(DirectoryContentVerifier::contains),
|
||||
;
|
||||
|
||||
AssertType(BiConsumer<DirectoryContentVerifier, Set<Path>> assertFunc) {
|
||||
this.assertFunc = assertFunc;
|
||||
}
|
||||
|
||||
private final BiConsumer<DirectoryContentVerifier, Set<Path>> assertFunc;
|
||||
}
|
||||
|
||||
private static ArgsBuilder buildArgs() {
|
||||
return new ArgsBuilder();
|
||||
}
|
||||
|
||||
private static class ArgsBuilder {
|
||||
|
||||
void applyTo(List<Object[]> data) {
|
||||
data.add(new Object[]{expectedPaths, actualPaths, assertOp, success});
|
||||
}
|
||||
|
||||
void applyVariantsTo(List<Object[]> data) {
|
||||
applyTo(data);
|
||||
boolean pathGroupsEqual = List.of(expectedPaths).equals(List.of(actualPaths));
|
||||
if (assertOp == MATCH) {
|
||||
if (!pathGroupsEqual) {
|
||||
data.add(new Object[]{actualPaths, expectedPaths, MATCH, success});
|
||||
}
|
||||
if (success) {
|
||||
data.add(new Object[]{expectedPaths, actualPaths, CONTAINS, success});
|
||||
if (!pathGroupsEqual) {
|
||||
data.add(new Object[]{actualPaths, expectedPaths, CONTAINS, success});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArgsBuilder expectedPaths(String... paths) {
|
||||
expectedPaths = paths;
|
||||
return this;
|
||||
}
|
||||
|
||||
ArgsBuilder actualPaths(String... paths) {
|
||||
actualPaths = paths;
|
||||
return this;
|
||||
}
|
||||
|
||||
ArgsBuilder assertOp(AssertType v) {
|
||||
assertOp = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
ArgsBuilder expectFail() {
|
||||
success = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
private String[] expectedPaths = new String[0];
|
||||
private String[] actualPaths = new String[0];
|
||||
private AssertType assertOp = MATCH;
|
||||
private boolean success = true;
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection input() {
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
buildArgs().applyVariantsTo(data);
|
||||
buildArgs().actualPaths("foo").assertOp(CONTAINS).applyTo(data);
|
||||
buildArgs().actualPaths("zoo").expectFail().applyVariantsTo(data);
|
||||
buildArgs().actualPaths("boo").expectedPaths("boo").applyVariantsTo(data);
|
||||
if (TKit.isWindows()) {
|
||||
buildArgs().actualPaths("moo").expectedPaths("Moo").applyVariantsTo(data);
|
||||
} else {
|
||||
buildArgs().actualPaths("moo").expectedPaths("Moo").expectFail().applyVariantsTo(data);
|
||||
}
|
||||
buildArgs().actualPaths("hello").expectedPaths().expectFail().applyVariantsTo(data);
|
||||
buildArgs().actualPaths("123").expectedPaths("456").expectFail().applyVariantsTo(data);
|
||||
buildArgs().actualPaths("a", "b", "c").expectedPaths("b", "a", "c").applyVariantsTo(data);
|
||||
buildArgs().actualPaths("AA", "BB", "CC").expectedPaths("BB", "AA").expectFail().applyVariantsTo(data);
|
||||
buildArgs().actualPaths("AA", "BB", "CC").expectedPaths("BB", "AA").assertOp(CONTAINS).applyTo(data);
|
||||
buildArgs().actualPaths("AA", "BB", "CC").expectedPaths("BB", "DD", "AA").expectFail().assertOp(CONTAINS).applyTo(data);
|
||||
buildArgs().actualPaths("AA", "BB", "CC").expectedPaths("BB", "DD", "AA").expectFail().applyTo(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public DirectoryContentVerifierTest(String[] expectedPaths, String[] actualPaths,
|
||||
AssertType assertOp, Boolean success) {
|
||||
this.expectedPaths = conv(expectedPaths);
|
||||
this.actualPaths = conv(actualPaths);
|
||||
this.assertOp = assertOp;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
TKit.withTempDirectory("basedir", this::test);
|
||||
}
|
||||
|
||||
private void test(Path basedir) throws IOException {
|
||||
for (var path : actualPaths) {
|
||||
Files.createFile(basedir.resolve(path));
|
||||
}
|
||||
|
||||
var testee = TKit.assertDirectoryContent(basedir);
|
||||
|
||||
assertAssert(success, () -> assertOp.assertFunc.accept(testee, expectedPaths));
|
||||
}
|
||||
|
||||
private static Set<Path> conv(String... paths) {
|
||||
return Stream.of(paths).map(Path::of).collect(toSet());
|
||||
}
|
||||
|
||||
private final Set<Path> expectedPaths;
|
||||
private final Set<Path> actualPaths;
|
||||
private final AssertType assertOp;
|
||||
private final boolean success;
|
||||
}
|
@ -34,30 +34,35 @@ public class CommandArguments<T> {
|
||||
args = new ArrayList<>();
|
||||
}
|
||||
|
||||
final public T addArgument(String v) {
|
||||
public final T clearArguments() {
|
||||
args.clear();
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public final T addArgument(String v) {
|
||||
args.add(v);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
final public T addArguments(List<String> v) {
|
||||
public final T addArguments(List<String> v) {
|
||||
args.addAll(v);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
final public T addArgument(Path v) {
|
||||
public final T addArgument(Path v) {
|
||||
return addArgument(v.toString());
|
||||
}
|
||||
|
||||
final public T addArguments(String... v) {
|
||||
public final T addArguments(String... v) {
|
||||
return addArguments(Arrays.asList(v));
|
||||
}
|
||||
|
||||
final public T addPathArguments(List<Path> v) {
|
||||
public final T addPathArguments(List<Path> v) {
|
||||
return addArguments(v.stream().map((p) -> p.toString()).collect(
|
||||
Collectors.toList()));
|
||||
}
|
||||
|
||||
final public List<String> getAllArguments() {
|
||||
public final List<String> getAllArguments() {
|
||||
return List.copyOf(args);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -52,6 +52,7 @@ import jdk.jpackage.internal.PackageFile;
|
||||
import static jdk.jpackage.test.AdditionalLauncher.forEachAdditionalLauncher;
|
||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||
import jdk.jpackage.test.Functional.ThrowingFunction;
|
||||
import jdk.jpackage.test.Functional.ThrowingRunnable;
|
||||
import jdk.jpackage.test.Functional.ThrowingSupplier;
|
||||
|
||||
/**
|
||||
@ -76,6 +77,8 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
immutable = cmd.immutable;
|
||||
prerequisiteActions = new Actions(cmd.prerequisiteActions);
|
||||
verifyActions = new Actions(cmd.verifyActions);
|
||||
appLayoutAsserts = cmd.appLayoutAsserts;
|
||||
executeInDirectory = cmd.executeInDirectory;
|
||||
}
|
||||
|
||||
JPackageCommand createImmutableCopy() {
|
||||
@ -198,7 +201,10 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
}
|
||||
|
||||
public Path outputDir() {
|
||||
return getArgumentValue("--dest", () -> Path.of("."), Path::of);
|
||||
var path = getArgumentValue("--dest", () -> Path.of("."), Path::of);
|
||||
return Optional.ofNullable(executeInDirectory).map(base -> {
|
||||
return base.resolve(path);
|
||||
}).orElse(path);
|
||||
}
|
||||
|
||||
public Path inputDir() {
|
||||
@ -691,6 +697,12 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JPackageCommand setDirectory(Path v) {
|
||||
verifyMutable();
|
||||
executeInDirectory = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JPackageCommand saveConsoleOutput(boolean v) {
|
||||
verifyMutable();
|
||||
saveConsoleOutput = v;
|
||||
@ -733,6 +745,7 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
private Executor createExecutor() {
|
||||
Executor exec = new Executor()
|
||||
.saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput)
|
||||
.setDirectory(executeInDirectory)
|
||||
.addArguments(args);
|
||||
|
||||
if (isWithToolProvider()) {
|
||||
@ -755,18 +768,19 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
executePrerequisiteActions();
|
||||
|
||||
if (hasArgument("--dest")) {
|
||||
if (isImagePackageType()) {
|
||||
TKit.deleteDirectoryContentsRecursive(outputDir());
|
||||
} else {
|
||||
nullableOutputBundle().ifPresent(path -> {
|
||||
if (ThrowingSupplier.toSupplier(() -> TKit.deleteIfExists(
|
||||
path)).get()) {
|
||||
nullableOutputBundle().ifPresent(path -> {
|
||||
ThrowingRunnable.toRunnable(() -> {
|
||||
if (Files.isDirectory(path)) {
|
||||
TKit.deleteDirectoryRecursive(path, String.format(
|
||||
"Delete [%s] folder before running jpackage",
|
||||
path));
|
||||
} else if (TKit.deleteIfExists(path)) {
|
||||
TKit.trace(String.format(
|
||||
"Deleted [%s] file before running jpackage",
|
||||
path));
|
||||
}
|
||||
});
|
||||
}
|
||||
}).run();
|
||||
});
|
||||
}
|
||||
|
||||
Path resourceDir = getArgumentValue("--resource-dir", () -> null, Path::of);
|
||||
@ -816,22 +830,69 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return this;
|
||||
}
|
||||
|
||||
JPackageCommand assertAppLayout() {
|
||||
assertAppImageFile();
|
||||
assertPackageFile();
|
||||
|
||||
TKit.assertDirectoryExists(appRuntimeDirectory());
|
||||
|
||||
if (!isRuntime()) {
|
||||
TKit.assertExecutableFileExists(appLauncherPath());
|
||||
TKit.assertFileExists(appLauncherCfgPath(null));
|
||||
|
||||
if (TKit.isOSX()) {
|
||||
TKit.assertFileExists(appRuntimeDirectory().resolve(
|
||||
"Contents/MacOS/libjli.dylib"));
|
||||
public static enum AppLayoutAssert {
|
||||
APP_IMAGE_FILE(JPackageCommand::assertAppImageFile),
|
||||
PACKAGE_FILE(JPackageCommand::assertPackageFile),
|
||||
MAIN_LAUNCHER(cmd -> {
|
||||
if (cmd.isRuntime()) {
|
||||
TKit.assertPathExists(convertFromRuntime(cmd).appLauncherPath(), false);
|
||||
} else {
|
||||
TKit.assertExecutableFileExists(cmd.appLauncherPath());
|
||||
}
|
||||
}),
|
||||
MAIN_LAUNCHER_CFG_FILE(cmd -> {
|
||||
if (cmd.isRuntime()) {
|
||||
TKit.assertPathExists(convertFromRuntime(cmd).appLauncherCfgPath(null), false);
|
||||
} else {
|
||||
TKit.assertFileExists(cmd.appLauncherCfgPath(null));
|
||||
}
|
||||
}),
|
||||
RUNTIME_DIRECTORY(cmd -> {
|
||||
TKit.assertDirectoryExists(cmd.appRuntimeDirectory());
|
||||
if (TKit.isOSX()) {
|
||||
var libjliPath = cmd.appRuntimeDirectory().resolve("Contents/MacOS/libjli.dylib");
|
||||
if (cmd.isRuntime()) {
|
||||
TKit.assertPathExists(libjliPath, false);
|
||||
} else {
|
||||
TKit.assertFileExists(libjliPath);
|
||||
}
|
||||
}
|
||||
}),
|
||||
MAC_BUNDLE_STRUCTURE(cmd -> {
|
||||
if (TKit.isOSX()) {
|
||||
MacHelper.verifyBundleStructure(cmd);
|
||||
}
|
||||
}),
|
||||
;
|
||||
|
||||
AppLayoutAssert(Consumer<JPackageCommand> action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
private static JPackageCommand convertFromRuntime(JPackageCommand cmd) {
|
||||
var copy = new JPackageCommand(cmd);
|
||||
copy.immutable = false;
|
||||
copy.removeArgumentWithValue("--runtime-image");
|
||||
return copy;
|
||||
}
|
||||
|
||||
private final Consumer<JPackageCommand> action;
|
||||
}
|
||||
|
||||
public JPackageCommand setAppLayoutAsserts(AppLayoutAssert ... asserts) {
|
||||
appLayoutAsserts = Set.of(asserts);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JPackageCommand excludeAppLayoutAsserts(AppLayoutAssert... asserts) {
|
||||
return setAppLayoutAsserts(Stream.of(asserts).filter(Predicate.not(
|
||||
appLayoutAsserts::contains)).toArray(AppLayoutAssert[]::new));
|
||||
}
|
||||
|
||||
JPackageCommand assertAppLayout() {
|
||||
for (var appLayoutAssert : appLayoutAsserts.stream().sorted().toList()) {
|
||||
appLayoutAssert.action.accept(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1111,9 +1172,11 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
private boolean immutable;
|
||||
private final Actions prerequisiteActions;
|
||||
private final Actions verifyActions;
|
||||
private Path executeInDirectory;
|
||||
private Set<AppLayoutAssert> appLayoutAsserts = Set.of(AppLayoutAssert.values());
|
||||
private static boolean defaultWithToolProvider;
|
||||
|
||||
private final static Map<String, PackageType> PACKAGE_TYPES = Functional.identity(
|
||||
private static final Map<String, PackageType> PACKAGE_TYPES = Functional.identity(
|
||||
() -> {
|
||||
Map<String, PackageType> reply = new HashMap<>();
|
||||
for (PackageType type : PackageType.values()) {
|
||||
@ -1122,7 +1185,7 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return reply;
|
||||
}).get();
|
||||
|
||||
public final static Path DEFAULT_RUNTIME_IMAGE = Functional.identity(() -> {
|
||||
public static final Path DEFAULT_RUNTIME_IMAGE = Functional.identity(() -> {
|
||||
// Set the property to the path of run-time image to speed up
|
||||
// building app images and platform bundles by avoiding running jlink
|
||||
// The value of the property will be automativcally appended to
|
||||
@ -1135,5 +1198,5 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return null;
|
||||
}).get();
|
||||
|
||||
private final static String UNPACKED_PATH_ARGNAME = "jpt-unpacked-folder";
|
||||
private static final String UNPACKED_PATH_ARGNAME = "jpt-unpacked-folder";
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -216,7 +216,9 @@ public final class JavaAppDesc {
|
||||
components[0].length() - 1);
|
||||
desc.setWithMainClass(true);
|
||||
}
|
||||
desc.setClassName(components[0]);
|
||||
if (!components[0].isEmpty()) {
|
||||
desc.setClassName(components[0]);
|
||||
}
|
||||
if (components.length == 2) {
|
||||
desc.setModuleVersion(components[1]);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -35,6 +35,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
@ -257,6 +258,20 @@ public final class MacHelper {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
static void verifyBundleStructure(JPackageCommand cmd) {
|
||||
Path bundleRoot;
|
||||
if (cmd.isImagePackageType()) {
|
||||
bundleRoot = cmd.outputBundle();
|
||||
} else {
|
||||
bundleRoot = cmd.pathToUnpackedPackageFile(
|
||||
cmd.appInstallationDirectory());
|
||||
}
|
||||
|
||||
TKit.assertDirectoryContent(bundleRoot).match(Path.of("Contents"));
|
||||
TKit.assertDirectoryContent(bundleRoot.resolve("Contents")).match(
|
||||
cmd.isRuntime() ? RUNTIME_BUNDLE_CONTENTS : APP_BUNDLE_CONTENTS);
|
||||
}
|
||||
|
||||
static String getBundleName(JPackageCommand cmd) {
|
||||
cmd.verifyIsOfType(PackageType.MAC);
|
||||
return String.format("%s-%s%s", getPackageName(cmd), cmd.version(),
|
||||
@ -390,5 +405,19 @@ public final class MacHelper {
|
||||
static final Set<Path> CRITICAL_RUNTIME_FILES = Set.of(Path.of(
|
||||
"Contents/Home/lib/server/libjvm.dylib"));
|
||||
|
||||
private final static Method getServicePListFileName = initGetServicePListFileName();
|
||||
private static final Method getServicePListFileName = initGetServicePListFileName();
|
||||
|
||||
private static final Set<Path> APP_BUNDLE_CONTENTS = Stream.of(
|
||||
"Info.plist",
|
||||
"MacOS",
|
||||
"app",
|
||||
"runtime",
|
||||
"Resources",
|
||||
"PkgInfo",
|
||||
"_CodeSignature"
|
||||
).map(Path::of).collect(toSet());
|
||||
|
||||
private static final Set<Path> RUNTIME_BUNDLE_CONTENTS = Stream.of(
|
||||
"Home"
|
||||
).map(Path::of).collect(toSet());
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -127,6 +127,15 @@ public final class PackageTest extends RunnablePackageTest {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PackageTest ignoreBundleOutputDir() {
|
||||
return ignoreBundleOutputDir(true);
|
||||
}
|
||||
|
||||
public PackageTest ignoreBundleOutputDir(boolean v) {
|
||||
ignoreBundleOutputDir = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v,
|
||||
String id) {
|
||||
if (id != null) {
|
||||
@ -368,7 +377,7 @@ public final class PackageTest extends RunnablePackageTest {
|
||||
private final List<Consumer<Action>> handlers;
|
||||
}
|
||||
|
||||
final static class PackageHandlers {
|
||||
static final class PackageHandlers {
|
||||
Consumer<JPackageCommand> installHandler;
|
||||
Consumer<JPackageCommand> uninstallHandler;
|
||||
BiFunction<JPackageCommand, Path, Path> unpackHandler;
|
||||
@ -528,7 +537,7 @@ public final class PackageTest extends RunnablePackageTest {
|
||||
private final JPackageCommand cmd = Functional.identity(() -> {
|
||||
JPackageCommand result = new JPackageCommand();
|
||||
result.setDefaultInputOutput().setDefaultAppName();
|
||||
if (BUNDLE_OUTPUT_DIR != null) {
|
||||
if (BUNDLE_OUTPUT_DIR != null && !ignoreBundleOutputDir) {
|
||||
result.setArgumentValue("--dest", BUNDLE_OUTPUT_DIR.toString());
|
||||
}
|
||||
type.applyTo(result);
|
||||
@ -777,8 +786,9 @@ public final class PackageTest extends RunnablePackageTest {
|
||||
private Map<PackageType, Handler> handlers;
|
||||
private Set<String> namedInitializers;
|
||||
private Map<PackageType, PackageHandlers> packageHandlers;
|
||||
private boolean ignoreBundleOutputDir;
|
||||
|
||||
private final static File BUNDLE_OUTPUT_DIR;
|
||||
private static final File BUNDLE_OUTPUT_DIR;
|
||||
|
||||
static {
|
||||
final String propertyName = "output";
|
||||
|
@ -40,10 +40,12 @@ import java.nio.file.WatchKey;
|
||||
import java.nio.file.WatchService;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -57,13 +59,14 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.test.Functional.ExceptionBox;
|
||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||
import jdk.jpackage.test.Functional.ThrowingRunnable;
|
||||
import jdk.jpackage.test.Functional.ThrowingSupplier;
|
||||
|
||||
final public class TKit {
|
||||
public final class TKit {
|
||||
|
||||
private static final String OS = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
@ -84,7 +87,7 @@ final public class TKit {
|
||||
return TEST_SRC_ROOT.resolve("../../../../src/jdk.jpackage").normalize().toAbsolutePath();
|
||||
}).get();
|
||||
|
||||
public final static String ICON_SUFFIX = Functional.identity(() -> {
|
||||
public static final String ICON_SUFFIX = Functional.identity(() -> {
|
||||
if (isOSX()) {
|
||||
return ".icns";
|
||||
}
|
||||
@ -273,7 +276,23 @@ final public class TKit {
|
||||
throw new AssertionError(v);
|
||||
}
|
||||
|
||||
private final static String TEMP_FILE_PREFIX = null;
|
||||
static void assertAssert(boolean expectedSuccess, Runnable runnable) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (AssertionError err) {
|
||||
if (expectedSuccess) {
|
||||
assertUnexpected("Assertion failed");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!expectedSuccess) {
|
||||
assertUnexpected("Assertion passed");
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TEMP_FILE_PREFIX = null;
|
||||
|
||||
private static Path createUniqueFileName(String defaultName) {
|
||||
final String[] nameComponents;
|
||||
@ -740,6 +759,101 @@ final public class TKit {
|
||||
error(concatMessages("Unexpected", msg));
|
||||
}
|
||||
|
||||
public static DirectoryContentVerifier assertDirectoryContent(Path dir) {
|
||||
return new DirectoryContentVerifier(dir);
|
||||
}
|
||||
|
||||
public static final class DirectoryContentVerifier {
|
||||
public DirectoryContentVerifier(Path baseDir) {
|
||||
this(baseDir, ThrowingSupplier.toSupplier(() -> {
|
||||
try (var files = Files.list(baseDir)) {
|
||||
return files.map(Path::getFileName).collect(toSet());
|
||||
}
|
||||
}).get());
|
||||
}
|
||||
|
||||
public void match(Path ... expected) {
|
||||
DirectoryContentVerifier.this.match(Set.of(expected));
|
||||
}
|
||||
|
||||
public void match(Set<Path> expected) {
|
||||
currentTest.notifyAssert();
|
||||
|
||||
var comm = Comm.compare(content, expected);
|
||||
if (!comm.unique1.isEmpty() && !comm.unique2.isEmpty()) {
|
||||
error(String.format(
|
||||
"assertDirectoryContentEquals(%s): Some expected %s. Unexpected %s. Missing %s",
|
||||
baseDir, format(comm.common), format(comm.unique1), format(comm.unique2)));
|
||||
} else if (!comm.unique1.isEmpty()) {
|
||||
error(String.format(
|
||||
"assertDirectoryContentEquals%s: Expected %s. Unexpected %s",
|
||||
baseDir, format(comm.common), format(comm.unique1)));
|
||||
} else if (!comm.unique2.isEmpty()) {
|
||||
error(String.format(
|
||||
"assertDirectoryContentEquals(%s): Some expected %s. Missing %s",
|
||||
baseDir, format(comm.common), format(comm.unique2)));
|
||||
} else {
|
||||
traceAssert(String.format(
|
||||
"assertDirectoryContentEquals(%s): Expected %s",
|
||||
baseDir, format(expected)));
|
||||
}
|
||||
}
|
||||
|
||||
public void contains(Path ... expected) {
|
||||
contains(Set.of(expected));
|
||||
}
|
||||
|
||||
public void contains(Set<Path> expected) {
|
||||
currentTest.notifyAssert();
|
||||
|
||||
var comm = Comm.compare(content, expected);
|
||||
if (!comm.unique2.isEmpty()) {
|
||||
error(String.format(
|
||||
"assertDirectoryContentContains(%s): Some expected %s. Missing %s",
|
||||
baseDir, format(comm.common), format(comm.unique2)));
|
||||
} else {
|
||||
traceAssert(String.format(
|
||||
"assertDirectoryContentContains(%s): Expected %s",
|
||||
baseDir, format(expected)));
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryContentVerifier removeAll(Path ... paths) {
|
||||
Set<Path> newContent = new HashSet<>(content);
|
||||
newContent.removeAll(List.of(paths));
|
||||
return new DirectoryContentVerifier(baseDir, newContent);
|
||||
}
|
||||
|
||||
private DirectoryContentVerifier(Path baseDir, Set<Path> contents) {
|
||||
this.baseDir = baseDir;
|
||||
this.content = contents;
|
||||
}
|
||||
|
||||
private static record Comm(Set<Path> common, Set<Path> unique1, Set<Path> unique2) {
|
||||
static Comm compare(Set<Path> a, Set<Path> b) {
|
||||
Set<Path> common = new HashSet<>(a);
|
||||
common.retainAll(b);
|
||||
|
||||
Set<Path> unique1 = new HashSet<>(a);
|
||||
unique1.removeAll(common);
|
||||
|
||||
Set<Path> unique2 = new HashSet<>(b);
|
||||
unique2.removeAll(common);
|
||||
|
||||
return new Comm(common, unique1, unique2);
|
||||
}
|
||||
}
|
||||
|
||||
private static String format(Set<Path> paths) {
|
||||
return Arrays.toString(
|
||||
paths.stream().sorted().map(Path::toString).toArray(
|
||||
String[]::new));
|
||||
}
|
||||
|
||||
private final Path baseDir;
|
||||
private final Set<Path> content;
|
||||
}
|
||||
|
||||
public static void assertStringListEquals(List<String> expected,
|
||||
List<String> actual, String msg) {
|
||||
currentTest.notifyAssert();
|
||||
@ -817,7 +931,7 @@ final public class TKit {
|
||||
};
|
||||
}
|
||||
|
||||
public final static class TextStreamVerifier {
|
||||
public static final class TextStreamVerifier {
|
||||
TextStreamVerifier(String value) {
|
||||
this.value = value;
|
||||
predicate(String::contains);
|
||||
@ -880,7 +994,7 @@ final public class TKit {
|
||||
private String label;
|
||||
private boolean negate;
|
||||
private Supplier<RuntimeException> createException;
|
||||
final private String value;
|
||||
private final String value;
|
||||
}
|
||||
|
||||
public static TextStreamVerifier assertTextStream(String what) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -22,12 +22,9 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.io.IOException;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
@ -40,11 +37,6 @@ public class DeployParamsTest {
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
testRoot = tempFolder.newFolder().toPath();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidAppName() throws PackagerException {
|
||||
initParamsAppName();
|
||||
@ -115,7 +107,6 @@ public class DeployParamsTest {
|
||||
private void initParamsAppName() {
|
||||
params = new DeployParams();
|
||||
|
||||
params.setOutput(testRoot);
|
||||
params.addBundleArgument(Arguments.CLIOptions.APPCLASS.getId(),
|
||||
"TestClass");
|
||||
params.addBundleArgument(Arguments.CLIOptions.MAIN_JAR.getId(),
|
||||
@ -128,6 +119,5 @@ public class DeployParamsTest {
|
||||
params.validate();
|
||||
}
|
||||
|
||||
private Path testRoot = null;
|
||||
private DeployParams params;
|
||||
}
|
||||
|
266
test/jdk/tools/jpackage/share/InOutPathTest.java
Normal file
266
test/jdk/tools/jpackage/share/InOutPathTest.java
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.AppImageFile;
|
||||
import jdk.jpackage.internal.ApplicationLayout;
|
||||
import jdk.jpackage.internal.PackageFile;
|
||||
import jdk.jpackage.test.Annotations;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.JPackageCommand.AppLayoutAssert;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
import jdk.jpackage.test.PackageType;
|
||||
import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK;
|
||||
import jdk.jpackage.test.TKit;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test jpackage command line with overlapping input and output paths
|
||||
* @library ../helpers
|
||||
* @build jdk.jpackage.test.*
|
||||
* @modules jdk.jpackage/jdk.jpackage.internal
|
||||
* @compile InOutPathTest.java
|
||||
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-run=InOutPathTest
|
||||
*/
|
||||
public final class InOutPathTest {
|
||||
|
||||
@Annotations.Parameters
|
||||
public static Collection input() {
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
for (var packageTypes : List.of(PackageType.IMAGE.toString(), ALL_NATIVE_PACKAGE_TYPES)) {
|
||||
data.addAll(List.of(new Object[][]{
|
||||
{packageTypes, wrap(InOutPathTest::outputDirInInputDir, "--dest in --input")},
|
||||
{packageTypes, wrap(InOutPathTest::outputDirSameAsInputDir, "--dest same as --input")},
|
||||
{packageTypes, wrap(InOutPathTest::tempDirInInputDir, "--temp in --input")},
|
||||
{packageTypes, wrap(cmd -> {
|
||||
outputDirInInputDir(cmd);
|
||||
tempDirInInputDir(cmd);
|
||||
}, "--dest and --temp in --input")},
|
||||
}));
|
||||
data.addAll(additionalContentInput(packageTypes, "--app-content"));
|
||||
}
|
||||
|
||||
data.addAll(List.of(new Object[][]{
|
||||
{PackageType.IMAGE.toString(), wrap(cmd -> {
|
||||
additionalContent(cmd, "--app-content", cmd.outputBundle());
|
||||
}, "--app-content same as output bundle")},
|
||||
}));
|
||||
|
||||
if (TKit.isOSX()) {
|
||||
data.addAll(additionalContentInput(PackageType.MAC_DMG.toString(),
|
||||
"--mac-dmg-content"));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static List<Object[]> additionalContentInput(String packageTypes, String argName) {
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
data.addAll(List.of(new Object[][]{
|
||||
{packageTypes, wrap(cmd -> {
|
||||
additionalContent(cmd, argName, cmd.inputDir());
|
||||
}, argName + " same as --input")},
|
||||
}));
|
||||
|
||||
if (!TKit.isOSX()) {
|
||||
data.addAll(List.of(new Object[][]{
|
||||
{packageTypes, wrap(cmd -> {
|
||||
additionalContent(cmd, argName, cmd.inputDir().resolve("foo"));
|
||||
}, argName + " in --input")},
|
||||
{packageTypes, wrap(cmd -> {
|
||||
additionalContent(cmd, argName, cmd.outputDir().resolve("bar"));
|
||||
}, argName + " in --dest")},
|
||||
{packageTypes, wrap(cmd -> {
|
||||
additionalContent(cmd, argName, cmd.outputDir());
|
||||
}, argName + " same as --dest")},
|
||||
{packageTypes, wrap(cmd -> {
|
||||
tempDirInInputDir(cmd);
|
||||
var tempDir = cmd.getArgumentValue("--temp");
|
||||
Files.createDirectories(Path.of(tempDir));
|
||||
cmd.addArguments(argName, tempDir);
|
||||
}, argName + " as --temp; --temp in --input")},
|
||||
}));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public InOutPathTest(String packageTypes, Envelope configure) {
|
||||
if (ALL_NATIVE_PACKAGE_TYPES.equals(packageTypes)) {
|
||||
this.packageTypes = PackageType.NATIVE;
|
||||
} else {
|
||||
this.packageTypes = Stream.of(packageTypes.split(",")).map(
|
||||
PackageType::valueOf).collect(toSet());
|
||||
}
|
||||
this.configure = configure.value;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Throwable {
|
||||
runTest(packageTypes, configure);
|
||||
}
|
||||
|
||||
private static Envelope wrap(ThrowingConsumer<JPackageCommand> v, String label) {
|
||||
return new Envelope(v, label);
|
||||
}
|
||||
|
||||
private static boolean isAppImageValid(JPackageCommand cmd) {
|
||||
return !cmd.hasArgument("--app-content") && !cmd.hasArgument("--mac-dmg-content");
|
||||
}
|
||||
|
||||
private static void runTest(Set<PackageType> packageTypes,
|
||||
ThrowingConsumer<JPackageCommand> configure) throws Throwable {
|
||||
ThrowingConsumer<JPackageCommand> configureWrapper = cmd -> {
|
||||
// Make sure the input directory is empty in every test run.
|
||||
// This is needed because jpackage output directories in this test
|
||||
// are subdirectories of the input directory.
|
||||
cmd.setInputToEmptyDirectory();
|
||||
configure.accept(cmd);
|
||||
if (cmd.hasArgument("--temp") && cmd.isImagePackageType()) {
|
||||
// Request to build app image wit user supplied temp directory,
|
||||
// ignore external runtime if any to make use of the temp directory
|
||||
// for runtime generation.
|
||||
cmd.ignoreDefaultRuntime(true);
|
||||
} else {
|
||||
cmd.setFakeRuntime();
|
||||
}
|
||||
|
||||
if (!isAppImageValid(cmd)) {
|
||||
// Standard asserts for .jpackage.xml fail in messed up app image. Disable them.
|
||||
// Other standard asserts for app image contents should pass.
|
||||
cmd.excludeAppLayoutAsserts(AppLayoutAssert.APP_IMAGE_FILE);
|
||||
}
|
||||
};
|
||||
|
||||
if (packageTypes.contains(PackageType.IMAGE)) {
|
||||
JPackageCommand cmd = JPackageCommand.helloAppImage(JAR_PATH.toString() + ":");
|
||||
configureWrapper.accept(cmd);
|
||||
cmd.executeAndAssertHelloAppImageCreated();
|
||||
if (isAppImageValid(cmd)) {
|
||||
verifyAppImage(cmd);
|
||||
}
|
||||
} else {
|
||||
new PackageTest()
|
||||
.forTypes(packageTypes)
|
||||
.configureHelloApp(JAR_PATH.toString() + ":")
|
||||
.addInitializer(configureWrapper)
|
||||
.addInstallVerifier(InOutPathTest::verifyAppImage)
|
||||
.run(CREATE_AND_UNPACK);
|
||||
}
|
||||
}
|
||||
|
||||
private static void outputDirInInputDir(JPackageCommand cmd) throws
|
||||
IOException {
|
||||
// Set output dir as a subdir of input dir
|
||||
Path outputDir = cmd.inputDir().resolve("out");
|
||||
TKit.createDirectories(outputDir);
|
||||
cmd.setArgumentValue("--dest", outputDir);
|
||||
}
|
||||
|
||||
private static void outputDirSameAsInputDir(JPackageCommand cmd) throws
|
||||
IOException {
|
||||
// Set output dir the same as the input dir
|
||||
cmd.setArgumentValue("--dest", cmd.inputDir());
|
||||
}
|
||||
|
||||
private static void tempDirInInputDir(JPackageCommand cmd) {
|
||||
// Set temp dir as a subdir of input dir
|
||||
Path tmpDir = cmd.inputDir().resolve("tmp");
|
||||
cmd.setArgumentValue("--temp", tmpDir);
|
||||
}
|
||||
|
||||
private static void additionalContent(JPackageCommand cmd,
|
||||
String argName, Path base) throws IOException {
|
||||
Path appContentFile = base.resolve(base.toString().replaceAll("[\\\\/]",
|
||||
"-") + "-foo.txt");
|
||||
TKit.createDirectories(appContentFile.getParent());
|
||||
TKit.createTextFile(appContentFile, List.of("Hello Duke!"));
|
||||
cmd.addArguments(argName, appContentFile.getParent());
|
||||
}
|
||||
|
||||
private static void verifyAppImage(JPackageCommand cmd) throws IOException {
|
||||
if (!isAppImageValid(cmd)) {
|
||||
// Don't verify the contents of app image as it is invalid.
|
||||
// jpackage exited without getting stuck in infinite spiral.
|
||||
// No more expectations from the tool for the give arguments.
|
||||
return;
|
||||
}
|
||||
|
||||
final Path rootDir = cmd.isImagePackageType() ? cmd.outputBundle() : cmd.pathToUnpackedPackageFile(
|
||||
cmd.appInstallationDirectory());
|
||||
final Path appDir = ApplicationLayout.platformAppImage().resolveAt(
|
||||
rootDir).appDirectory();
|
||||
|
||||
final var knownFiles = Set.of(
|
||||
JAR_PATH.getName(0).toString(),
|
||||
PackageFile.getPathInAppImage(Path.of("")).getFileName().toString(),
|
||||
AppImageFile.getPathInAppImage(Path.of("")).getFileName().toString(),
|
||||
cmd.name() + ".cfg"
|
||||
);
|
||||
|
||||
TKit.assertFileExists(appDir.resolve(JAR_PATH));
|
||||
|
||||
try (Stream<Path> actualFilesStream = Files.list(appDir)) {
|
||||
var unexpectedFiles = actualFilesStream.map(path -> {
|
||||
return path.getFileName().toString();
|
||||
}).filter(Predicate.not(knownFiles::contains)).toList();
|
||||
TKit.assertStringListEquals(List.of(), unexpectedFiles,
|
||||
"Check there are no unexpected files in `app` folder");
|
||||
}
|
||||
}
|
||||
|
||||
private static final record Envelope(ThrowingConsumer<JPackageCommand> value, String label) {
|
||||
@Override
|
||||
public String toString() {
|
||||
// Will produce the same test description for the same label every
|
||||
// time it's executed.
|
||||
// The test runner will keep the same test output directory.
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private final Set<PackageType> packageTypes;
|
||||
private final ThrowingConsumer<JPackageCommand> configure;
|
||||
|
||||
// Placing jar file in the "Resources" subdir of the input directory would allow
|
||||
// to use the input directory with `--app-content` on OSX.
|
||||
// For other platforms it doesn't matter. Keep it the same across
|
||||
// all platforms for simplicity.
|
||||
private static final Path JAR_PATH = Path.of("Resources/duke.jar");
|
||||
|
||||
private static final String ALL_NATIVE_PACKAGE_TYPES = "NATIVE";
|
||||
}
|
@ -28,6 +28,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
@ -41,6 +42,8 @@ import jdk.jpackage.test.Executor;
|
||||
import jdk.jpackage.test.JavaTool;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||
import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK;
|
||||
|
||||
import static jdk.jpackage.test.WindowsHelper.getTempDirectory;
|
||||
|
||||
@ -249,6 +252,63 @@ public final class BasicTest {
|
||||
.executeAndAssertHelloAppImageCreated();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameter("true")
|
||||
@Parameter("false")
|
||||
public void testNoOutputDir(boolean appImage) throws Throwable {
|
||||
var cmd = JPackageCommand.helloAppImage();
|
||||
|
||||
final var execDir = cmd.outputDir();
|
||||
|
||||
final ThrowingConsumer<JPackageCommand> initializer = cmdNoOutputDir -> {
|
||||
cmd.executePrerequisiteActions();
|
||||
|
||||
final var pkgType = cmdNoOutputDir.packageType();
|
||||
|
||||
cmdNoOutputDir
|
||||
.clearArguments()
|
||||
.addArguments(cmd.getAllArguments())
|
||||
// Restore the value of `--type` parameter.
|
||||
.setPackageType(pkgType)
|
||||
.removeArgumentWithValue("--dest")
|
||||
.setArgumentValue("--input", execDir.relativize(cmd.inputDir()))
|
||||
.setDirectory(execDir)
|
||||
// Force to use jpackage as executable because we need to
|
||||
// change the current directory.
|
||||
.useToolProvider(false);
|
||||
|
||||
Optional.ofNullable(cmdNoOutputDir.getArgumentValue("--runtime-image",
|
||||
() -> null, Path::of)).ifPresent(runtimePath -> {
|
||||
if (!runtimePath.isAbsolute()) {
|
||||
cmdNoOutputDir.setArgumentValue("--runtime-image",
|
||||
execDir.relativize(runtimePath));
|
||||
}
|
||||
});
|
||||
|
||||
// JPackageCommand.execute() will not do the cleanup if `--dest` parameter
|
||||
// is not specified, do it manually.
|
||||
TKit.createDirectories(execDir);
|
||||
TKit.deleteDirectoryContentsRecursive(execDir);
|
||||
};
|
||||
|
||||
if (appImage) {
|
||||
var cmdNoOutputDir = new JPackageCommand()
|
||||
.setPackageType(cmd.packageType());
|
||||
initializer.accept(cmdNoOutputDir);
|
||||
cmdNoOutputDir.executeAndAssertHelloAppImageCreated();
|
||||
} else {
|
||||
// Save time by packing non-functional runtime.
|
||||
// Build the runtime in app image only. This is sufficient coverage.
|
||||
cmd.setFakeRuntime();
|
||||
new PackageTest()
|
||||
.addInitializer(initializer)
|
||||
.addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput)
|
||||
// Prevent adding `--dest` parameter to jpackage command line.
|
||||
.ignoreBundleOutputDir()
|
||||
.run(CREATE_AND_UNPACK);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameter("ALL-MODULE-PATH")
|
||||
@Parameter("ALL-DEFAULT")
|
||||
|
Loading…
x
Reference in New Issue
Block a user