< prev index next >

test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java

Print this page




  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.jpackage.test;
  24 
  25 import java.io.FileOutputStream;
  26 import java.io.IOException;
  27 import java.nio.file.Files;
  28 import java.nio.file.Path;
  29 import java.security.SecureRandom;
  30 import java.util.*;
  31 import java.util.function.Consumer;
  32 import java.util.function.Function;

  33 import java.util.function.Supplier;
  34 import java.util.regex.Matcher;
  35 import java.util.regex.Pattern;
  36 import java.util.stream.Collectors;
  37 import java.util.stream.Stream;
  38 import jdk.incubator.jpackage.internal.ApplicationLayout;
  39 import jdk.jpackage.test.Functional.ThrowingConsumer;
  40 import jdk.jpackage.test.Functional.ThrowingFunction;

  41 
  42 /**
  43  * jpackage command line with prerequisite actions. Prerequisite actions can be
  44  * anything. The simplest is to compile test application and pack in a jar for
  45  * use on jpackage command line.
  46  */
  47 public final class JPackageCommand extends CommandArguments<JPackageCommand> {
  48 
  49     public JPackageCommand() {
  50         actions = new ArrayList<>();

  51     }
  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 
  64     JPackageCommand createImmutableCopy() {
  65         JPackageCommand reply = new JPackageCommand(this);
  66         reply.immutable = true;
  67         return reply;
  68     }
  69 
  70     public JPackageCommand setArgumentValue(String argName, String newValue) {
  71         verifyMutable();
  72 
  73         String prevArg = null;
  74         ListIterator<String> it = args.listIterator();
  75         while (it.hasNext()) {
  76             String value = it.next();
  77             if (prevArg != null && prevArg.equals(argName)) {
  78                 if (newValue != null) {
  79                     it.set(newValue);
  80                 } else {
  81                     it.remove();


 187     public Path inputDir() {
 188         return getArgumentValue("--input", () -> null, Path::of);
 189     }
 190 
 191     public String version() {
 192         return getArgumentValue("--app-version", () -> "1.0");
 193     }
 194 
 195     public String name() {
 196         return getArgumentValue("--name", () -> getArgumentValue("--main-class"));
 197     }
 198 
 199     public boolean isRuntime() {
 200         return  hasArgument("--runtime-image")
 201                 && !hasArgument("--main-jar")
 202                 && !hasArgument("--module")
 203                 && !hasArgument("--app-image");
 204     }
 205 
 206     public JPackageCommand setDefaultInputOutput() {
 207         addArguments("--input", TKit.defaultInputDir());
 208         addArguments("--dest", TKit.defaultOutputDir());
 209         return this;
 210     }
 211 
 212     public JPackageCommand setFakeRuntime() {
 213         verifyMutable();
 214 
 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 -> {
 225             Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime");
 226 
 227             TKit.trace(String.format("Init fake runtime in [%s] directory",
 228                     fakeRuntimeDir));
 229 
 230             Files.createDirectories(fakeRuntimeDir);
 231 
 232             if (TKit.isWindows() || TKit.isLinux()) {
 233                 // Needed to make WindowsAppBundler happy as it copies MSVC dlls
 234                 // from `bin` directory.
 235                 // Need to make the code in rpm spec happy as it assumes there is
 236                 // always something in application image.
 237                 fakeRuntimeDir.resolve("bin").toFile().mkdir();
 238             }
 239 
 240             if (TKit.isOSX()) {
 241                 // Make MacAppImageBuilder happy
 242                 createBulkFile.accept(fakeRuntimeDir.resolve(Path.of(
 243                         "Contents/Home/lib/jli/libjli.dylib")));
 244             }
 245 
 246             // Mak sure fake runtime takes some disk space.
 247             // Package bundles with 0KB size are unexpected and considered
 248             // an error by PackageTest.
 249             createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk")));
 250 
 251             cmd.addArguments("--runtime-image", fakeRuntimeDir);
 252         });
 253 
 254         return this;
 255     }
 256 
 257     JPackageCommand addAction(ThrowingConsumer<JPackageCommand> action) {






 258         verifyMutable();
 259         actions.add(ThrowingConsumer.toConsumer(action));
 260         return this;
 261     }
 262 
 263     /**
 264      * Shorthand for {@code helloAppImage(null)}.
 265      */
 266     public static JPackageCommand helloAppImage() {
 267         JavaAppDesc javaAppDesc = null;
 268         return helloAppImage(javaAppDesc);
 269     }
 270 
 271     /**
 272      * Creates new JPackageCommand instance configured with the test Java app.
 273      * For the explanation of `javaAppDesc` parameter, see documentation for
 274      * #JavaAppDesc.parse() method.
 275      *
 276      * @param javaAppDesc Java application description
 277      * @return this
 278      */
 279     public static JPackageCommand helloAppImage(String javaAppDesc) {


 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.
 361      *
 362      * E.g. on Linux for app named Foo default the function will return
 363      * `/opt/foo`
 364      */
 365     public Path appInstallationDirectory() {






 366         if (isImagePackageType()) {
 367             return null;
 368         }
 369 
 370         if (TKit.isLinux()) {
 371             if (isRuntime()) {
 372                 // Not fancy, but OK.
 373                 return Path.of(getArgumentValue("--install-dir", () -> "/opt"),
 374                         LinuxHelper.getPackageName(this));
 375             }
 376 
 377             // Launcher is in "bin" subfolder of the installation directory.
 378             return appLauncherPath().getParent().getParent();
 379         }
 380 
 381         if (TKit.isWindows()) {
 382             return WindowsHelper.getInstallationDirectory(this);
 383         }
 384 
 385         if (TKit.isOSX()) {


 409      * E.g.: [jpackage --name Foo --type rpm] -> `/opt/foo/bin/Foo`
 410      * [jpackage --name Foo --type app-image --dest bar] ->
 411      * `bar/Foo/bin/Foo`
 412      *
 413      * @param launcherName name of launcher or {@code null} for the main
 414      * launcher
 415      *
 416      * @throws IllegalArgumentException if the command is configured for
 417      * packaging Java runtime
 418      */
 419     public Path appLauncherPath(String launcherName) {
 420         verifyNotRuntime();
 421         if (launcherName == null) {
 422             launcherName = name();
 423         }
 424 
 425         if (TKit.isWindows()) {
 426             launcherName = launcherName + ".exe";
 427         }
 428 
 429         if (isImagePackageType()) {
 430             return appLayout().launchersDirectory().resolve(launcherName);
 431         }
 432 
 433         if (TKit.isLinux()) {
 434             return LinuxHelper.getLauncherPath(this).getParent().resolve(launcherName);
 435         }
 436 
 437         return appLayout().launchersDirectory().resolve(launcherName);
 438     }
 439 
 440     /**
 441      * Shorthand for {@code appLauncherPath(null)}.
 442      */
 443     public Path appLauncherPath() {
 444         return appLauncherPath(null);
 445     }
 446 
 447     private void verifyNotRuntime() {
 448         if (isRuntime()) {
 449             throw new IllegalArgumentException("Java runtime packaging");


 479         } else if (TKit.isLinux()) {
 480             criticalRuntimeFiles = LinuxHelper.CRITICAL_RUNTIME_FILES;
 481         } else if (TKit.isOSX()) {
 482             criticalRuntimeFiles = MacHelper.CRITICAL_RUNTIME_FILES;
 483         } else {
 484             throw TKit.throwUnknownPlatformError();
 485         }
 486 
 487         if (criticalRuntimeFiles.stream().filter(
 488                 v -> runtimeDir.resolve(v).toFile().exists()).findFirst().orElse(
 489                         null) == null) {
 490             // Fake runtime
 491             TKit.trace(String.format(
 492                     "%s because application runtime directory [%s] is incomplete",
 493                     msg, runtimeDir));
 494             return true;
 495         }
 496         return false;
 497     }
 498 













 499     public static void useToolProviderByDefault() {
 500         defaultWithToolProvider = true;
 501     }
 502 
 503     public static void useExecutableByDefault() {
 504         defaultWithToolProvider = false;
 505     }
 506 
 507     public JPackageCommand useToolProvider(boolean v) {
 508         verifyMutable();
 509         withToolProvider = v;
 510         return this;
 511     }
 512 
 513     public JPackageCommand saveConsoleOutput(boolean v) {
 514         verifyMutable();
 515         saveConsoleOutput = v;
 516         return this;
 517     }
 518 
 519     public JPackageCommand dumpOutput(boolean v) {
 520         verifyMutable();
 521         suppressOutput = !v;
 522         return this;
 523     }
 524 
 525     public JPackageCommand ignoreDefaultRuntime(boolean v) {
 526         verifyMutable();
 527         ignoreDefaultRuntime = v;
 528         return this;
 529     }
 530 






 531     public boolean isWithToolProvider() {
 532         return Optional.ofNullable(withToolProvider).orElse(
 533                 defaultWithToolProvider);
 534     }
 535 
 536     public JPackageCommand executePrerequisiteActions() {
 537         verifyMutable();
 538         if (!actionsExecuted) {
 539             actionsExecuted = true;
 540             if (actions != null) {
 541                 actions.stream().forEach(r -> r.accept(this));
 542             }
 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;
 560     }
 561 
 562     public Executor.Result execute() {




 563         executePrerequisiteActions();
 564 
 565         if (isImagePackageType()) {
 566             TKit.deleteDirectoryContentsRecursive(outputDir());






















 567         }
 568 
 569         return new JPackageCommand(this)
 570                 .adjustArgumentsBeforeExecution()
 571                 .createExecutor()
 572                 .execute();






 573     }
 574 
 575     public JPackageCommand executeAndAssertHelloAppImageCreated() {
 576         executeAndAssertImageCreated();
 577         HelloApp.executeLauncherAndVerifyOutput(this);
 578         return this;
 579     }
 580 
 581     public JPackageCommand executeAndAssertImageCreated() {
 582         execute().assertExitCodeIsZero();
 583         return assertImageCreated();

 584     }
 585 
 586     public JPackageCommand assertImageCreated() {
 587         verifyIsOfType(PackageType.IMAGE);
 588         TKit.assertDirectoryExists(appRuntimeDirectory());
 589 
 590         if (!isRuntime()) {
 591             TKit.assertExecutableFileExists(appLauncherPath());
 592             TKit.assertFileExists(appLauncherCfgPath(null));
 593         }
 594 
 595         return this;
 596     }
 597 






 598     private JPackageCommand adjustArgumentsBeforeExecution() {
 599         if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) {
 600             addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE);
 601         }
 602 
 603         if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE) {
 604             addArgument("--verbose");
 605         }
 606 
 607         return this;
 608     }
 609 
 610     String getPrintableCommandLine() {
 611         return new Executor()
 612                 .setExecutable(JavaTool.JPACKAGE)
 613                 .addArguments(args)
 614                 .getPrintableCommandLine();
 615     }
 616 
 617     public void verifyIsOfType(Collection<PackageType> types) {
 618         verifyIsOfType(types.toArray(PackageType[]::new));
 619     }
 620 
 621     public void verifyIsOfType(PackageType ... types) {
 622         final var typesSet = Stream.of(types).collect(Collectors.toSet());
 623         if (!hasArgument("--type")) {
 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;


 682         if (TKit.isOSX()) {
 683             path = Path.of("Contents/Home").resolve(path);
 684         }
 685         return path;
 686     }
 687 
 688     public static Stream<String> filterOutput(Stream<String> jpackageOutput) {
 689         // Skip "WARNING: Using incubator ..." first line of output
 690         return jpackageOutput.skip(1);
 691     }
 692 
 693     public static List<String> filterOutput(List<String> jpackageOutput) {
 694         return filterOutput(jpackageOutput.stream()).collect(Collectors.toList());
 695     }
 696 
 697     @Override
 698     protected boolean isMutable() {
 699         return !immutable;
 700     }
 701 


































 702     private Boolean withToolProvider;
 703     private boolean saveConsoleOutput;
 704     private boolean suppressOutput;
 705     private boolean ignoreDefaultRuntime;

 706     private boolean immutable;
 707     private boolean actionsExecuted;
 708     private final List<Consumer<JPackageCommand>> actions;
 709     private static boolean defaultWithToolProvider;
 710 
 711     private final static Map<String, PackageType> PACKAGE_TYPES = Functional.identity(
 712             () -> {
 713                 Map<String, PackageType> reply = new HashMap<>();
 714                 for (PackageType type : PackageType.values()) {
 715                     reply.put(type.getName(), type);
 716                 }
 717                 return reply;
 718             }).get();
 719 
 720     public final static Path DEFAULT_RUNTIME_IMAGE = Functional.identity(() -> {
 721         // Set the property to the path of run-time image to speed up
 722         // building app images and platform bundles by avoiding running jlink
 723         // The value of the property will be automativcally appended to
 724         // jpackage command line if the command line doesn't have
 725         // `--runtime-image` parameter set.
 726         String val = TKit.getConfigProperty("runtime-image");
 727         if (val != null) {
 728             return Path.of(val);
 729         }
 730         return null;
 731     }).get();


 732 }


  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.jpackage.test;
  24 
  25 import java.io.FileOutputStream;
  26 import java.io.IOException;
  27 import java.nio.file.Files;
  28 import java.nio.file.Path;
  29 import java.security.SecureRandom;
  30 import java.util.*;
  31 import java.util.function.Consumer;
  32 import java.util.function.Function;
  33 import java.util.function.Predicate;
  34 import java.util.function.Supplier;

  35 import java.util.regex.Pattern;
  36 import java.util.stream.Collectors;
  37 import java.util.stream.Stream;
  38 import jdk.incubator.jpackage.internal.ApplicationLayout;
  39 import jdk.jpackage.test.Functional.ThrowingConsumer;
  40 import jdk.jpackage.test.Functional.ThrowingFunction;
  41 import jdk.jpackage.test.Functional.ThrowingSupplier;
  42 
  43 /**
  44  * jpackage command line with prerequisite actions. Prerequisite actions can be
  45  * anything. The simplest is to compile test application and pack in a jar for
  46  * use on jpackage command line.
  47  */
  48 public final class JPackageCommand extends CommandArguments<JPackageCommand> {
  49 
  50     public JPackageCommand() {
  51         prerequisiteActions = new Actions();
  52         verifyActions = new Actions();
  53     }
  54 
  55     public JPackageCommand(JPackageCommand cmd) {

  56         args.addAll(cmd.args);
  57         withToolProvider = cmd.withToolProvider;
  58         saveConsoleOutput = cmd.saveConsoleOutput;
  59         suppressOutput = cmd.suppressOutput;
  60         ignoreDefaultRuntime = cmd.ignoreDefaultRuntime;
  61         ignoreDefaultVerbose = cmd.ignoreDefaultVerbose;
  62         immutable = cmd.immutable;
  63         prerequisiteActions = new Actions(cmd.prerequisiteActions);
  64         verifyActions = new Actions(cmd.verifyActions);
  65     }
  66 
  67     JPackageCommand createImmutableCopy() {
  68         JPackageCommand reply = new JPackageCommand(this);
  69         reply.immutable = true;
  70         return reply;
  71     }
  72 
  73     public JPackageCommand setArgumentValue(String argName, String newValue) {
  74         verifyMutable();
  75 
  76         String prevArg = null;
  77         ListIterator<String> it = args.listIterator();
  78         while (it.hasNext()) {
  79             String value = it.next();
  80             if (prevArg != null && prevArg.equals(argName)) {
  81                 if (newValue != null) {
  82                     it.set(newValue);
  83                 } else {
  84                     it.remove();


 190     public Path inputDir() {
 191         return getArgumentValue("--input", () -> null, Path::of);
 192     }
 193 
 194     public String version() {
 195         return getArgumentValue("--app-version", () -> "1.0");
 196     }
 197 
 198     public String name() {
 199         return getArgumentValue("--name", () -> getArgumentValue("--main-class"));
 200     }
 201 
 202     public boolean isRuntime() {
 203         return  hasArgument("--runtime-image")
 204                 && !hasArgument("--main-jar")
 205                 && !hasArgument("--module")
 206                 && !hasArgument("--app-image");
 207     }
 208 
 209     public JPackageCommand setDefaultInputOutput() {
 210         setArgumentValue("--input", TKit.workDir().resolve("input"));
 211         setArgumentValue("--dest", TKit.workDir().resolve("output"));
 212         return this;
 213     }
 214 
 215     public JPackageCommand setFakeRuntime() {
 216         verifyMutable();
 217 
 218         ThrowingConsumer<Path> createBulkFile = path -> {
 219             Files.createDirectories(path.getParent());
 220             try (FileOutputStream out = new FileOutputStream(path.toFile())) {
 221                 byte[] bytes = new byte[4 * 1024];
 222                 new SecureRandom().nextBytes(bytes);
 223                 out.write(bytes);
 224             }
 225         };
 226 
 227         addPrerequisiteAction(cmd -> {
 228             Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime");
 229 
 230             TKit.trace(String.format("Init fake runtime in [%s] directory",
 231                     fakeRuntimeDir));
 232 
 233             Files.createDirectories(fakeRuntimeDir);
 234 
 235             if (TKit.isWindows() || TKit.isLinux()) {
 236                 // Needed to make WindowsAppBundler happy as it copies MSVC dlls
 237                 // from `bin` directory.
 238                 // Need to make the code in rpm spec happy as it assumes there is
 239                 // always something in application image.
 240                 fakeRuntimeDir.resolve("bin").toFile().mkdir();
 241             }
 242 
 243             if (TKit.isOSX()) {
 244                 // Make MacAppImageBuilder happy
 245                 createBulkFile.accept(fakeRuntimeDir.resolve(Path.of(
 246                         "Contents/Home/lib/jli/libjli.dylib")));
 247             }
 248 
 249             // Mak sure fake runtime takes some disk space.
 250             // Package bundles with 0KB size are unexpected and considered
 251             // an error by PackageTest.
 252             createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk")));
 253 
 254             cmd.addArguments("--runtime-image", fakeRuntimeDir);
 255         });
 256 
 257         return this;
 258     }
 259 
 260     JPackageCommand addPrerequisiteAction(ThrowingConsumer<JPackageCommand> action) {
 261         verifyMutable();
 262         prerequisiteActions.add(action);
 263         return this;
 264     }
 265 
 266     JPackageCommand addVerifyAction(ThrowingConsumer<JPackageCommand> action) {
 267         verifyMutable();
 268         verifyActions.add(action);
 269         return this;
 270     }
 271 
 272     /**
 273      * Shorthand for {@code helloAppImage(null)}.
 274      */
 275     public static JPackageCommand helloAppImage() {
 276         JavaAppDesc javaAppDesc = null;
 277         return helloAppImage(javaAppDesc);
 278     }
 279 
 280     /**
 281      * Creates new JPackageCommand instance configured with the test Java app.
 282      * For the explanation of `javaAppDesc` parameter, see documentation for
 283      * #JavaAppDesc.parse() method.
 284      *
 285      * @param javaAppDesc Java application description
 286      * @return this
 287      */
 288     public static JPackageCommand helloAppImage(String javaAppDesc) {


 355             layout = ApplicationLayout.javaRuntime();
 356         } else {
 357             layout = ApplicationLayout.platformAppImage();
 358         }
 359 
 360         if (isImagePackageType()) {
 361             return layout.resolveAt(outputBundle());
 362         }
 363 
 364         return layout.resolveAt(appInstallationDirectory());
 365     }
 366 
 367     /**
 368      * Returns path to directory where application will be installed or null if
 369      * this is build image command.
 370      *
 371      * E.g. on Linux for app named Foo default the function will return
 372      * `/opt/foo`
 373      */
 374     public Path appInstallationDirectory() {
 375         Path unpackedDir = getArgumentValue(UNPACKED_PATH_ARGNAME, () -> null,
 376                 Path::of);
 377         if (unpackedDir != null) {
 378             return unpackedDir;
 379         }
 380 
 381         if (isImagePackageType()) {
 382             return null;
 383         }
 384 
 385         if (TKit.isLinux()) {
 386             if (isRuntime()) {
 387                 // Not fancy, but OK.
 388                 return Path.of(getArgumentValue("--install-dir", () -> "/opt"),
 389                         LinuxHelper.getPackageName(this));
 390             }
 391 
 392             // Launcher is in "bin" subfolder of the installation directory.
 393             return appLauncherPath().getParent().getParent();
 394         }
 395 
 396         if (TKit.isWindows()) {
 397             return WindowsHelper.getInstallationDirectory(this);
 398         }
 399 
 400         if (TKit.isOSX()) {


 424      * E.g.: [jpackage --name Foo --type rpm] -> `/opt/foo/bin/Foo`
 425      * [jpackage --name Foo --type app-image --dest bar] ->
 426      * `bar/Foo/bin/Foo`
 427      *
 428      * @param launcherName name of launcher or {@code null} for the main
 429      * launcher
 430      *
 431      * @throws IllegalArgumentException if the command is configured for
 432      * packaging Java runtime
 433      */
 434     public Path appLauncherPath(String launcherName) {
 435         verifyNotRuntime();
 436         if (launcherName == null) {
 437             launcherName = name();
 438         }
 439 
 440         if (TKit.isWindows()) {
 441             launcherName = launcherName + ".exe";
 442         }
 443 
 444         if (isImagePackageType() || isPackageUnpacked()) {
 445             return appLayout().launchersDirectory().resolve(launcherName);
 446         }
 447 
 448         if (TKit.isLinux()) {
 449             return LinuxHelper.getLauncherPath(this).getParent().resolve(launcherName);
 450         }
 451 
 452         return appLayout().launchersDirectory().resolve(launcherName);
 453     }
 454 
 455     /**
 456      * Shorthand for {@code appLauncherPath(null)}.
 457      */
 458     public Path appLauncherPath() {
 459         return appLauncherPath(null);
 460     }
 461 
 462     private void verifyNotRuntime() {
 463         if (isRuntime()) {
 464             throw new IllegalArgumentException("Java runtime packaging");


 494         } else if (TKit.isLinux()) {
 495             criticalRuntimeFiles = LinuxHelper.CRITICAL_RUNTIME_FILES;
 496         } else if (TKit.isOSX()) {
 497             criticalRuntimeFiles = MacHelper.CRITICAL_RUNTIME_FILES;
 498         } else {
 499             throw TKit.throwUnknownPlatformError();
 500         }
 501 
 502         if (criticalRuntimeFiles.stream().filter(
 503                 v -> runtimeDir.resolve(v).toFile().exists()).findFirst().orElse(
 504                         null) == null) {
 505             // Fake runtime
 506             TKit.trace(String.format(
 507                     "%s because application runtime directory [%s] is incomplete",
 508                     msg, runtimeDir));
 509             return true;
 510         }
 511         return false;
 512     }
 513 
 514     public boolean isPackageUnpacked(String msg) {
 515         if (isPackageUnpacked()) {
 516             TKit.trace(String.format(
 517                     "%s because package was unpacked, not installed", msg));
 518             return true;
 519         }
 520         return false;
 521     }
 522 
 523     boolean isPackageUnpacked() {
 524         return hasArgument(UNPACKED_PATH_ARGNAME);
 525     }
 526 
 527     public static void useToolProviderByDefault() {
 528         defaultWithToolProvider = true;
 529     }
 530 
 531     public static void useExecutableByDefault() {
 532         defaultWithToolProvider = false;
 533     }
 534 
 535     public JPackageCommand useToolProvider(boolean v) {
 536         verifyMutable();
 537         withToolProvider = v;
 538         return this;
 539     }
 540 
 541     public JPackageCommand saveConsoleOutput(boolean v) {
 542         verifyMutable();
 543         saveConsoleOutput = v;
 544         return this;
 545     }
 546 
 547     public JPackageCommand dumpOutput(boolean v) {
 548         verifyMutable();
 549         suppressOutput = !v;
 550         return this;
 551     }
 552 
 553     public JPackageCommand ignoreDefaultRuntime(boolean v) {
 554         verifyMutable();
 555         ignoreDefaultRuntime = v;
 556         return this;
 557     }
 558 
 559     public JPackageCommand ignoreDefaultVerbose(boolean v) {
 560         verifyMutable();
 561         ignoreDefaultVerbose = v;
 562         return this;
 563     }
 564 
 565     public boolean isWithToolProvider() {
 566         return Optional.ofNullable(withToolProvider).orElse(
 567                 defaultWithToolProvider);
 568     }
 569 
 570     public JPackageCommand executePrerequisiteActions() {
 571         prerequisiteActions.run();
 572         return this;




 573     }
 574 
 575     public JPackageCommand executeVerifyActions() {
 576         verifyActions.run();
 577         return this;
 578     }
 579 
 580     private Executor createExecutor() {

 581         Executor exec = new Executor()
 582                 .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput)
 583                 .addArguments(args);
 584 
 585         if (isWithToolProvider()) {
 586             exec.setToolProvider(JavaTool.JPACKAGE);
 587         } else {
 588             exec.setExecutable(JavaTool.JPACKAGE);
 589         }
 590 
 591         return exec;
 592     }
 593 
 594     public Executor.Result execute() {
 595         return execute(0);
 596     }
 597 
 598     public Executor.Result execute(int expectedExitCode) {
 599         executePrerequisiteActions();
 600 
 601         if (isImagePackageType()) {
 602             TKit.deleteDirectoryContentsRecursive(outputDir());
 603         } else if (ThrowingSupplier.toSupplier(() -> Files.deleteIfExists(
 604                 outputBundle())).get()) {
 605             TKit.trace(
 606                     String.format("Deleted [%s] file before running jpackage",
 607                             outputBundle()));
 608         }
 609 
 610         Path resourceDir = getArgumentValue("--resource-dir", () -> null, Path::of);
 611         if (resourceDir != null && Files.isDirectory(resourceDir)) {
 612             TKit.trace(String.format("Files in [%s] resource dir:",
 613                     resourceDir));
 614             try (var files = Files.walk(resourceDir, 1)) {
 615                 files.sequential()
 616                         .filter(Predicate.not(resourceDir::equals))
 617                         .map(path -> String.format("[%s]", path.getFileName()))
 618                         .forEachOrdered(TKit::trace);
 619                 TKit.trace("Done");
 620             } catch (IOException ex) {
 621                 TKit.trace(String.format(
 622                         "Failed to list files in [%s] resource directory: %s",
 623                         resourceDir, ex));
 624             }
 625         }
 626 
 627         Executor.Result result = new JPackageCommand(this)
 628                 .adjustArgumentsBeforeExecution()
 629                 .createExecutor()
 630                 .execute(expectedExitCode);
 631 
 632         if (result.exitCode == 0) {
 633             executeVerifyActions();
 634         }
 635 
 636         return result;
 637     }
 638 
 639     public Executor.Result executeAndAssertHelloAppImageCreated() {
 640         Executor.Result result = executeAndAssertImageCreated();
 641         HelloApp.executeLauncherAndVerifyOutput(this);
 642         return result;
 643     }
 644 
 645     public Executor.Result executeAndAssertImageCreated() {
 646         Executor.Result result = execute();
 647         assertImageCreated();
 648         return result;
 649     }
 650 
 651     public JPackageCommand assertImageCreated() {
 652         verifyIsOfType(PackageType.IMAGE);
 653         TKit.assertDirectoryExists(appRuntimeDirectory());
 654 
 655         if (!isRuntime()) {
 656             TKit.assertExecutableFileExists(appLauncherPath());
 657             TKit.assertFileExists(appLauncherCfgPath(null));
 658         }
 659 
 660         return this;
 661     }
 662 
 663     JPackageCommand setUnpackedPackageLocation(Path path) {
 664         verifyIsOfType(PackageType.NATIVE);
 665         setArgumentValue(UNPACKED_PATH_ARGNAME, path);
 666         return this;
 667     }
 668 
 669     private JPackageCommand adjustArgumentsBeforeExecution() {
 670         if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) {
 671             addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE);
 672         }
 673 
 674         if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE && !ignoreDefaultVerbose) {
 675             addArgument("--verbose");
 676         }
 677 
 678         return this;
 679     }
 680 
 681     public String getPrintableCommandLine() {
 682         return createExecutor().getPrintableCommandLine();



 683     }
 684 
 685     public void verifyIsOfType(Collection<PackageType> types) {
 686         verifyIsOfType(types.toArray(PackageType[]::new));
 687     }
 688 
 689     public void verifyIsOfType(PackageType ... types) {
 690         final var typesSet = Stream.of(types).collect(Collectors.toSet());
 691         if (!hasArgument("--type")) {
 692             if (!isImagePackageType()) {
 693                 if (TKit.isLinux() && typesSet.equals(PackageType.LINUX)) {
 694                     return;
 695                 }
 696 
 697                 if (TKit.isWindows() && typesSet.equals(PackageType.WINDOWS)) {
 698                     return;
 699                 }
 700 
 701                 if (TKit.isOSX() && typesSet.equals(PackageType.MAC)) {
 702                     return;


 750         if (TKit.isOSX()) {
 751             path = Path.of("Contents/Home").resolve(path);
 752         }
 753         return path;
 754     }
 755 
 756     public static Stream<String> filterOutput(Stream<String> jpackageOutput) {
 757         // Skip "WARNING: Using incubator ..." first line of output
 758         return jpackageOutput.skip(1);
 759     }
 760 
 761     public static List<String> filterOutput(List<String> jpackageOutput) {
 762         return filterOutput(jpackageOutput.stream()).collect(Collectors.toList());
 763     }
 764 
 765     @Override
 766     protected boolean isMutable() {
 767         return !immutable;
 768     }
 769 
 770     private final class Actions implements Runnable {
 771         Actions() {
 772             actions = new ArrayList<>();
 773         }
 774 
 775         Actions(Actions other) {
 776             this();
 777             actions.addAll(other.actions);
 778         }
 779 
 780         void add(ThrowingConsumer<JPackageCommand> action) {
 781             Objects.requireNonNull(action);
 782             verifyMutable();
 783             actions.add(new Consumer<JPackageCommand>() {
 784                 @Override
 785                 public void accept(JPackageCommand t) {
 786                     if (!executed) {
 787                         executed = true;
 788                         ThrowingConsumer.toConsumer(action).accept(t);
 789                     }
 790                 }
 791                 private boolean executed;
 792             });
 793         }
 794 
 795         @Override
 796         public void run() {
 797             verifyMutable();
 798             actions.forEach(action -> action.accept(JPackageCommand.this));
 799         }
 800 
 801         private final List<Consumer<JPackageCommand>> actions;
 802     }
 803 
 804     private Boolean withToolProvider;
 805     private boolean saveConsoleOutput;
 806     private boolean suppressOutput;
 807     private boolean ignoreDefaultRuntime;
 808     private boolean ignoreDefaultVerbose;
 809     private boolean immutable;
 810     private final Actions prerequisiteActions;
 811     private final Actions verifyActions;
 812     private static boolean defaultWithToolProvider;
 813 
 814     private final static Map<String, PackageType> PACKAGE_TYPES = Functional.identity(
 815             () -> {
 816                 Map<String, PackageType> reply = new HashMap<>();
 817                 for (PackageType type : PackageType.values()) {
 818                     reply.put(type.getName(), type);
 819                 }
 820                 return reply;
 821             }).get();
 822 
 823     public final static Path DEFAULT_RUNTIME_IMAGE = Functional.identity(() -> {
 824         // Set the property to the path of run-time image to speed up
 825         // building app images and platform bundles by avoiding running jlink
 826         // The value of the property will be automativcally appended to
 827         // jpackage command line if the command line doesn't have
 828         // `--runtime-image` parameter set.
 829         String val = TKit.getConfigProperty("runtime-image");
 830         if (val != null) {
 831             return Path.of(val);
 832         }
 833         return null;
 834     }).get();
 835 
 836     private final static String UNPACKED_PATH_ARGNAME = "jpt-unpacked-folder";
 837 }
< prev index next >