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 }
|