test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java
branchJDK-8200758-branch
changeset 58648 3bf53ffa9ae7
parent 58416 f09bf58c1f17
child 58671 3b578a5976df
equal deleted inserted replaced
58647:2c43b89b1679 58648:3bf53ffa9ae7
    29 import java.security.SecureRandom;
    29 import java.security.SecureRandom;
    30 import java.util.*;
    30 import java.util.*;
    31 import java.util.function.Consumer;
    31 import java.util.function.Consumer;
    32 import java.util.function.Function;
    32 import java.util.function.Function;
    33 import java.util.function.Supplier;
    33 import java.util.function.Supplier;
       
    34 import java.util.regex.Matcher;
    34 import java.util.regex.Pattern;
    35 import java.util.regex.Pattern;
    35 import java.util.stream.Collectors;
    36 import java.util.stream.Collectors;
    36 import java.util.stream.Stream;
    37 import java.util.stream.Stream;
       
    38 import jdk.jpackage.internal.ApplicationLayout;
    37 import jdk.jpackage.test.Functional.ThrowingConsumer;
    39 import jdk.jpackage.test.Functional.ThrowingConsumer;
       
    40 import jdk.jpackage.test.Functional.ThrowingFunction;
    38 
    41 
    39 /**
    42 /**
    40  * jpackage command line with prerequisite actions. Prerequisite actions can be
    43  * jpackage command line with prerequisite actions. Prerequisite actions can be
    41  * anything. The simplest is to compile test application and pack in a jar for
    44  * anything. The simplest is to compile test application and pack in a jar for
    42  * use on jpackage command line.
    45  * use on jpackage command line.
    45 
    48 
    46     public JPackageCommand() {
    49     public JPackageCommand() {
    47         actions = new ArrayList<>();
    50         actions = new ArrayList<>();
    48     }
    51     }
    49 
    52 
       
    53     public JPackageCommand(JPackageCommand cmd) {
       
    54         this();
       
    55         args.addAll(cmd.args);
       
    56         withToolProvider = cmd.withToolProvider;
       
    57         saveConsoleOutput = cmd.saveConsoleOutput;
       
    58         suppressOutput = cmd.suppressOutput;
       
    59         ignoreDefaultRuntime = cmd.ignoreDefaultRuntime;
       
    60         immutable = cmd.immutable;
       
    61         actionsExecuted = cmd.actionsExecuted;
       
    62     }
       
    63 
    50     JPackageCommand createImmutableCopy() {
    64     JPackageCommand createImmutableCopy() {
    51         JPackageCommand reply = new JPackageCommand();
    65         JPackageCommand reply = new JPackageCommand(this);
    52         reply.immutable = true;
    66         reply.immutable = true;
    53         reply.args.addAll(args);
       
    54         return reply;
    67         return reply;
    55     }
    68     }
    56 
    69 
    57     public JPackageCommand setArgumentValue(String argName, String newValue) {
    70     public JPackageCommand setArgumentValue(String argName, String newValue) {
    58         verifyMutable();
    71         verifyMutable();
   150 
   163 
   151     public JPackageCommand addArguments(String name, Path value) {
   164     public JPackageCommand addArguments(String name, Path value) {
   152         return addArguments(name, value.toString());
   165         return addArguments(name, value.toString());
   153     }
   166     }
   154 
   167 
       
   168     public boolean isImagePackageType() {
       
   169         return PackageType.IMAGE == getArgumentValue("--package-type",
       
   170                 () -> null, PACKAGE_TYPES::get);
       
   171     }
       
   172 
   155     public PackageType packageType() {
   173     public PackageType packageType() {
   156         // Don't try to be in sync with jpackage defaults. Keep it simple:
   174         // Don't try to be in sync with jpackage defaults. Keep it simple:
   157         // if no `--package-type` explicitely set on the command line, consider
   175         // if no `--package-type` explicitely set on the command line, consider
   158         // this is operator's fault.
   176         // this is operator's fault.
   159         return getArgumentValue("--package-type",
   177         return getArgumentValue("--package-type",
   192     }
   210     }
   193 
   211 
   194     public JPackageCommand setFakeRuntime() {
   212     public JPackageCommand setFakeRuntime() {
   195         verifyMutable();
   213         verifyMutable();
   196 
   214 
   197         try {
   215         ThrowingConsumer<Path> createBulkFile = path -> {
       
   216             Files.createDirectories(path.getParent());
       
   217             try (FileOutputStream out = new FileOutputStream(path.toFile())) {
       
   218                 byte[] bytes = new byte[4 * 1024];
       
   219                 new SecureRandom().nextBytes(bytes);
       
   220                 out.write(bytes);
       
   221             }
       
   222         };
       
   223 
       
   224         addAction(cmd -> {
   198             Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime");
   225             Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime");
       
   226 
       
   227             TKit.trace(String.format("Init fake runtime in [%s] directory",
       
   228                     fakeRuntimeDir));
       
   229 
   199             Files.createDirectories(fakeRuntimeDir);
   230             Files.createDirectories(fakeRuntimeDir);
   200 
   231 
   201             if (TKit.isWindows() || TKit.isLinux()) {
   232             if (TKit.isWindows() || TKit.isLinux()) {
   202                 // Needed to make WindowsAppBundler happy as it copies MSVC dlls
   233                 // Needed to make WindowsAppBundler happy as it copies MSVC dlls
   203                 // from `bin` directory.
   234                 // from `bin` directory.
   204                 // Need to make the code in rpm spec happy as it assumes there is
   235                 // Need to make the code in rpm spec happy as it assumes there is
   205                 // always something in application image.
   236                 // always something in application image.
   206                 fakeRuntimeDir.resolve("bin").toFile().mkdir();
   237                 fakeRuntimeDir.resolve("bin").toFile().mkdir();
   207             }
   238             }
   208 
   239 
   209             Path bulk = fakeRuntimeDir.resolve(Path.of("bin", "bulk"));
   240             if (TKit.isOSX()) {
       
   241                 // Make MacAppImageBuilder happy
       
   242                 createBulkFile.accept(fakeRuntimeDir.resolve(Path.of(
       
   243                         "Contents/Home/lib/jli/libjli.dylib")));
       
   244             }
   210 
   245 
   211             // Mak sure fake runtime takes some disk space.
   246             // Mak sure fake runtime takes some disk space.
   212             // Package bundles with 0KB size are unexpected and considered
   247             // Package bundles with 0KB size are unexpected and considered
   213             // an error by PackageTest.
   248             // an error by PackageTest.
   214             Files.createDirectories(bulk.getParent());
   249             createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk")));
   215             try (FileOutputStream out = new FileOutputStream(bulk.toFile())) {
   250 
   216                 byte[] bytes = new byte[4 * 1024];
   251             cmd.addArguments("--runtime-image", fakeRuntimeDir);
   217                 new SecureRandom().nextBytes(bytes);
   252         });
   218                 out.write(bytes);
       
   219             }
       
   220 
       
   221             addArguments("--runtime-image", fakeRuntimeDir);
       
   222         } catch (IOException ex) {
       
   223             throw new RuntimeException(ex);
       
   224         }
       
   225 
   253 
   226         return this;
   254         return this;
   227     }
   255     }
   228 
   256 
   229     JPackageCommand addAction(ThrowingConsumer<JPackageCommand> action) {
   257     JPackageCommand addAction(ThrowingConsumer<JPackageCommand> action) {
   230         verifyMutable();
   258         verifyMutable();
   231         actions.add(ThrowingConsumer.toConsumer(action));
   259         actions.add(ThrowingConsumer.toConsumer(action));
   232         return this;
   260         return this;
   233     }
   261     }
   234 
   262 
       
   263     /**
       
   264      * Shorthand for {@code helloAppImage(null)}.
       
   265      */
   235     public static JPackageCommand helloAppImage() {
   266     public static JPackageCommand helloAppImage() {
   236         return helloAppImage(null);
   267         JavaAppDesc javaAppDesc = null;
       
   268         return helloAppImage(javaAppDesc);
   237     }
   269     }
   238 
   270 
   239     /**
   271     /**
   240      * Creates new JPackageCommand instance configured with the test Java app.
   272      * Creates new JPackageCommand instance configured with the test Java app.
   241      * For the explanation of `javaAppDesc` parameter, see documentation for
   273      * For the explanation of `javaAppDesc` parameter, see documentation for
   242      * HelloApp.addTo() method.
   274      * #JavaAppDesc.parse() method.
   243      *
   275      *
   244      * @param javaAppDesc Java application description
   276      * @param javaAppDesc Java application description
   245      * @return this
   277      * @return this
   246      */
   278      */
   247     public static JPackageCommand helloAppImage(String javaAppDesc) {
   279     public static JPackageCommand helloAppImage(String javaAppDesc) {
       
   280         final JavaAppDesc appDesc;
       
   281         if (javaAppDesc == null) {
       
   282             appDesc = null;
       
   283         } else {
       
   284             appDesc = JavaAppDesc.parse(javaAppDesc);
       
   285         }
       
   286         return helloAppImage(appDesc);
       
   287     }
       
   288 
       
   289     public static JPackageCommand helloAppImage(JavaAppDesc javaAppDesc) {
   248         JPackageCommand cmd = new JPackageCommand();
   290         JPackageCommand cmd = new JPackageCommand();
   249         cmd.setDefaultInputOutput().setDefaultAppName();
   291         cmd.setDefaultInputOutput().setDefaultAppName();
   250         PackageType.IMAGE.applyTo(cmd);
   292         PackageType.IMAGE.applyTo(cmd);
   251         HelloApp.addTo(cmd, javaAppDesc);
   293         new HelloApp(javaAppDesc).addTo(cmd);
   252         return cmd;
   294         return cmd;
   253     }
   295     }
   254 
   296 
   255     public JPackageCommand setPackageType(PackageType type) {
   297     public JPackageCommand setPackageType(PackageType type) {
   256         verifyMutable();
   298         verifyMutable();
   260 
   302 
   261     JPackageCommand setDefaultAppName() {
   303     JPackageCommand setDefaultAppName() {
   262         return addArguments("--name", TKit.getCurrentDefaultAppName());
   304         return addArguments("--name", TKit.getCurrentDefaultAppName());
   263     }
   305     }
   264 
   306 
       
   307     /**
       
   308      * Returns path to output bundle of configured jpackage command.
       
   309      *
       
   310      * If this is build image command, returns path to application image directory.
       
   311      */
   265     public Path outputBundle() {
   312     public Path outputBundle() {
   266         final PackageType type = packageType();
   313         final String bundleName;
   267         if (PackageType.IMAGE == type) {
   314         if (isImagePackageType()) {
   268             return null;
   315             String dirName = name();
   269         }
   316             if (TKit.isOSX()) {
   270 
   317                 dirName = dirName + ".app";
   271         String bundleName = null;
   318             }
   272         if (PackageType.LINUX.contains(type)) {
   319             bundleName = dirName;
       
   320         } else if (TKit.isLinux()) {
   273             bundleName = LinuxHelper.getBundleName(this);
   321             bundleName = LinuxHelper.getBundleName(this);
   274         } else if (PackageType.WINDOWS.contains(type)) {
   322         } else if (TKit.isWindows()) {
   275             bundleName = WindowsHelper.getBundleName(this);
   323             bundleName = WindowsHelper.getBundleName(this);
   276         } else if (PackageType.MAC.contains(type)) {
   324         } else if (TKit.isOSX()) {
   277             bundleName = MacHelper.getBundleName(this);
   325             bundleName = MacHelper.getBundleName(this);
       
   326         } else {
       
   327             throw TKit.throwUnknownPlatformError();
   278         }
   328         }
   279 
   329 
   280         return outputDir().resolve(bundleName);
   330         return outputDir().resolve(bundleName);
   281     }
   331     }
   282 
   332 
   283     /**
   333     /**
   284      * Returns path to directory where application will be installed.
   334      * Returns application layout.
       
   335      *
       
   336      * If this is build image command, returns application image layout of the
       
   337      * output bundle relative to output directory. Otherwise returns layout of
       
   338      * installed application relative to the root directory.
       
   339      *
       
   340      * If this command builds Java runtime, not an application, returns
       
   341      * corresponding layout.
       
   342      */
       
   343     public ApplicationLayout appLayout() {
       
   344         final ApplicationLayout layout;
       
   345         if (isRuntime()) {
       
   346             layout = ApplicationLayout.javaRuntime();
       
   347         } else {
       
   348             layout = ApplicationLayout.platformAppImage();
       
   349         }
       
   350 
       
   351         if (isImagePackageType()) {
       
   352             return layout.resolveAt(outputBundle());
       
   353         }
       
   354 
       
   355         return layout.resolveAt(appInstallationDirectory());
       
   356     }
       
   357 
       
   358     /**
       
   359      * Returns path to directory where application will be installed or null if
       
   360      * this is build image command.
   285      *
   361      *
   286      * E.g. on Linux for app named Foo default the function will return
   362      * E.g. on Linux for app named Foo default the function will return
   287      * `/opt/foo`
   363      * `/opt/foo`
   288      */
   364      */
   289     public Path appInstallationDirectory() {
   365     public Path appInstallationDirectory() {
   290         final PackageType type = packageType();
   366         if (isImagePackageType()) {
   291         if (PackageType.IMAGE == type) {
       
   292             return null;
   367             return null;
   293         }
   368         }
   294 
   369 
   295         if (PackageType.LINUX.contains(type)) {
   370         if (TKit.isLinux()) {
   296             if (isRuntime()) {
   371             if (isRuntime()) {
   297                 // Not fancy, but OK.
   372                 // Not fancy, but OK.
   298                 return Path.of(getArgumentValue("--install-dir", () -> "/opt"),
   373                 return Path.of(getArgumentValue("--install-dir", () -> "/opt"),
   299                         LinuxHelper.getPackageName(this));
   374                         LinuxHelper.getPackageName(this));
   300             }
   375             }
   301 
   376 
   302             // Launcher is in "bin" subfolder of the installation directory.
   377             // Launcher is in "bin" subfolder of the installation directory.
   303             return launcherInstallationPath().getParent().getParent();
   378             return appLauncherPath().getParent().getParent();
   304         }
   379         }
   305 
   380 
   306         if (PackageType.WINDOWS.contains(type)) {
   381         if (TKit.isWindows()) {
   307             return WindowsHelper.getInstallationDirectory(this);
   382             return WindowsHelper.getInstallationDirectory(this);
   308         }
   383         }
   309 
   384 
   310         if (PackageType.MAC.contains(type)) {
   385         if (TKit.isOSX()) {
   311             return MacHelper.getInstallationDirectory(this);
   386             return MacHelper.getInstallationDirectory(this);
   312         }
   387         }
   313 
   388 
   314         throw throwUnexpectedPackageTypeError();
   389         throw TKit.throwUnknownPlatformError();
   315     }
   390     }
   316 
   391 
   317     /**
   392     /**
   318      * Returns path where application's Java runtime will be installed.
   393      * Returns path to application's Java runtime.
   319      * If the command will package Java run-time only, still returns path to
   394      * If the command will package Java runtime only, returns correct path to
   320      * runtime subdirectory.
   395      * runtime directory.
   321      *
   396      *
   322      * E.g. on Linux for app named `Foo` the function will return
   397      * E.g.:
   323      * `/opt/foo/runtime`
   398      * [jpackage --name Foo --package-type rpm] -> `/opt/foo/lib/runtime`
   324      */
   399      * [jpackage --name Foo --package-type app-image --dest bar] -> `bar/Foo/lib/runtime`
   325     public Path appRuntimeInstallationDirectory() {
   400      * [jpackage --name Foo --package-type rpm --runtime-image java] -> `/opt/foo`
   326         if (PackageType.IMAGE == packageType()) {
   401      */
   327             return null;
   402     public Path appRuntimeDirectory() {
   328         }
   403         return appLayout().runtimeDirectory();
   329         return appInstallationDirectory().resolve(appRuntimePath(packageType()));
   404     }
   330     }
   405 
   331 
   406     /**
   332     /**
   407      * Returns path for application launcher with the given name.
   333      * Returns path where application launcher will be installed.
   408      *
   334      * If the command will package Java run-time only, still returns path to
   409      * E.g.: [jpackage --name Foo --package-type rpm] -> `/opt/foo/bin/Foo`
   335      * application launcher.
   410      * [jpackage --name Foo --package-type app-image --dest bar] ->
   336      *
   411      * `bar/Foo/bin/Foo`
   337      * E.g. on Linux for app named Foo default the function will return
   412      *
   338      * `/opt/foo/bin/Foo`
   413      * @param launcherName name of launcher or {@code null} for the main
   339      */
   414      * launcher
   340     public Path launcherInstallationPath() {
   415      *
   341         final PackageType type = packageType();
   416      * @throws IllegalArgumentException if the command is configured for
   342         if (PackageType.IMAGE == type) {
   417      * packaging Java runtime
   343             return null;
   418      */
   344         }
   419     public Path appLauncherPath(String launcherName) {
   345 
   420         verifyNotRuntime();
   346         if (PackageType.LINUX.contains(type)) {
   421         if (launcherName == null) {
   347             return outputDir().resolve(LinuxHelper.getLauncherPath(this));
   422             launcherName = name();
   348         }
   423         }
   349 
   424 
   350         if (PackageType.WINDOWS.contains(type)) {
   425         if (TKit.isWindows()) {
   351             return appInstallationDirectory().resolve(name() + ".exe");
   426             launcherName = launcherName + ".exe";
   352         }
   427         }
   353 
   428 
   354         if (PackageType.MAC.contains(type)) {
   429         if (isImagePackageType()) {
   355             return appInstallationDirectory().resolve(Path.of("Contents", "MacOS", name()));
   430             return appLayout().launchersDirectory().resolve(launcherName);
   356         }
   431         }
   357 
       
   358         throw throwUnexpectedPackageTypeError();
       
   359     }
       
   360 
       
   361     /**
       
   362      * Returns path to application image directory.
       
   363      *
       
   364      * E.g. if --dest is set to `foo` and --name is set to `bar` the function
       
   365      * will return `foo/bar` path on Linux and Windows and `foo/bar.app` on macOS.
       
   366      *
       
   367      * @throws IllegalArgumentException is command is doing platform packaging
       
   368      */
       
   369     public Path appImage() {
       
   370         verifyIsOfType(PackageType.IMAGE);
       
   371         String dirName = name();
       
   372         if (TKit.isOSX()) {
       
   373             dirName = dirName + ".app";
       
   374         }
       
   375         return outputDir().resolve(dirName);
       
   376     }
       
   377 
       
   378     /**
       
   379      * Returns path to application launcher relative to image directory.
       
   380      *
       
   381      * E.g. if --name is set to `Foo` the function will return `bin/Foo` path on
       
   382      * Linux, and `Foo.exe` on Windows.
       
   383      *
       
   384      * @throws IllegalArgumentException is command is doing platform packaging
       
   385      */
       
   386     public Path launcherPathInAppImage() {
       
   387         verifyIsOfType(PackageType.IMAGE);
       
   388 
   432 
   389         if (TKit.isLinux()) {
   433         if (TKit.isLinux()) {
   390             return Path.of("bin", name());
   434             LinuxHelper.getLauncherPath(this).getParent().resolve(launcherName);
   391         }
   435         }
   392 
   436 
   393         if (TKit.isOSX()) {
   437         return appLayout().launchersDirectory().resolve(launcherName);
   394             return Path.of("Contents", "MacOS", name());
   438     }
   395         }
   439 
   396 
   440     /**
   397         if (TKit.isWindows()) {
   441      * Shorthand for {@code appLauncherPath(null)}.
   398             return Path.of(name() + ".exe");
   442      */
   399         }
   443     public Path appLauncherPath() {
   400 
   444         return appLauncherPath(null);
   401         throw TKit.throwUnknownPlatformError();
   445     }
   402     }
   446 
   403 
   447     private void verifyNotRuntime() {
   404     /**
   448         if (isRuntime()) {
   405      * Returns path to runtime directory relative to image directory.
   449             throw new IllegalArgumentException("Java runtime packaging");
   406      *
   450         }
   407      * @throws IllegalArgumentException if command is configured for platform
   451     }
   408      * packaging
   452 
   409      */
   453     /**
   410     public Path appRuntimeDirectoryInAppImage() {
   454      * Returns path to .cfg file of the given application launcher.
   411         verifyIsOfType(PackageType.IMAGE);
   455      *
   412         return appRuntimePath(packageType());
   456      * E.g.:
   413     }
   457      * [jpackage --name Foo --package-type rpm] -> `/opt/foo/lib/app/Foo.cfg`
   414 
   458      * [jpackage --name Foo --package-type app-image --dest bar] -> `bar/Foo/lib/app/Foo.cfg`
   415     private static Path appRuntimePath(PackageType type) {
   459      *
   416         if (TKit.isLinux()) {
   460      * @param launcher name of launcher or {@code null} for the main launcher
   417             return Path.of("lib/runtime");
   461      *
   418         }
   462      * @throws IllegalArgumentException if the command is configured for
   419         if (TKit.isOSX()) {
   463      * packaging Java runtime
   420             return Path.of("Contents/runtime");
   464      */
   421         }
   465     public Path appLauncherCfgPath(String launcherName) {
   422 
   466         verifyNotRuntime();
   423         return Path.of("runtime");
   467         if (launcherName == null) {
   424     }
   468             launcherName = name();
   425 
   469         }
   426     public boolean isFakeRuntimeInAppImage(String msg) {
   470         return appLayout().appDirectory().resolve(launcherName + ".cfg");
   427         return isFakeRuntime(appImage().resolve(
   471     }
   428                 appRuntimeDirectoryInAppImage()), msg);
   472 
   429     }
   473     public boolean isFakeRuntime(String msg) {
   430 
   474         Path runtimeDir = appRuntimeDirectory();
   431     public boolean isFakeRuntimeInstalled(String msg) {
   475 
   432         return isFakeRuntime(appRuntimeInstallationDirectory(), msg);
       
   433     }
       
   434 
       
   435     private static boolean isFakeRuntime(Path runtimeDir, String msg) {
       
   436         final Collection<Path> criticalRuntimeFiles;
   476         final Collection<Path> criticalRuntimeFiles;
   437         if (TKit.isWindows()) {
   477         if (TKit.isWindows()) {
   438             criticalRuntimeFiles = WindowsHelper.CRITICAL_RUNTIME_FILES;
   478             criticalRuntimeFiles = WindowsHelper.CRITICAL_RUNTIME_FILES;
   439         } else if (TKit.isLinux()) {
   479         } else if (TKit.isLinux()) {
   440             criticalRuntimeFiles = LinuxHelper.CRITICAL_RUNTIME_FILES;
   480             criticalRuntimeFiles = LinuxHelper.CRITICAL_RUNTIME_FILES;
   480         verifyMutable();
   520         verifyMutable();
   481         suppressOutput = !v;
   521         suppressOutput = !v;
   482         return this;
   522         return this;
   483     }
   523     }
   484 
   524 
       
   525     public JPackageCommand ignoreDefaultRuntime(boolean v) {
       
   526         verifyMutable();
       
   527         ignoreDefaultRuntime = v;
       
   528         return this;
       
   529     }
       
   530 
   485     public boolean isWithToolProvider() {
   531     public boolean isWithToolProvider() {
   486         return Optional.ofNullable(withToolProvider).orElse(
   532         return Optional.ofNullable(withToolProvider).orElse(
   487                 defaultWithToolProvider);
   533                 defaultWithToolProvider);
   488     }
   534     }
   489 
   535 
   490     public void executePrerequisiteActions() {
   536     public JPackageCommand executePrerequisiteActions() {
   491         verifyMutable();
   537         verifyMutable();
   492         if (!actionsExecuted) {
   538         if (!actionsExecuted) {
   493             actionsExecuted = true;
   539             actionsExecuted = true;
   494             if (actions != null) {
   540             if (actions != null) {
   495                 actions.stream().forEach(r -> r.accept(this));
   541                 actions.stream().forEach(r -> r.accept(this));
   496             }
   542             }
   497         }
   543         }
       
   544         return this;
       
   545     }
       
   546 
       
   547     public Executor createExecutor() {
       
   548         verifyMutable();
       
   549         Executor exec = new Executor()
       
   550                 .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput)
       
   551                 .addArguments(args);
       
   552 
       
   553         if (isWithToolProvider()) {
       
   554             exec.setToolProvider(JavaTool.JPACKAGE);
       
   555         } else {
       
   556             exec.setExecutable(JavaTool.JPACKAGE);
       
   557         }
       
   558 
       
   559         return exec;
   498     }
   560     }
   499 
   561 
   500     public Executor.Result execute() {
   562     public Executor.Result execute() {
   501         executePrerequisiteActions();
   563         executePrerequisiteActions();
   502 
   564 
   503         if (packageType() == PackageType.IMAGE) {
   565         if (isImagePackageType()) {
   504             TKit.deleteDirectoryContentsRecursive(outputDir());
   566             TKit.deleteDirectoryContentsRecursive(outputDir());
   505         }
   567         }
   506 
   568 
   507         Executor exec = new Executor()
   569         return new JPackageCommand(this)
   508                 .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput)
   570                 .adjustArgumentsBeforeExecution()
   509                 .addArguments(new JPackageCommand().addArguments(
   571                 .createExecutor()
   510                                 args).adjustArgumentsBeforeExecution().args);
   572                 .execute();
   511 
       
   512         if (isWithToolProvider()) {
       
   513             exec.setToolProvider(JavaTool.JPACKAGE.asToolProvider());
       
   514         } else {
       
   515             exec.setExecutable(JavaTool.JPACKAGE);
       
   516         }
       
   517         return exec.execute();
       
   518     }
   573     }
   519 
   574 
   520     public JPackageCommand executeAndAssertHelloAppImageCreated() {
   575     public JPackageCommand executeAndAssertHelloAppImageCreated() {
   521         executeAndAssertImageCreated();
   576         executeAndAssertImageCreated();
   522         HelloApp.executeLauncherAndVerifyOutput(this);
   577         HelloApp.executeLauncherAndVerifyOutput(this);
   528         return assertImageCreated();
   583         return assertImageCreated();
   529     }
   584     }
   530 
   585 
   531     public JPackageCommand assertImageCreated() {
   586     public JPackageCommand assertImageCreated() {
   532         verifyIsOfType(PackageType.IMAGE);
   587         verifyIsOfType(PackageType.IMAGE);
   533         TKit.assertExecutableFileExists(appImage().resolve(
   588         TKit.assertDirectoryExists(appRuntimeDirectory());
   534                 launcherPathInAppImage()));
   589 
   535         TKit.assertDirectoryExists(appImage().resolve(
   590         if (!isRuntime()) {
   536                 appRuntimeDirectoryInAppImage()));
   591             TKit.assertExecutableFileExists(appLauncherPath());
       
   592             TKit.assertFileExists(appLauncherCfgPath(null));
       
   593         }
       
   594 
   537         return this;
   595         return this;
   538     }
   596     }
   539 
   597 
   540     private JPackageCommand adjustArgumentsBeforeExecution() {
   598     private JPackageCommand adjustArgumentsBeforeExecution() {
   541         if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null) {
   599         if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) {
   542             addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE);
   600             addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE);
   543         }
   601         }
   544 
   602 
   545         if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE) {
   603         if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE) {
   546             addArgument("--verbose");
   604             addArgument("--verbose");
   547         }
   605         }
   548 
   606 
   549         return this;
   607         return this;
   550     }
       
   551 
       
   552     private static RuntimeException throwUnexpectedPackageTypeError() {
       
   553         throw new IllegalArgumentException("Unexpected package type");
       
   554     }
   608     }
   555 
   609 
   556     String getPrintableCommandLine() {
   610     String getPrintableCommandLine() {
   557         return new Executor()
   611         return new Executor()
   558                 .setExecutable(JavaTool.JPACKAGE)
   612                 .setExecutable(JavaTool.JPACKAGE)
   563     public void verifyIsOfType(Collection<PackageType> types) {
   617     public void verifyIsOfType(Collection<PackageType> types) {
   564         verifyIsOfType(types.toArray(PackageType[]::new));
   618         verifyIsOfType(types.toArray(PackageType[]::new));
   565     }
   619     }
   566 
   620 
   567     public void verifyIsOfType(PackageType ... types) {
   621     public void verifyIsOfType(PackageType ... types) {
   568         if (!Arrays.asList(types).contains(packageType())) {
   622         final Set<PackageType> typesSet = Set.of(types);
   569             throwUnexpectedPackageTypeError();
   623         if (!hasArgument("--package-type")) {
   570         }
   624             if (!isImagePackageType()) {
       
   625                 if (TKit.isLinux() && typesSet.equals(PackageType.LINUX)) {
       
   626                     return;
       
   627                 }
       
   628 
       
   629                 if (TKit.isWindows() && typesSet.equals(PackageType.WINDOWS)) {
       
   630                     return;
       
   631                 }
       
   632 
       
   633                 if (TKit.isOSX() && typesSet.equals(PackageType.MAC)) {
       
   634                     return;
       
   635                 }
       
   636             } else if (typesSet.equals(Set.of(PackageType.IMAGE))) {
       
   637                 return;
       
   638             }
       
   639         }
       
   640 
       
   641         if (!typesSet.contains(packageType())) {
       
   642             throw new IllegalArgumentException("Unexpected package type");
       
   643         }
       
   644     }
       
   645 
       
   646     public CfgFile readLaunherCfgFile() {
       
   647         return readLaunherCfgFile(null);
       
   648     }
       
   649 
       
   650     public CfgFile readLaunherCfgFile(String launcherName) {
       
   651         verifyIsOfType(PackageType.IMAGE);
       
   652         if (isRuntime()) {
       
   653             return null;
       
   654         }
       
   655         return ThrowingFunction.toFunction(CfgFile::readFromFile).apply(
       
   656                 appLauncherCfgPath(launcherName));
   571     }
   657     }
   572 
   658 
   573     public static String escapeAndJoin(String... args) {
   659     public static String escapeAndJoin(String... args) {
   574         return escapeAndJoin(List.of(args));
   660         return escapeAndJoin(List.of(args));
   575     }
   661     }
   614     }
   700     }
   615 
   701 
   616     private Boolean withToolProvider;
   702     private Boolean withToolProvider;
   617     private boolean saveConsoleOutput;
   703     private boolean saveConsoleOutput;
   618     private boolean suppressOutput;
   704     private boolean suppressOutput;
       
   705     private boolean ignoreDefaultRuntime;
   619     private boolean immutable;
   706     private boolean immutable;
   620     private boolean actionsExecuted;
   707     private boolean actionsExecuted;
   621     private final List<Consumer<JPackageCommand>> actions;
   708     private final List<Consumer<JPackageCommand>> actions;
   622     private static boolean defaultWithToolProvider;
   709     private static boolean defaultWithToolProvider;
   623 
   710