mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8375240: Make bundling progress messages issued by jpackage consistent across platforms
Reviewed-by: almatvee
This commit is contained in:
parent
5da70b1804
commit
b082a390b7
@ -147,8 +147,6 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
|
||||
Path debFile = outputPackageFile();
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-to-location", debFile.toAbsolutePath()));
|
||||
|
||||
List<String> cmdline = new ArrayList<>();
|
||||
Stream.of(sysEnv.fakeroot(), sysEnv.dpkgdeb()).map(Path::toString).forEach(cmdline::add);
|
||||
if (Log.isVerbose()) {
|
||||
@ -159,8 +157,6 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
// run dpkg
|
||||
Executor.of(cmdline).retryOnKnownErrorMessage(
|
||||
"semop(1): encountered an error: Invalid argument").execute();
|
||||
|
||||
Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -133,8 +133,6 @@ final class LinuxRpmPackager extends LinuxPackager<LinuxRpmPackage> {
|
||||
|
||||
Path rpmFile = outputPackageFile();
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-bundle-location", rpmFile.getParent()));
|
||||
|
||||
//run rpmbuild
|
||||
Executor.of(sysEnv.rpmbuild().toString(),
|
||||
"-bb", specFile().toAbsolutePath().toString(),
|
||||
@ -147,8 +145,6 @@ final class LinuxRpmPackager extends LinuxPackager<LinuxRpmPackage> {
|
||||
env.buildRoot().toAbsolutePath()),
|
||||
"--define", String.format("%%_rpmfilename %s", rpmFile.getFileName())
|
||||
).executeExpectSuccess();
|
||||
|
||||
Log.verbose(I18N.format("message.output-bundle-location", rpmFile.getParent()));
|
||||
}
|
||||
|
||||
private Path installPrefix() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2017, 2026, 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,11 +49,7 @@ error.rpm-arch-not-detected="Failed to detect RPM arch"
|
||||
|
||||
message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place.
|
||||
message.test-for-tool=Test for [{0}]. Result: {1}
|
||||
message.outputting-to-location=Generating DEB for installer to: {0}.
|
||||
message.output-to-location=Package (.deb) saved to: {0}.
|
||||
message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application.
|
||||
message.outputting-bundle-location=Generating RPM for installer to: {0}.
|
||||
message.output-bundle-location=Package (.rpm) saved to: {0}.
|
||||
|
||||
message.ldd-not-available=ldd command not found. Package dependencies will not be generated.
|
||||
message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd.
|
||||
|
||||
@ -54,7 +54,6 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
buildEnv()::create,
|
||||
MacBundlingEnvironment::buildPipeline,
|
||||
(env, pkg, outputDir) -> {
|
||||
Log.verbose(I18N.format("message.building-dmg", pkg.app().name()));
|
||||
return new MacDmgPackager(env, pkg, outputDir, sysEnv);
|
||||
});
|
||||
}
|
||||
@ -64,10 +63,7 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
MacFromOptions.createMacPkgPackage(options),
|
||||
buildEnv()::create,
|
||||
MacBundlingEnvironment::buildPipeline,
|
||||
(env, pkg, outputDir) -> {
|
||||
Log.verbose(I18N.format("message.building-pkg", pkg.app().name()));
|
||||
return new MacPkgPackager(env, pkg, outputDir);
|
||||
});
|
||||
MacPkgPackager::new);
|
||||
}
|
||||
|
||||
private static void signAppImage(Options options) {
|
||||
|
||||
@ -235,17 +235,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
|
||||
final Path srcFolder = env.appImageDir();
|
||||
|
||||
Log.verbose(MessageFormat.format(I18N.getString(
|
||||
"message.creating-dmg-file"), finalDMG.toAbsolutePath()));
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(finalDMG);
|
||||
} catch (IOException ex) {
|
||||
throw new IOException(MessageFormat.format(I18N.getString(
|
||||
"message.dmg-cannot-be-overwritten"),
|
||||
finalDMG.toAbsolutePath()));
|
||||
}
|
||||
|
||||
Files.createDirectories(protoDMG.getParent());
|
||||
Files.createDirectories(finalDMG.getParent());
|
||||
|
||||
@ -383,11 +372,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
} catch (IOException ex) {
|
||||
// Don't care if fails
|
||||
}
|
||||
|
||||
Log.verbose(MessageFormat.format(I18N.getString(
|
||||
"message.output-to-location"),
|
||||
pkg.app().name(), normalizedAbsolutePathString(finalDMG)));
|
||||
|
||||
}
|
||||
|
||||
private void detachVolume() throws IOException {
|
||||
|
||||
@ -217,6 +217,11 @@ final class MacPackagingPipeline {
|
||||
|
||||
enum SignAppImagePackageType implements PackageType {
|
||||
VALUE;
|
||||
|
||||
@Override
|
||||
public String label() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
static Package createSignAppImagePackage(MacApplication app, BuildEnv env) {
|
||||
|
||||
@ -57,12 +57,7 @@ message.version-string-first-number-not-zero=The first number in an app-version
|
||||
message.keychain.error=Unable to get keychain list.
|
||||
message.invalid-identifier=Invalid mac bundle identifier [{0}].
|
||||
message.invalid-identifier.advice=specify identifier with "--mac-package-identifier".
|
||||
message.building-dmg=Building DMG package for {0}.
|
||||
message.preparing-dmg-setup=Preparing dmg setup: {0}.
|
||||
message.creating-dmg-file=Creating DMG file: {0}.
|
||||
message.dmg-cannot-be-overwritten=Dmg file exists [{0}] and can not be removed.
|
||||
message.output-to-location=Result DMG installer for {0}: {1}.
|
||||
message.building-pkg=Building PKG package for {0}.
|
||||
message.preparing-scripts=Preparing package scripts.
|
||||
message.preparing-distribution-dist=Preparing distribution.dist: {0}.
|
||||
message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
|
||||
|
||||
@ -42,17 +42,15 @@ import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.cli.CliBundlingEnvironment;
|
||||
import jdk.jpackage.internal.cli.Options;
|
||||
import jdk.jpackage.internal.cli.StandardBundlingOperation;
|
||||
import jdk.jpackage.internal.model.AppImagePackageType;
|
||||
import jdk.jpackage.internal.model.Application;
|
||||
import jdk.jpackage.internal.model.BundlingOperationDescriptor;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
@ -134,7 +132,9 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
Objects.requireNonNull(app);
|
||||
Objects.requireNonNull(pipelineBuilder);
|
||||
|
||||
final var outputDir = OptionUtils.outputDir(options).resolve(app.appImageDirName());
|
||||
final var outputDir = PathUtils.normalizedAbsolutePath(OptionUtils.outputDir(options).resolve(app.appImageDirName()));
|
||||
|
||||
Log.verbose(I18N.getString("message.create-app-image"));
|
||||
|
||||
IOUtils.writableOutputDir(outputDir.getParent());
|
||||
|
||||
@ -142,14 +142,14 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
.predefinedAppImageLayout(app.asApplicationLayout().orElseThrow())
|
||||
.create(options, app);
|
||||
|
||||
Log.verbose(I18N.format("message.creating-app-bundle", outputDir.getFileName(), outputDir.toAbsolutePath().getParent()));
|
||||
|
||||
if (Files.exists(outputDir)) {
|
||||
throw new JPackageException(I18N.format("error.root-exists", outputDir.toAbsolutePath()));
|
||||
throw new JPackageException(I18N.format("error.root-exists", outputDir));
|
||||
}
|
||||
|
||||
pipelineBuilder.excludeDirFromCopying(outputDir.getParent())
|
||||
.create().execute(BuildEnv.withAppImageDir(env, outputDir), app);
|
||||
|
||||
Log.verbose(I18N.getString("message.app-image-created"));
|
||||
}
|
||||
|
||||
static <T extends Package> void createNativePackage(Options options,
|
||||
@ -174,11 +174,20 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
Objects.requireNonNull(createPipelineBuilder);
|
||||
Objects.requireNonNull(pipelineBuilderMutatorFactory);
|
||||
|
||||
var pipelineBuilder = Objects.requireNonNull(createPipelineBuilder.apply(pkg));
|
||||
|
||||
// Delete an old output package file (if any) before creating a new one.
|
||||
pipelineBuilder.task(PackageTaskID.DELETE_OLD_PACKAGE_FILE)
|
||||
.addDependencies(pipelineBuilder.taskGraphSnapshot().getTailsOf(PackageTaskID.CREATE_PACKAGE_FILE))
|
||||
.addDependent(PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.packageAction(PackagingPipeline::deleteOutputBundle)
|
||||
.add();
|
||||
|
||||
Packager.<T>build().pkg(pkg)
|
||||
.outputDir(OptionUtils.outputDir(options))
|
||||
.env(Objects.requireNonNull(createBuildEnv.apply(options, pkg)))
|
||||
.pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory)
|
||||
.execute(Objects.requireNonNull(createPipelineBuilder.apply(pkg)));
|
||||
.outputDir(OptionUtils.outputDir(options))
|
||||
.env(Objects.requireNonNull(createBuildEnv.apply(options, pkg)))
|
||||
.pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory)
|
||||
.execute(pipelineBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -195,10 +204,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
permanentWorkDirectory = Optional.of(tempDir.path());
|
||||
}
|
||||
bundler.accept(tempDir.options());
|
||||
|
||||
var packageType = OptionUtils.bundlingOperation(cmdline).packageType();
|
||||
|
||||
Log.verbose(I18N.format("message.bundle-created", I18N.getString(bundleTypeDescription(packageType, op.os()))));
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
} finally {
|
||||
@ -219,55 +224,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
});
|
||||
}
|
||||
|
||||
private String bundleTypeDescription(PackageType type, OperatingSystem os) {
|
||||
switch (type) {
|
||||
case StandardPackageType stdType -> {
|
||||
switch (stdType) {
|
||||
case WIN_MSI -> {
|
||||
return "bundle-type.win-msi";
|
||||
}
|
||||
case WIN_EXE -> {
|
||||
return "bundle-type.win-exe";
|
||||
}
|
||||
case LINUX_DEB -> {
|
||||
return "bundle-type.linux-deb";
|
||||
}
|
||||
case LINUX_RPM -> {
|
||||
return "bundle-type.linux-rpm";
|
||||
}
|
||||
case MAC_DMG -> {
|
||||
return "bundle-type.mac-dmg";
|
||||
}
|
||||
case MAC_PKG -> {
|
||||
return "bundle-type.mac-pkg";
|
||||
}
|
||||
default -> {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
case AppImagePackageType appImageType -> {
|
||||
switch (os) {
|
||||
case WINDOWS -> {
|
||||
return "bundle-type.win-app";
|
||||
}
|
||||
case LINUX -> {
|
||||
return "bundle-type.linux-app";
|
||||
}
|
||||
case MACOS -> {
|
||||
return "bundle-type.mac-app";
|
||||
}
|
||||
default -> {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class CachingSupplier<T> implements Supplier<T> {
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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,6 +29,7 @@ import static java.util.stream.Collectors.toMap;
|
||||
import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@ -39,12 +40,16 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.Application;
|
||||
import jdk.jpackage.internal.model.ApplicationLayout;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.pipeline.DirectedEdge;
|
||||
import jdk.jpackage.internal.pipeline.FixedDAG;
|
||||
@ -97,7 +102,7 @@ final class PackagingPipeline {
|
||||
/**
|
||||
* The way to access packaging build environment before building a package in a pipeline.
|
||||
*/
|
||||
interface StartupParameters {
|
||||
sealed interface StartupParameters {
|
||||
BuildEnv packagingEnv();
|
||||
}
|
||||
|
||||
@ -127,6 +132,7 @@ final class PackagingPipeline {
|
||||
enum PackageTaskID implements TaskID {
|
||||
RUN_POST_IMAGE_USER_SCRIPT,
|
||||
CREATE_CONFIG_FILES,
|
||||
DELETE_OLD_PACKAGE_FILE,
|
||||
CREATE_PACKAGE_FILE
|
||||
}
|
||||
|
||||
@ -183,9 +189,11 @@ final class PackagingPipeline {
|
||||
void execute() throws IOException;
|
||||
}
|
||||
|
||||
record TaskConfig(Optional<TaskAction> action) {
|
||||
record TaskConfig(Optional<TaskAction> action, Optional<TaskAction> beforeAction, Optional<TaskAction> afterAction) {
|
||||
TaskConfig {
|
||||
Objects.requireNonNull(action);
|
||||
Objects.requireNonNull(beforeAction);
|
||||
Objects.requireNonNull(afterAction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,47 +204,64 @@ final class PackagingPipeline {
|
||||
|
||||
final class TaskBuilder extends TaskSpecBuilder<TaskID> {
|
||||
|
||||
private TaskBuilder(TaskID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
private TaskBuilder(TaskID id, TaskConfig config) {
|
||||
this(id);
|
||||
config.action().ifPresent(this::setAction);
|
||||
}
|
||||
|
||||
private TaskBuilder setAction(TaskAction v) {
|
||||
action = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
TaskBuilder noaction() {
|
||||
action = null;
|
||||
return this;
|
||||
return setAction(ActionRole.WORKLOAD, null);
|
||||
}
|
||||
|
||||
<T extends Application, U extends ApplicationLayout> TaskBuilder applicationAction(ApplicationImageTaskAction<T, U> action) {
|
||||
return setAction(action);
|
||||
return applicationAction(ActionRole.WORKLOAD, action);
|
||||
}
|
||||
|
||||
<T extends Application, U extends AppImageLayout> TaskBuilder appImageAction(AppImageTaskAction<T, U> action) {
|
||||
return setAction(action);
|
||||
return appImageAction(ActionRole.WORKLOAD, action);
|
||||
}
|
||||
|
||||
<T extends Package> TaskBuilder copyAction(CopyAppImageTaskAction<T> action) {
|
||||
return setAction(action);
|
||||
return copyAction(ActionRole.WORKLOAD, action);
|
||||
}
|
||||
|
||||
<T extends Package, U extends AppImageLayout> TaskBuilder packageAction(PackageTaskAction<T, U> action) {
|
||||
return setAction(action);
|
||||
return packageAction(ActionRole.WORKLOAD, action);
|
||||
}
|
||||
|
||||
TaskBuilder action(NoArgTaskAction action) {
|
||||
return setAction(action);
|
||||
return action(ActionRole.WORKLOAD, action);
|
||||
}
|
||||
|
||||
<T extends Application, U extends AppImageLayout> TaskBuilder logAppImageActionBegin(String keyId, Function<AppImageBuildEnv<T, U>, Object[]> formatArgsSupplier) {
|
||||
return logAppImageAction(ActionRole.BEFORE, keyId, formatArgsSupplier);
|
||||
}
|
||||
|
||||
<T extends Application, U extends AppImageLayout> TaskBuilder logAppImageActionEnd(String keyId, Function<AppImageBuildEnv<T, U>, Object[]> formatArgsSupplier) {
|
||||
return logAppImageAction(ActionRole.AFTER, keyId, formatArgsSupplier);
|
||||
}
|
||||
|
||||
<T extends Package, U extends AppImageLayout> TaskBuilder logPackageActionBegin(String keyId, Function<PackageBuildEnv<T, U>, Object[]> argsSupplier) {
|
||||
return logPackageAction(ActionRole.BEFORE, keyId, argsSupplier);
|
||||
}
|
||||
|
||||
<T extends Package, U extends AppImageLayout> TaskBuilder logPackageActionEnd(String keyId, Function<PackageBuildEnv<T, U>, Object[]> argsSupplier) {
|
||||
return logPackageAction(ActionRole.AFTER, keyId, argsSupplier);
|
||||
}
|
||||
|
||||
TaskBuilder logActionBegin(String keyId, Supplier<Object[]> formatArgsSupplier) {
|
||||
return logAction(ActionRole.BEFORE, keyId, formatArgsSupplier);
|
||||
}
|
||||
|
||||
TaskBuilder logActionBegin(String keyId, Object... formatArgsSupplier) {
|
||||
return logAction(ActionRole.BEFORE, keyId, () -> formatArgsSupplier);
|
||||
}
|
||||
|
||||
TaskBuilder logActionEnd(String keyId, Supplier<Object[]> formatArgsSupplier) {
|
||||
return logAction(ActionRole.AFTER, keyId, formatArgsSupplier);
|
||||
}
|
||||
|
||||
TaskBuilder logActionEnd(String keyId, Object... formatArgsSupplier) {
|
||||
return logAction(ActionRole.AFTER, keyId, () -> formatArgsSupplier);
|
||||
}
|
||||
|
||||
boolean hasAction() {
|
||||
return action != null;
|
||||
return workloadAction != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -272,13 +297,109 @@ final class PackagingPipeline {
|
||||
}
|
||||
|
||||
Builder add() {
|
||||
final var config = new TaskConfig(Optional.ofNullable(action));
|
||||
final var config = new TaskConfig(
|
||||
Optional.ofNullable(workloadAction),
|
||||
Optional.ofNullable(beforeAction),
|
||||
Optional.ofNullable(afterAction));
|
||||
taskConfig.put(task(), config);
|
||||
createLinks().forEach(Builder.this::linkTasks);
|
||||
return Builder.this;
|
||||
}
|
||||
|
||||
private TaskAction action;
|
||||
|
||||
private enum ActionRole {
|
||||
WORKLOAD(TaskBuilder::setWorkloadAction),
|
||||
BEFORE(TaskBuilder::setBeforeAction),
|
||||
AFTER(TaskBuilder::setAfterAction),
|
||||
;
|
||||
|
||||
ActionRole(BiConsumer<TaskBuilder, TaskAction> actionSetter) {
|
||||
this.actionSetter = Objects.requireNonNull(actionSetter);
|
||||
}
|
||||
|
||||
TaskBuilder setAction(TaskBuilder taskBuilder, TaskAction action) {
|
||||
actionSetter.accept(taskBuilder, action);
|
||||
return taskBuilder;
|
||||
}
|
||||
|
||||
private final BiConsumer<TaskBuilder, TaskAction> actionSetter;
|
||||
}
|
||||
|
||||
|
||||
private TaskBuilder(TaskID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
private TaskBuilder(TaskID id, TaskConfig config) {
|
||||
this(id);
|
||||
config.action().ifPresent(this::setWorkloadAction);
|
||||
config.beforeAction().ifPresent(this::setBeforeAction);
|
||||
config.afterAction().ifPresent(this::setAfterAction);
|
||||
}
|
||||
|
||||
private TaskBuilder setAction(ActionRole role, TaskAction v) {
|
||||
return role.setAction(this, v);
|
||||
}
|
||||
|
||||
private TaskBuilder setWorkloadAction(TaskAction v) {
|
||||
workloadAction = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private TaskBuilder setBeforeAction(TaskAction v) {
|
||||
beforeAction = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private TaskBuilder setAfterAction(TaskAction v) {
|
||||
afterAction = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private <T extends Application, U extends ApplicationLayout> TaskBuilder applicationAction(ActionRole role, ApplicationImageTaskAction<T, U> action) {
|
||||
return setAction(role, action);
|
||||
}
|
||||
|
||||
private <T extends Application, U extends AppImageLayout> TaskBuilder appImageAction(ActionRole role, AppImageTaskAction<T, U> action) {
|
||||
return setAction(role, action);
|
||||
}
|
||||
|
||||
private <T extends Package> TaskBuilder copyAction(ActionRole role, CopyAppImageTaskAction<T> action) {
|
||||
return setAction(role, action);
|
||||
}
|
||||
|
||||
private <T extends Package, U extends AppImageLayout> TaskBuilder packageAction(ActionRole role, PackageTaskAction<T, U> action) {
|
||||
return setAction(role, action);
|
||||
}
|
||||
|
||||
private TaskBuilder action(ActionRole role, NoArgTaskAction action) {
|
||||
return setAction(role, action);
|
||||
}
|
||||
|
||||
private <T extends Application, U extends AppImageLayout> TaskBuilder logAppImageAction(ActionRole role, String keyId, Function<AppImageBuildEnv<T, U>, Object[]> formatArgsSupplier) {
|
||||
Objects.requireNonNull(keyId);
|
||||
return appImageAction(role, (AppImageBuildEnv<T, U> env) -> {
|
||||
Log.verbose(I18N.format(keyId, formatArgsSupplier.apply(env)));
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends Package, U extends AppImageLayout> TaskBuilder logPackageAction(ActionRole role, String keyId, Function<PackageBuildEnv<T, U>, Object[]> formatArgsSupplier) {
|
||||
Objects.requireNonNull(keyId);
|
||||
return packageAction(role, (PackageBuildEnv<T, U> env) -> {
|
||||
Log.verbose(I18N.format(keyId, formatArgsSupplier.apply(env)));
|
||||
});
|
||||
}
|
||||
|
||||
private TaskBuilder logAction(ActionRole role, String keyId, Supplier<Object[]> formatArgsSupplier) {
|
||||
Objects.requireNonNull(keyId);
|
||||
return action(role, () -> {
|
||||
Log.verbose(I18N.format(keyId, formatArgsSupplier.get()));
|
||||
});
|
||||
}
|
||||
|
||||
private TaskAction workloadAction;
|
||||
private TaskAction beforeAction;
|
||||
private TaskAction afterAction;
|
||||
}
|
||||
|
||||
Builder linkTasks(DirectedEdge<TaskID> edge) {
|
||||
@ -294,7 +415,11 @@ final class PackagingPipeline {
|
||||
}
|
||||
|
||||
TaskBuilder task(TaskID id) {
|
||||
return new TaskBuilder(id);
|
||||
return Optional.ofNullable(taskConfig.get(id)).map(taskConfig -> {
|
||||
return new TaskBuilder(id, taskConfig);
|
||||
}).orElseGet(() -> {
|
||||
return new TaskBuilder(id);
|
||||
});
|
||||
}
|
||||
|
||||
Stream<TaskBuilder> configuredTasks() {
|
||||
@ -392,6 +517,8 @@ final class PackagingPipeline {
|
||||
|
||||
builder.task(PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.addDependent(PrimaryTaskID.PACKAGE)
|
||||
.logActionBegin("message.create-package")
|
||||
.logActionEnd("message.package-created")
|
||||
.add();
|
||||
|
||||
builder.task(PrimaryTaskID.PACKAGE).add();
|
||||
@ -425,6 +552,17 @@ final class PackagingPipeline {
|
||||
.run(env.env(), env.pkg().app().name());
|
||||
}
|
||||
|
||||
static void deleteOutputBundle(PackageBuildEnv<Package, AppImageLayout> env) throws IOException {
|
||||
|
||||
var outputBundle = env.outputDir().resolve(env.pkg().packageFileNameWithSuffix());
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(outputBundle);
|
||||
} catch (IOException ex) {
|
||||
throw new JPackageException(I18N.format("error.output-bundle-cannot-be-overwritten", outputBundle.toAbsolutePath()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private PackagingPipeline(FixedDAG<TaskID> taskGraph, Map<TaskID, TaskConfig> taskConfig,
|
||||
UnaryOperator<TaskContext> contextMapper) {
|
||||
this.taskGraph = Objects.requireNonNull(taskGraph);
|
||||
@ -645,7 +783,13 @@ final class PackagingPipeline {
|
||||
}
|
||||
|
||||
if (accepted) {
|
||||
if (config.beforeAction.isPresent()) {
|
||||
context.execute(config.beforeAction.orElseThrow());
|
||||
}
|
||||
context.execute(config.action.orElseThrow());
|
||||
if (config.afterAction.isPresent()) {
|
||||
context.execute(config.afterAction.orElseThrow());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -56,7 +56,7 @@ import jdk.jpackage.internal.model.BundlingEnvironment;
|
||||
import jdk.jpackage.internal.model.BundlingOperationDescriptor;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.model.BundleType;
|
||||
|
||||
/**
|
||||
* Analyzes jpackage command line structure.
|
||||
@ -250,7 +250,7 @@ final class OptionsAnalyzer {
|
||||
return error("ERR_NoInstallerEntryPoint", mapFormatArguments(optionSpec));
|
||||
} else {
|
||||
return error("ERR_InvalidTypeOption", mapFormatArguments(
|
||||
optionSpec, bundlingOperation.packageTypeValue()));
|
||||
optionSpec, bundlingOperation.bundleTypeValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,30 +267,31 @@ final class OptionsAnalyzer {
|
||||
final var typeOption = TYPE.getOption();
|
||||
|
||||
return cmdline.find(typeOption).map(obj -> {
|
||||
if (obj instanceof PackageType packageType) {
|
||||
return packageType;
|
||||
if (obj instanceof BundleType bundleType) {
|
||||
return bundleType;
|
||||
} else {
|
||||
return typeOption.spec()
|
||||
var spec = new StandardOptionContext(os).mapOptionSpec(typeOption.spec());
|
||||
return spec
|
||||
.converter().orElseThrow()
|
||||
.convert(typeOption.spec().name(), StringToken.of(((String[])obj)[0]))
|
||||
.convert(spec.name(), StringToken.of(((String[])obj)[0]))
|
||||
.orElseThrow();
|
||||
}
|
||||
}).map(packageType -> {
|
||||
// Find standard bundling operations producing the given package type.
|
||||
}).map(bundleType -> {
|
||||
// Find standard bundling operations producing the given bundle type.
|
||||
var bundlingOperations = Stream.of(StandardBundlingOperation.values()).filter(op -> {
|
||||
return op.packageType().equals(packageType);
|
||||
return op.bundleType().equals(bundleType);
|
||||
}).toList();
|
||||
|
||||
if (bundlingOperations.isEmpty()) {
|
||||
// jpackage internal error: none of the standard bundling operations produce
|
||||
// bundles of the `packageType`.
|
||||
// bundles of the `bundleType`.
|
||||
throw new AssertionError(String.format(
|
||||
"None of the standard bundling operations produce bundles of type [%s]",
|
||||
packageType));
|
||||
bundleType));
|
||||
} else if (bundlingOperations.size() == 1) {
|
||||
return bundlingOperations.getFirst();
|
||||
} else {
|
||||
// Multiple standard bundling operations produce the `packageType` package type.
|
||||
// Multiple standard bundling operations produce the `bundleType` bundle type.
|
||||
// Filter those that belong to the current OS
|
||||
bundlingOperations = bundlingOperations.stream().filter(op -> {
|
||||
return op.os().equals(OperatingSystem.current());
|
||||
@ -298,10 +299,10 @@ final class OptionsAnalyzer {
|
||||
|
||||
if (bundlingOperations.isEmpty()) {
|
||||
// jpackage internal error: none of the standard bundling operations produce
|
||||
// bundles of the `packageType` on the current OS.
|
||||
// bundles of the `bundleType` on the current OS.
|
||||
throw new AssertionError(String.format(
|
||||
"None of the standard bundling operations produce bundles of type [%s] on %s",
|
||||
packageType, OperatingSystem.current()));
|
||||
bundleType, OperatingSystem.current()));
|
||||
} else if (bundlingOperations.size() == 1) {
|
||||
return bundlingOperations.getFirst();
|
||||
} else if (StandardBundlingOperation.MACOS_APP_IMAGE.containsAll(bundlingOperations)) {
|
||||
@ -316,7 +317,7 @@ final class OptionsAnalyzer {
|
||||
}
|
||||
}
|
||||
}).orElseGet(() -> {
|
||||
// No package type specified, use the default bundling operation in the given environment.
|
||||
// No bundle type specified, use the default bundling operation in the given environment.
|
||||
return env.defaultOperation().map(descriptor -> {
|
||||
return Stream.of(StandardBundlingOperation.values()).filter(op -> {
|
||||
return descriptor.equals(op.descriptor());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,8 +24,6 @@
|
||||
*/
|
||||
package jdk.jpackage.internal.cli;
|
||||
|
||||
import static jdk.jpackage.internal.model.AppImagePackageType.APP_IMAGE;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -34,6 +32,8 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.model.AppImageBundleType;
|
||||
import jdk.jpackage.internal.model.BundleType;
|
||||
import jdk.jpackage.internal.model.BundlingOperationDescriptor;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
@ -44,16 +44,16 @@ import jdk.jpackage.internal.util.SetBuilder;
|
||||
* Standard jpackage operations.
|
||||
*/
|
||||
public enum StandardBundlingOperation implements BundlingOperationOptionScope {
|
||||
CREATE_WIN_APP_IMAGE(APP_IMAGE, "^(?!(linux-|mac-|win-exe-|win-msi-))", OperatingSystem.WINDOWS),
|
||||
CREATE_LINUX_APP_IMAGE(APP_IMAGE, "^(?!(win-|mac-|linux-rpm-|linux-deb-))", OperatingSystem.LINUX),
|
||||
CREATE_MAC_APP_IMAGE(APP_IMAGE, "^(?!(linux-|win-|mac-dmg-|mac-pkg-))", OperatingSystem.MACOS),
|
||||
CREATE_WIN_APP_IMAGE(AppImageBundleType.WIN_APP_IMAGE, "^(?!(linux-|mac-|win-exe-|win-msi-))", OperatingSystem.WINDOWS),
|
||||
CREATE_LINUX_APP_IMAGE(AppImageBundleType.LINUX_APP_IMAGE, "^(?!(win-|mac-|linux-rpm-|linux-deb-))", OperatingSystem.LINUX),
|
||||
CREATE_MAC_APP_IMAGE(AppImageBundleType.MAC_APP_IMAGE, "^(?!(linux-|win-|mac-dmg-|mac-pkg-))", OperatingSystem.MACOS),
|
||||
CREATE_WIN_EXE(StandardPackageType.WIN_EXE, "^(?!(linux-|mac-|win-msi-))", OperatingSystem.WINDOWS),
|
||||
CREATE_WIN_MSI(StandardPackageType.WIN_MSI, "^(?!(linux-|mac-|win-exe-))", OperatingSystem.WINDOWS),
|
||||
CREATE_LINUX_RPM(StandardPackageType.LINUX_RPM, "^(?!(win-|mac-|linux-deb-))", OperatingSystem.LINUX),
|
||||
CREATE_LINUX_DEB(StandardPackageType.LINUX_DEB, "^(?!(win-|mac-|linux-rpm-))", OperatingSystem.LINUX),
|
||||
CREATE_MAC_PKG(StandardPackageType.MAC_PKG, "^(?!(linux-|win-|mac-dmg-))", OperatingSystem.MACOS),
|
||||
CREATE_MAC_DMG(StandardPackageType.MAC_DMG, "^(?!(linux-|win-|mac-pkg-))", OperatingSystem.MACOS),
|
||||
SIGN_MAC_APP_IMAGE(APP_IMAGE, OperatingSystem.MACOS, Verb.SIGN);
|
||||
SIGN_MAC_APP_IMAGE(AppImageBundleType.MAC_APP_IMAGE, OperatingSystem.MACOS, Verb.SIGN);
|
||||
|
||||
/**
|
||||
* Supported values of the {@link BundlingOperationDescriptor#verb()} property.
|
||||
@ -78,19 +78,19 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope {
|
||||
private final String value;
|
||||
}
|
||||
|
||||
StandardBundlingOperation(PackageType packageType, String optionNameRegexp, OperatingSystem os, Verb descriptorVerb) {
|
||||
this.packageType = Objects.requireNonNull(packageType);
|
||||
StandardBundlingOperation(BundleType bundleType, String optionNameRegexp, OperatingSystem os, Verb descriptorVerb) {
|
||||
this.bundleType = Objects.requireNonNull(bundleType);
|
||||
optionNamePredicate = Pattern.compile(optionNameRegexp).asPredicate();
|
||||
this.os = Objects.requireNonNull(os);
|
||||
this.descriptorVerb = Objects.requireNonNull(descriptorVerb);
|
||||
}
|
||||
|
||||
StandardBundlingOperation(PackageType packageType, String optionNameRegexp, OperatingSystem os) {
|
||||
this(packageType, optionNameRegexp, os, Verb.CREATE);
|
||||
StandardBundlingOperation(BundleType bundleType, String optionNameRegexp, OperatingSystem os) {
|
||||
this(bundleType, optionNameRegexp, os, Verb.CREATE);
|
||||
}
|
||||
|
||||
StandardBundlingOperation(PackageType packageType, OperatingSystem os, Verb descriptorVerb) {
|
||||
this.packageType = Objects.requireNonNull(packageType);
|
||||
StandardBundlingOperation(BundleType bundleType, OperatingSystem os, Verb descriptorVerb) {
|
||||
this.bundleType = Objects.requireNonNull(bundleType);
|
||||
optionNamePredicate = v -> false;
|
||||
this.os = Objects.requireNonNull(os);
|
||||
this.descriptorVerb = Objects.requireNonNull(descriptorVerb);
|
||||
@ -100,16 +100,20 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope {
|
||||
return os;
|
||||
}
|
||||
|
||||
public String packageTypeValue() {
|
||||
if (packageType.equals(APP_IMAGE)) {
|
||||
public String bundleTypeValue() {
|
||||
if (bundleType instanceof AppImageBundleType) {
|
||||
return "app-image";
|
||||
} else {
|
||||
return ((StandardPackageType)packageType).suffix().substring(1);
|
||||
return ((StandardPackageType)bundleType).suffix().substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
public BundleType bundleType() {
|
||||
return bundleType;
|
||||
}
|
||||
|
||||
public PackageType packageType() {
|
||||
return packageType;
|
||||
return (PackageType)bundleType();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,7 +126,7 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope {
|
||||
|
||||
@Override
|
||||
public BundlingOperationDescriptor descriptor() {
|
||||
return new BundlingOperationDescriptor(os(), packageTypeValue(), descriptorVerb.value());
|
||||
return new BundlingOperationDescriptor(os(), bundleTypeValue(), descriptorVerb.value());
|
||||
}
|
||||
|
||||
public static Optional<StandardBundlingOperation> valueOf(BundlingOperationDescriptor descriptor) {
|
||||
@ -199,6 +203,6 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope {
|
||||
|
||||
private final Predicate<String> optionNamePredicate;
|
||||
private final OperatingSystem os;
|
||||
private final PackageType packageType;
|
||||
private final BundleType bundleType;
|
||||
private final Verb descriptorVerb;
|
||||
}
|
||||
|
||||
@ -55,11 +55,12 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.model.AppImageBundleType;
|
||||
import jdk.jpackage.internal.model.BundleType;
|
||||
import jdk.jpackage.internal.model.BundlingOperationDescriptor;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
import jdk.jpackage.internal.model.LauncherShortcut;
|
||||
import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.util.SetBuilder;
|
||||
|
||||
/**
|
||||
@ -103,18 +104,18 @@ public final class StandardOption {
|
||||
|
||||
public static final OptionValue<Boolean> VERBOSE = auxilaryOption("verbose").create();
|
||||
|
||||
public static final OptionValue<PackageType> TYPE = option("type", PackageType.class).addAliases("t")
|
||||
public static final OptionValue<BundleType> TYPE = option("type", BundleType.class).addAliases("t")
|
||||
.scope(StandardBundlingOperation.values()).inScope(NOT_BUILDING_APP_IMAGE)
|
||||
.converterExceptionFactory(ERROR_WITH_VALUE).converterExceptionFormatString("ERR_InvalidInstallerType")
|
||||
.converter(str -> {
|
||||
Objects.requireNonNull(str);
|
||||
return Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> {
|
||||
return bundlingOperation.packageTypeValue().equals(str);
|
||||
}).map(StandardBundlingOperation::packageType).findFirst().orElseThrow(IllegalArgumentException::new);
|
||||
return parseBundleType(str, OperatingSystem.current());
|
||||
})
|
||||
.description("help.option.type" + resourceKeySuffix(OperatingSystem.current()))
|
||||
.mutate(createOptionSpecBuilderMutator((b, context) -> {
|
||||
b.description("help.option.type" + resourceKeySuffix(context.os()));
|
||||
b.converter(str -> {
|
||||
return parseBundleType(str, context.os());
|
||||
});
|
||||
})).create();
|
||||
|
||||
public static final OptionValue<Path> INPUT = directoryOption("input").addAliases("i")
|
||||
@ -665,6 +666,23 @@ public final class StandardOption {
|
||||
}).defaultArrayValue(new AdditionalLauncher[0]).createArray();
|
||||
}
|
||||
|
||||
private static BundleType parseBundleType(String str, OperatingSystem appImageOS) {
|
||||
Objects.requireNonNull(str);
|
||||
Objects.requireNonNull(appImageOS);
|
||||
|
||||
return Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> {
|
||||
return bundlingOperation.bundleTypeValue().equals(str);
|
||||
})
|
||||
.filter(bundlingOperation -> {
|
||||
// Skip app image bundle type if it is from another platform.
|
||||
return !(bundlingOperation.bundleType() instanceof AppImageBundleType)
|
||||
|| (bundlingOperation.os() == appImageOS);
|
||||
})
|
||||
.map(StandardBundlingOperation::bundleType)
|
||||
.findFirst()
|
||||
.orElseThrow(IllegalArgumentException::new);
|
||||
}
|
||||
|
||||
private static String resourceKeySuffix(OperatingSystem os) {
|
||||
switch (os) {
|
||||
case LINUX -> {
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 2026, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* App image bundle type.
|
||||
*
|
||||
* @see StandardPackageType
|
||||
*/
|
||||
public enum AppImageBundleType implements BundleType {
|
||||
|
||||
WIN_APP_IMAGE("bundle-type.win-app"),
|
||||
LINUX_APP_IMAGE("bundle-type.linux-app"),
|
||||
MAC_APP_IMAGE("bundle-type.mac-app"),
|
||||
;
|
||||
|
||||
private AppImageBundleType(String key) {
|
||||
this.key = Objects.requireNonNull(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String label() {
|
||||
return I18N.getString(key);
|
||||
}
|
||||
|
||||
private final String key;
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, 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,20 +22,18 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jpackage.internal.model;
|
||||
|
||||
/**
|
||||
* App image packaging type.
|
||||
*
|
||||
* @see StandardPackageType
|
||||
*/
|
||||
public final class AppImagePackageType implements PackageType {
|
||||
|
||||
private AppImagePackageType() {
|
||||
}
|
||||
/**
|
||||
* Generic bundle type. E.g.: application image, rpm, msi are all bundle types.
|
||||
*/
|
||||
public sealed interface BundleType permits PackageType, AppImageBundleType {
|
||||
|
||||
/**
|
||||
* Singleton
|
||||
* Returns a user-facing label of this bundle type.
|
||||
* @return a user-facing label of this bundle type.
|
||||
*/
|
||||
public static final AppImagePackageType APP_IMAGE = new AppImagePackageType();
|
||||
String label();
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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,8 +27,8 @@ package jdk.jpackage.internal.model;
|
||||
|
||||
|
||||
/**
|
||||
* Generic package type. E.g.: application image, rpm, msi are all package types.
|
||||
* Native package type. E.g.: dmg, rpm, msi are all package types.
|
||||
*
|
||||
* @see jdk.jpackage.internal.model.Package
|
||||
*/
|
||||
public interface PackageType {}
|
||||
public non-sealed interface PackageType extends BundleType {}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,19 +24,22 @@
|
||||
*/
|
||||
package jdk.jpackage.internal.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Standard native package types.
|
||||
*/
|
||||
public enum StandardPackageType implements PackageType {
|
||||
WIN_MSI(".msi"),
|
||||
WIN_EXE(".exe"),
|
||||
LINUX_DEB(".deb"),
|
||||
LINUX_RPM(".rpm"),
|
||||
MAC_PKG(".pkg"),
|
||||
MAC_DMG(".dmg");
|
||||
WIN_MSI("bundle-type.win-msi", ".msi"),
|
||||
WIN_EXE("bundle-type.win-exe", ".exe"),
|
||||
LINUX_DEB("bundle-type.linux-deb", ".deb"),
|
||||
LINUX_RPM("bundle-type.linux-rpm", ".rpm"),
|
||||
MAC_PKG("bundle-type.mac-pkg", ".pkg"),
|
||||
MAC_DMG("bundle-type.mac-dmg", ".dmg");
|
||||
|
||||
StandardPackageType(String suffix) {
|
||||
this.suffix = suffix;
|
||||
StandardPackageType(String key, String suffix) {
|
||||
this.key = Objects.requireNonNull(key);
|
||||
this.suffix = Objects.requireNonNull(suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,5 +51,11 @@ public enum StandardPackageType implements PackageType {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String label() {
|
||||
return I18N.getString(key);
|
||||
}
|
||||
|
||||
private final String key;
|
||||
private final String suffix;
|
||||
}
|
||||
|
||||
@ -28,14 +28,14 @@ param.copyright.default=Copyright (C) {0,date,YYYY}
|
||||
param.vendor.default=Unknown
|
||||
|
||||
bundle-type.win-app=Windows Application Image
|
||||
bundle-type.win-exe=EXE Installer Package
|
||||
bundle-type.win-msi=MSI Installer Package
|
||||
bundle-type.win-exe=Windows EXE Installer
|
||||
bundle-type.win-msi=Windows MSI Installer
|
||||
bundle-type.mac-app=Mac Application Image
|
||||
bundle-type.mac-dmg=Mac DMG Package
|
||||
bundle-type.mac-pkg=Mac PKG Package
|
||||
bundle-type.linux-app=Linux Application Image
|
||||
bundle-type.linux-deb=DEB Bundle
|
||||
bundle-type.linux-rpm=RPM Bundle
|
||||
bundle-type.linux-deb=Linux DEB Package
|
||||
bundle-type.linux-rpm=Linux RPM Package
|
||||
|
||||
resource.post-app-image-script=script to run after application image is populated
|
||||
|
||||
@ -43,9 +43,14 @@ message.using-default-resource=Using default package resource {0} {1} (add {2} t
|
||||
message.no-default-resource=No default package resource {0} (add {1} to the resource-dir to customize).
|
||||
message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}).
|
||||
message.using-custom-resource=Using custom package resource {0} (loaded from {1}).
|
||||
message.creating-app-bundle=Creating app package: {0} in {1}
|
||||
|
||||
message.create-package=Building output package file...
|
||||
message.create-app-image=Building output application image directory...
|
||||
message.package-created=Succeeded in building output package file
|
||||
message.app-image-created=Succeeded in building output application image directory
|
||||
|
||||
message.debug-working-directory=Kept working directory for debug: {0}
|
||||
message.bundle-created=Succeeded in building {0} package
|
||||
|
||||
message.module-version=Using version "{0}" from module "{1}" as application version
|
||||
|
||||
message.error-header=Error: {0}
|
||||
@ -97,6 +102,8 @@ error.tool-not-found.advice=Please install "{0}"
|
||||
error.tool-old-version=Can not find "{0}" {1} or newer
|
||||
error.tool-old-version.advice=Please install "{0}" {1} or newer
|
||||
|
||||
error.output-bundle-cannot-be-overwritten=Output package file "{0}" exists and can not be removed.
|
||||
|
||||
error.blocked.option=jlink option [{0}] is not permitted in --jlink-options
|
||||
error.no.name=Name not specified with --name and cannot infer one from app-image
|
||||
error.no.name.advice=Specify name with --name
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -79,8 +79,6 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat
|
||||
|
||||
private void wrapMsiInExe() throws IOException {
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-to-location", outputDir.toAbsolutePath()));
|
||||
|
||||
final var msi = msi();
|
||||
|
||||
// Copy template msi wrapper next to msi file
|
||||
@ -102,7 +100,5 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat
|
||||
Files.copy(exePath, dstExePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
dstExePath.toFile().setExecutable(true);
|
||||
|
||||
Log.verbose(I18N.format("message.output-location", outputDir.toAbsolutePath()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -315,7 +315,6 @@ final class WinMsiPackager implements Consumer<PackagingPipeline.Builder> {
|
||||
|
||||
private void buildPackage() throws IOException {
|
||||
final var msiOut = outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
Log.verbose(I18N.format("message.generating-msi", msiOut.toAbsolutePath()));
|
||||
wixPipeline.buildMsi(msiOut.toAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2017, 2026, 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
|
||||
@ -56,13 +56,9 @@ error.missing-service-installer.advice=Add 'service-installer.exe' service insta
|
||||
|
||||
message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place.
|
||||
message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
|
||||
message.outputting-to-location=Generating EXE for installer to: {0}.
|
||||
message.output-location=Installer (.exe) saved to: {0}
|
||||
message.tool-version=Detected [{0}] version [{1}].
|
||||
message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
|
||||
message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action.
|
||||
message.product-code=MSI ProductCode: {0}.
|
||||
message.upgrade-code=MSI UpgradeCode: {0}.
|
||||
message.preparing-msi-config=Preparing MSI config: {0}.
|
||||
message.generating-msi=Generating MSI: {0}.
|
||||
|
||||
|
||||
@ -80,6 +80,7 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
prerequisiteActions = new Actions();
|
||||
verifyActions = new Actions();
|
||||
excludeStandardAsserts(StandardAssert.MAIN_LAUNCHER_DESCRIPTION);
|
||||
removeOldOutputBundle = true;
|
||||
}
|
||||
|
||||
private JPackageCommand(JPackageCommand cmd, boolean immutable) {
|
||||
@ -91,6 +92,7 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
suppressOutput = cmd.suppressOutput;
|
||||
ignoreDefaultRuntime = cmd.ignoreDefaultRuntime;
|
||||
ignoreDefaultVerbose = cmd.ignoreDefaultVerbose;
|
||||
removeOldOutputBundle = cmd.removeOldOutputBundle;
|
||||
this.immutable = immutable;
|
||||
prerequisiteActions = new Actions(cmd.prerequisiteActions);
|
||||
verifyActions = new Actions(cmd.verifyActions);
|
||||
@ -844,6 +846,28 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this instance to optionally remove the existing output bundle
|
||||
* before running the jpackage command.
|
||||
*
|
||||
* @param v {@code true} to remove existing output bundle before running the
|
||||
* jpackage command, and {@code false} otherwise
|
||||
* @return this
|
||||
*/
|
||||
public JPackageCommand removeOldOutputBundle(boolean v) {
|
||||
verifyMutable();
|
||||
removeOldOutputBundle = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this instance will remove existing output bundle
|
||||
* before running the jpackage command, and {@code false} otherwise.
|
||||
*/
|
||||
public boolean isRemoveOldOutputBundle() {
|
||||
return removeOldOutputBundle;
|
||||
}
|
||||
|
||||
public JPackageCommand validateOutput(TKit.TextStreamVerifier validator) {
|
||||
return validateOutput(validator::apply);
|
||||
}
|
||||
@ -946,21 +970,18 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
verifyMutable();
|
||||
executePrerequisiteActions();
|
||||
|
||||
if (hasArgument("--dest")) {
|
||||
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();
|
||||
});
|
||||
}
|
||||
nullableOutputBundle().filter(_ -> {
|
||||
return removeOldOutputBundle;
|
||||
}).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);
|
||||
if (resourceDir != null && Files.isDirectory(resourceDir)) {
|
||||
@ -1090,7 +1111,7 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
private final Map<ReadOnlyPathAssert, List<TKit.PathSnapshot>> snapshots;
|
||||
}
|
||||
|
||||
public static enum ReadOnlyPathAssert{
|
||||
public static enum ReadOnlyPathAssert {
|
||||
APP_IMAGE(new Builder("--app-image").enable(cmd -> {
|
||||
// External app image should be R/O unless it is an app image signing on macOS.
|
||||
return !(TKit.isOSX() && MacHelper.signPredefinedAppImage(cmd));
|
||||
@ -1774,6 +1795,7 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
private boolean suppressOutput;
|
||||
private boolean ignoreDefaultRuntime;
|
||||
private boolean ignoreDefaultVerbose;
|
||||
private boolean removeOldOutputBundle;
|
||||
private boolean immutable;
|
||||
private final Actions prerequisiteActions;
|
||||
private final Actions verifyActions;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, 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
|
||||
@ -726,12 +726,30 @@ public final class PackageTest extends RunnablePackageTest {
|
||||
}
|
||||
|
||||
case CREATE -> {
|
||||
Executor.Result result = cmd.execute(expectedJPackageExitCode);
|
||||
var nullableOutputBundle = cmd.nullableOutputBundle();
|
||||
|
||||
var oldOutputBundleSnapshot = nullableOutputBundle
|
||||
.filter(Files::exists)
|
||||
.filter(_ -> {
|
||||
return !cmd.isRemoveOldOutputBundle();
|
||||
})
|
||||
.map(TKit.PathSnapshot::new);
|
||||
|
||||
var result = cmd.execute(expectedJPackageExitCode);
|
||||
|
||||
if (expectedJPackageExitCode == 0) {
|
||||
TKit.assertFileExists(cmd.outputBundle());
|
||||
} else {
|
||||
cmd.nullableOutputBundle().ifPresent(outputBundle -> {
|
||||
TKit.assertPathExists(outputBundle, false);
|
||||
nullableOutputBundle.ifPresent(outputBundle -> {
|
||||
oldOutputBundleSnapshot.ifPresentOrElse(snapshot -> {
|
||||
// jpackage failed, but the output bundle exists.
|
||||
// This output bundle existed before the jpackage was invoked.
|
||||
// Verify jpackage didn't modify it.
|
||||
new TKit.PathSnapshot(outputBundle).assertEquals(snapshot, String.format(
|
||||
"Check jpackage didn't modify the old output bundle [%s]", outputBundle));
|
||||
}, () -> {
|
||||
TKit.assertPathExists(outputBundle, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
verifyPackageBundle(cmd, result, expectedJPackageExitCode);
|
||||
|
||||
@ -41,7 +41,7 @@ import java.util.spi.ToolProvider;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.cli.StandardBundlingOperation;
|
||||
import jdk.jpackage.internal.model.AppImagePackageType;
|
||||
import jdk.jpackage.internal.model.AppImageBundleType;
|
||||
import jdk.jpackage.internal.model.BundlingOperationDescriptor;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
@ -119,9 +119,9 @@ public class DefaultBundlingEnvironmentTest extends JUnitAdapter {
|
||||
// #2 - jpackage should bail out earlier).
|
||||
//
|
||||
|
||||
final var type = op.packageTypeValue();
|
||||
final var type = op.bundleTypeValue();
|
||||
final int iterationCount;
|
||||
if (op.packageType() instanceof AppImagePackageType) {
|
||||
if (op.bundleType() instanceof AppImageBundleType) {
|
||||
iterationCount = 1;
|
||||
} else {
|
||||
iterationCount = 2;
|
||||
@ -165,7 +165,7 @@ public class DefaultBundlingEnvironmentTest extends JUnitAdapter {
|
||||
|
||||
private static Script createMockScript(StandardBundlingOperation op) {
|
||||
|
||||
if (op.packageType() instanceof AppImagePackageType) {
|
||||
if (op.bundleType() instanceof AppImageBundleType) {
|
||||
return Script.build().createSequence();
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -633,7 +633,12 @@ public class PackagingPipelineTest {
|
||||
Package create() {
|
||||
return new Package.Stub(
|
||||
app,
|
||||
new PackageType() {},
|
||||
new PackageType() {
|
||||
@Override
|
||||
public String label() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
},
|
||||
"the-package",
|
||||
"My package",
|
||||
"1.0",
|
||||
|
||||
@ -53,6 +53,8 @@ import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.cli.JOptSimpleOptionsBuilder.ConvertedOptionsBuilder;
|
||||
import jdk.jpackage.internal.cli.JOptSimpleOptionsBuilder.OptionsBuilder;
|
||||
import jdk.jpackage.internal.cli.StandardOption.LauncherProperty;
|
||||
import jdk.jpackage.internal.model.AppImageBundleType;
|
||||
import jdk.jpackage.internal.model.BundleType;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
import jdk.jpackage.internal.model.LauncherShortcut;
|
||||
import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory;
|
||||
@ -175,16 +177,21 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer {
|
||||
assertEquals(DirectoryNotEmptyException.class, ex.getCause().getClass());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(names = {"WINDOWS", "LINUX", "MACOS"})
|
||||
public void test_TYPE_valid(OperatingSystem appImageOS) {
|
||||
|
||||
var spec = new StandardOptionContext(appImageOS).mapOptionSpec(StandardOption.TYPE.getSpec());
|
||||
|
||||
test_TYPE_valid(spec, appImageOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_TYPE_valid() {
|
||||
|
||||
var spec = StandardOption.TYPE.getSpec();
|
||||
|
||||
Stream.of(StandardBundlingOperation.values()).forEach(bundlingOperation -> {
|
||||
var pkgTypeStr = bundlingOperation.packageTypeValue();
|
||||
var pkgType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(pkgTypeStr)).orElseThrow();
|
||||
assertSame(bundlingOperation.packageType(), pkgType);
|
||||
});
|
||||
test_TYPE_valid(spec, OperatingSystem.current());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ -336,6 +343,18 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer {
|
||||
assertEquals(expectedOptionTable, optionTable);
|
||||
}
|
||||
|
||||
private void test_TYPE_valid(OptionSpec<BundleType> spec, OperatingSystem appImageOS) {
|
||||
Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> {
|
||||
// Skip app image bundle type if it is from another platform.
|
||||
return !(bundlingOperation.bundleType() instanceof AppImageBundleType)
|
||||
|| (bundlingOperation.os() == appImageOS);
|
||||
}).forEach(bundlingOperation -> {
|
||||
var bundleTypeStr = bundlingOperation.bundleTypeValue();
|
||||
var bundleType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow();
|
||||
assertSame(bundlingOperation.bundleType(), bundleType);
|
||||
});
|
||||
}
|
||||
|
||||
private static Collection<Arguments> test_ARGUMENTS() {
|
||||
return List.of(
|
||||
Arguments.of("abc", List.of("abc")),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, 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,6 +22,7 @@
|
||||
*/
|
||||
|
||||
|
||||
import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE;
|
||||
import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -32,6 +33,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
@ -41,6 +43,7 @@ import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Annotations.ParameterSupplier;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.CannedFormattedString;
|
||||
import jdk.jpackage.test.ConfigurationTarget;
|
||||
import jdk.jpackage.test.Executor;
|
||||
import jdk.jpackage.test.HelloApp;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
@ -176,57 +179,74 @@ public final class BasicTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testVerbose() {
|
||||
JPackageCommand cmd = JPackageCommand.helloAppImage()
|
||||
// Disable default logic adding `--verbose` option
|
||||
// to jpackage command line.
|
||||
.ignoreDefaultVerbose(true)
|
||||
.saveConsoleOutput(true)
|
||||
.setFakeRuntime().executePrerequisiteActions();
|
||||
@Parameter("false")
|
||||
@Parameter("true")
|
||||
public void testQuiet(boolean appImage) {
|
||||
|
||||
List<String> expectedVerboseOutputStrings = new ArrayList<>();
|
||||
expectedVerboseOutputStrings.add("Creating app package:");
|
||||
if (TKit.isWindows()) {
|
||||
expectedVerboseOutputStrings.add(
|
||||
"Succeeded in building Windows Application Image package");
|
||||
} else if (TKit.isLinux()) {
|
||||
expectedVerboseOutputStrings.add(
|
||||
"Succeeded in building Linux Application Image package");
|
||||
} else if (TKit.isOSX()) {
|
||||
expectedVerboseOutputStrings.add("Preparing Info.plist:");
|
||||
expectedVerboseOutputStrings.add(
|
||||
"Succeeded in building Mac Application Image package");
|
||||
ConfigurationTarget target;
|
||||
if (appImage) {
|
||||
target = new ConfigurationTarget(JPackageCommand.helloAppImage());
|
||||
} else {
|
||||
TKit.throwUnknownPlatformError();
|
||||
target = new ConfigurationTarget(new PackageTest().configureHelloApp());
|
||||
}
|
||||
|
||||
TKit.deleteDirectoryContentsRecursive(cmd.outputDir());
|
||||
List<String> nonVerboseOutput = cmd.execute().getOutput();
|
||||
List<String>[] verboseOutput = (List<String>[])new List<?>[1];
|
||||
|
||||
// Directory clean up is not 100% reliable on Windows because of
|
||||
// antivirus software that can lock .exe files. Setup
|
||||
// different output directory instead of cleaning the default one for
|
||||
// verbose jpackage run.
|
||||
TKit.withTempDirectory("verbose-output", tempDir -> {
|
||||
cmd.setArgumentValue("--dest", tempDir);
|
||||
cmd.addArgument("--verbose");
|
||||
verboseOutput[0] = cmd.execute().getOutput();
|
||||
target.addInitializer(cmd -> {
|
||||
// Disable the default logic adding `--verbose` option to jpackage command line.
|
||||
cmd.ignoreDefaultVerbose(true)
|
||||
.useToolProvider(true)
|
||||
.saveConsoleOutput(true)
|
||||
.setFakeRuntime();
|
||||
});
|
||||
|
||||
TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(),
|
||||
"Check verbose output is longer than regular");
|
||||
Consumer<Executor.Result> asserter = result -> {
|
||||
TKit.assertStringListEquals(List.of(), result.getOutput(), "Check output is empty");
|
||||
};
|
||||
|
||||
expectedVerboseOutputStrings.forEach(str -> {
|
||||
TKit.assertTextStream(str).label("regular output")
|
||||
.predicate(String::contains).negate()
|
||||
.apply(nonVerboseOutput);
|
||||
target.cmd().map(JPackageCommand::execute).ifPresent(asserter);
|
||||
target.test().ifPresent(test -> {
|
||||
test.addBundleVerifier((_, result) -> {
|
||||
asserter.accept(result);
|
||||
}).run(CREATE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameter("false")
|
||||
@Parameter("true")
|
||||
public void testVerbose(boolean appImage) {
|
||||
|
||||
ConfigurationTarget target;
|
||||
if (appImage) {
|
||||
target = new ConfigurationTarget(JPackageCommand.helloAppImage());
|
||||
} else {
|
||||
target = new ConfigurationTarget(new PackageTest().configureHelloApp());
|
||||
}
|
||||
|
||||
target.addInitializer(cmd -> {
|
||||
// Disable the default logic adding `--verbose` option to jpackage command line.
|
||||
cmd.ignoreDefaultVerbose(true)
|
||||
.useToolProvider(true)
|
||||
.addArgument("--verbose")
|
||||
.saveConsoleOutput(true)
|
||||
.setFakeRuntime();
|
||||
|
||||
List<CannedFormattedString> verboseContent;
|
||||
if (appImage) {
|
||||
verboseContent = List.of(
|
||||
JPackageStringBundle.MAIN.cannedFormattedString("message.create-app-image"),
|
||||
JPackageStringBundle.MAIN.cannedFormattedString("message.app-image-created"));
|
||||
} else {
|
||||
verboseContent = List.of(
|
||||
JPackageStringBundle.MAIN.cannedFormattedString("message.create-package"),
|
||||
JPackageStringBundle.MAIN.cannedFormattedString("message.package-created"));
|
||||
}
|
||||
|
||||
cmd.validateOutput(verboseContent.toArray(CannedFormattedString[]::new));
|
||||
});
|
||||
|
||||
expectedVerboseOutputStrings.forEach(str -> {
|
||||
TKit.assertTextStream(str).label("verbose output")
|
||||
.apply(verboseOutput[0]);
|
||||
target.cmd().ifPresent(JPackageCommand::execute);
|
||||
target.test().ifPresent(test -> {
|
||||
test.run(CREATE);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
119
test/jdk/tools/jpackage/share/OutputErrorTest.java
Normal file
119
test/jdk/tools/jpackage/share/OutputErrorTest.java
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.spi.ToolProvider;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.JPackageStringBundle;
|
||||
import jdk.jpackage.test.JavaTool;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
import jdk.jpackage.test.TKit;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test how jpackage handles errors writing output bundle
|
||||
* @library /test/jdk/tools/jpackage/helpers
|
||||
* @build jdk.jpackage.test.*
|
||||
* @compile -Xlint:all -Werror OutputErrorTest.java
|
||||
* @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-run=OutputErrorTest
|
||||
*/
|
||||
|
||||
public final class OutputErrorTest {
|
||||
|
||||
@Test
|
||||
@Parameter("DIR")
|
||||
// "Locked file error" reliably works only on Windows
|
||||
@Parameter(value = "LOCKED_FILE", ifOS = OperatingSystem.WINDOWS)
|
||||
public void testPackage(ExistingOutputBundleType existingOutputBundleType) {
|
||||
|
||||
new PackageTest().configureHelloApp().addInitializer(cmd -> {
|
||||
|
||||
cmd.setFakeRuntime();
|
||||
cmd.setArgumentValue("--dest", TKit.createTempDirectory("output"));
|
||||
cmd.removeOldOutputBundle(false);
|
||||
cmd.validateOutput(JPackageCommand.makeError(JPackageStringBundle.MAIN.cannedFormattedString(
|
||||
"error.output-bundle-cannot-be-overwritten", cmd.outputBundle().toAbsolutePath())));
|
||||
|
||||
var outputBundle = cmd.outputBundle();
|
||||
|
||||
switch (existingOutputBundleType) {
|
||||
case DIR -> {
|
||||
Files.createDirectories(outputBundle);
|
||||
Files.writeString(outputBundle.resolve("foo.txt"), "Hello");
|
||||
}
|
||||
case LOCKED_FILE -> {
|
||||
Files.writeString(outputBundle, "Hello");
|
||||
cmd.useToolProvider(createToolProviderWithLockedFile(
|
||||
JavaTool.JPACKAGE.asToolProvider(), outputBundle));
|
||||
}
|
||||
}
|
||||
|
||||
}).setExpectedExitCode(1).run();
|
||||
}
|
||||
|
||||
enum ExistingOutputBundleType {
|
||||
DIR,
|
||||
LOCKED_FILE,
|
||||
;
|
||||
}
|
||||
|
||||
private static ToolProvider createToolProviderWithLockedFile(ToolProvider tp, Path lockedFile) {
|
||||
Objects.requireNonNull(tp);
|
||||
if (!Files.isRegularFile(lockedFile)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return new ToolProvider() {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "jpackage-mock";
|
||||
}
|
||||
|
||||
@SuppressWarnings("try")
|
||||
@Override
|
||||
public int run(PrintWriter out, PrintWriter err, String... args) {
|
||||
try {
|
||||
var lastModifiedTime = Files.getLastModifiedTime(lockedFile);
|
||||
try (var fos = new FileOutputStream(lockedFile.toFile()); var lock = fos.getChannel().lock()) {
|
||||
Files.setLastModifiedTime(lockedFile, lastModifiedTime);
|
||||
return tp.run(out, err, args);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user