Skip to content

Commit

Permalink
8325089: jpackage utility creates an "infinite", undeleteable directo…
Browse files Browse the repository at this point in the history
…ry tree

Reviewed-by: almatvee
  • Loading branch information
Alexey Semenyuk committed Oct 31, 2024
1 parent 7c36fa7 commit 568b07a
Show file tree
Hide file tree
Showing 15 changed files with 834 additions and 87 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -27,14 +27,20 @@

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;

/*
Expand Down Expand Up @@ -73,8 +79,21 @@ protected void copyApplication(Map<String, ? super Object> params)
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -135,9 +132,6 @@ public Arguments(String[] args) {
allOptions = new ArrayList<>();

addLaunchers = new ArrayList<>();

output = Paths.get("").toAbsolutePath();
deployParams.setOutput(output);
}

// CLIOptions is public for DeployParamsTest
Expand All @@ -147,13 +141,12 @@ public enum CLIOptions {
}),

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),
Expand Down Expand Up @@ -711,7 +704,8 @@ private void generateBundle(Map<String,? super Object> params)
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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down
37 changes: 30 additions & 7 deletions src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,29 +121,52 @@ public static void copyRecursive(Path src, Path dest, CopyOption... options)
}

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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -43,7 +43,6 @@
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -596,7 +603,7 @@ static void copyPredefinedRuntimeImage(Map<String, ? super Object> params,
}

// 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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 568b07a

Please sign in to comment.