25
26 package jdk.jpackage.internal;
27
28 import java.io.*;
29 import java.nio.charset.Charset;
30 import java.nio.file.Files;
31 import java.text.MessageFormat;
32 import java.util.*;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import static jdk.jpackage.internal.WindowsBundlerParam.*;
37
38 public class WinExeBundler extends AbstractBundler {
39
40 private static final ResourceBundle I18N = ResourceBundle.getBundle(
41 "jdk.jpackage.internal.resources.WinResources");
42
43 public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
44 new WindowsBundlerParam<>(
45 getString("param.exe-bundler.name"),
46 getString("param.exe-bundler.description"),
47 "win.app.bundler",
48 WinAppBundler.class,
49 params -> new WinAppBundler(),
50 null);
51
52 public static final BundlerParamInfo<File> EXE_IMAGE_DIR =
53 new WindowsBundlerParam<>(
54 getString("param.image-dir.name"),
55 getString("param.image-dir.description"),
56 "win.exe.imageDir",
57 File.class,
58 params -> {
59 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
60 if (!imagesRoot.exists()) imagesRoot.mkdirs();
61 return new File(imagesRoot, "win-exe.image");
62 },
63 (s, p) -> null);
64
65 public static final BundlerParamInfo<File> WIN_APP_IMAGE =
66 new WindowsBundlerParam<>(
67 getString("param.app-dir.name"),
68 getString("param.app-dir.description"),
69 "win.app.image",
70 File.class,
71 null,
72 (s, p) -> null);
73
74 public static final BundlerParamInfo<UUID> UPGRADE_UUID =
75 new WindowsBundlerParam<>(
76 I18N.getString("param.upgrade-uuid.name"),
77 I18N.getString("param.upgrade-uuid.description"),
78 Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
79 UUID.class,
80 params -> UUID.randomUUID(),
81 (s, p) -> UUID.fromString(s));
82
83 public static final StandardBundlerParam<Boolean> EXE_SYSTEM_WIDE =
84 new StandardBundlerParam<>(
85 getString("param.system-wide.name"),
86 getString("param.system-wide.description"),
87 Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
88 Boolean.class,
89 params -> true, // default to system wide
90 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
91 : Boolean.valueOf(s)
92 );
93 public static final StandardBundlerParam<String> PRODUCT_VERSION =
94 new StandardBundlerParam<>(
95 getString("param.product-version.name"),
96 getString("param.product-version.description"),
97 "win.msi.productVersion",
98 String.class,
99 VERSION::fetchFrom,
100 (s, p) -> s
101 );
102
103 public static final StandardBundlerParam<Boolean> MENU_HINT =
104 new WindowsBundlerParam<>(
105 getString("param.menu-shortcut-hint.name"),
106 getString("param.menu-shortcut-hint.description"),
107 Arguments.CLIOptions.WIN_MENU_HINT.getId(),
108 Boolean.class,
109 params -> false,
110 (s, p) -> (s == null ||
111 "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
112 );
113
114 public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
115 new WindowsBundlerParam<>(
116 getString("param.desktop-shortcut-hint.name"),
117 getString("param.desktop-shortcut-hint.description"),
118 Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
119 Boolean.class,
120 params -> false,
121 (s, p) -> (s == null ||
122 "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
123 );
124
125 private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
126 private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss";
127 private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
128
129 public static final BundlerParamInfo<String>
130 TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(
131 getString("param.iscc-path.name"),
132 getString("param.iscc-path.description"),
133 "win.exe.iscc.exe",
134 String.class,
135 params -> {
136 for (String dirString : (System.getenv("PATH")
137 + ";C:\\Program Files (x86)\\Inno Setup 5;"
138 + "C:\\Program Files\\Inno Setup 5").split(";")) {
139 File f = new File(dirString.replace("\"", ""),
140 TOOL_INNO_SETUP_COMPILER);
141 if (f.isFile()) {
142 return f.toString();
143 }
144 }
145 return null;
146 },
147 null);
148
149 @Override
150 public String getName() {
151 return getString("exe.bundler.name");
152 }
357 }
358 }
359 }
360
361 return true;
362 }
363
364 public File bundle(Map<String, ? super Object> p, File outdir)
365 throws PackagerException {
366 if (!outdir.isDirectory() && !outdir.mkdirs()) {
367 throw new PackagerException("error.cannot-create-output-dir",
368 outdir.getAbsolutePath());
369 }
370 if (!outdir.canWrite()) {
371 throw new PackagerException("error.cannot-write-to-output-dir",
372 outdir.getAbsolutePath());
373 }
374
375 String tempDirectory = WindowsDefender.getUserTempDirectory();
376 if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
377 tempDirectory = BUILD_ROOT.fetchFrom(p).getAbsolutePath();
378 }
379 if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
380 tempDirectory)) {
381 Log.error(MessageFormat.format(
382 getString("message.potential.windows.defender.issue"),
383 tempDirectory));
384 }
385
386 // validate we have valid tools before continuing
387 String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
388 if (iscc == null || !new File(iscc).isFile()) {
389 Log.verbose(getString("error.iscc-not-found"));
390 Log.verbose(MessageFormat.format(
391 getString("message.iscc-file-string"), iscc));
392 throw new PackagerException("error.iscc-not-found");
393 }
394
395 File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
396 try {
397 imageDir.mkdirs();
522 data.put("ARCHITECTURE_BIT_MODE", "x64");
523
524 validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p);
525
526 validateValueAndPut(data, "APPLICATION_DESCRIPTION",
527 DESCRIPTION, p);
528
529 data.put("APPLICATION_SERVICE", "returnFalse");
530 data.put("APPLICATION_NOT_SERVICE", "returnFalse");
531 data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse");
532 data.put("START_ON_INSTALL", "");
533 data.put("STOP_ON_UNINSTALL", "");
534 data.put("RUN_AT_STARTUP", "");
535
536 String imagePathString =
537 WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString();
538 data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString));
539 Log.verbose("setting APPLICATION_IMAGE to " +
540 innosetupEscape(imagePathString) + " for InnoSetup");
541
542 StringBuilder secondaryLaunchersCfg = new StringBuilder();
543 for (Map<String, ? super Object>
544 launcher : SECONDARY_LAUNCHERS.fetchFrom(p)) {
545 String application_name = APP_NAME.fetchFrom(launcher);
546 if (MENU_HINT.fetchFrom(launcher)) {
547 // Name: "{group}\APPLICATION_NAME";
548 // Filename: "{app}\APPLICATION_NAME.exe";
549 // IconFilename: "{app}\APPLICATION_NAME.ico"
550 secondaryLaunchersCfg.append("Name: \"{group}\\");
551 secondaryLaunchersCfg.append(application_name);
552 secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
553 secondaryLaunchersCfg.append(application_name);
554 secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
555 secondaryLaunchersCfg.append(application_name);
556 secondaryLaunchersCfg.append(".ico\"\r\n");
557 }
558 if (SHORTCUT_HINT.fetchFrom(launcher)) {
559 // Name: "{commondesktop}\APPLICATION_NAME";
560 // Filename: "{app}\APPLICATION_NAME.exe";
561 // IconFilename: "{app}\APPLICATION_NAME.ico"
562 secondaryLaunchersCfg.append("Name: \"{commondesktop}\\");
563 secondaryLaunchersCfg.append(application_name);
564 secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
565 secondaryLaunchersCfg.append(application_name);
566 secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
567 secondaryLaunchersCfg.append(application_name);
568 secondaryLaunchersCfg.append(".ico\"\r\n");
569 }
570 }
571 data.put("SECONDARY_LAUNCHERS", secondaryLaunchersCfg.toString());
572
573 StringBuilder registryEntries = new StringBuilder();
574 String regName = APP_REGISTRY_NAME.fetchFrom(p);
575 List<Map<String, ? super Object>> fetchFrom =
576 FILE_ASSOCIATIONS.fetchFrom(p);
577 for (int i = 0; i < fetchFrom.size(); i++) {
578 Map<String, ? super Object> fileAssociation = fetchFrom.get(i);
579 String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
580 File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO
581
582 List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
583 String entryName = regName + "File";
584 if (i > 0) {
585 entryName += "." + i;
586 }
587
588 if (extensions == null) {
589 Log.verbose(getString(
590 "message.creating-association-with-null-extension"));
591 } else {
706 .append(APP_NAME.fetchFrom(p))
707 .append("\"\" \"\"%1\"\"\"\r\n");
708 } else {
709 registryEntries.append(
710 "Root: HKCU; Subkey: \"Software\\Classes\\")
711 .append(entryName)
712 .append("\\shell\\open\\command\"; ValueType: " +
713 "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
714 .append(APP_NAME.fetchFrom(p))
715 .append("\"\" \"\"%1\"\"\"\r\n");
716 }
717 }
718 if (registryEntries.length() > 0) {
719 data.put("FILE_ASSOCIATIONS",
720 "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" +
721 registryEntries.toString());
722 } else {
723 data.put("FILE_ASSOCIATIONS", "");
724 }
725
726 // TODO - alternate template for JRE installer
727 String iss = RUNTIME_INSTALLER.fetchFrom(p) ?
728 DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE;
729
730 Writer w = new BufferedWriter(new FileWriter(
731 getConfig_ExeProjectFile(p)));
732
733 String content = preprocessTextResource(
734 getConfig_ExeProjectFile(p).getName(),
735 getString("resource.inno-setup-project-file"),
736 iss, data, VERBOSE.fetchFrom(p),
737 RESOURCE_DIR.fetchFrom(p));
738 w.write(content);
739 w.close();
740 return true;
741 }
742
743 private final static String removeQuotes(String s) {
744 if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) {
745 // special case for '"XXX"' return 'XXX' not '-XXX-'
746 // note '"' and '""' are excluded from this special case
747 s = s.substring(1, s.length() - 1);
|
25
26 package jdk.jpackage.internal;
27
28 import java.io.*;
29 import java.nio.charset.Charset;
30 import java.nio.file.Files;
31 import java.text.MessageFormat;
32 import java.util.*;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import static jdk.jpackage.internal.WindowsBundlerParam.*;
37
38 public class WinExeBundler extends AbstractBundler {
39
40 private static final ResourceBundle I18N = ResourceBundle.getBundle(
41 "jdk.jpackage.internal.resources.WinResources");
42
43 public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
44 new WindowsBundlerParam<>(
45 "win.app.bundler",
46 WinAppBundler.class,
47 params -> new WinAppBundler(),
48 null);
49
50 public static final BundlerParamInfo<File> EXE_IMAGE_DIR =
51 new WindowsBundlerParam<>(
52 "win.exe.imageDir",
53 File.class,
54 params -> {
55 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
56 if (!imagesRoot.exists()) imagesRoot.mkdirs();
57 return new File(imagesRoot, "win-exe.image");
58 },
59 (s, p) -> null);
60
61 public static final BundlerParamInfo<File> WIN_APP_IMAGE =
62 new WindowsBundlerParam<>(
63 "win.app.image",
64 File.class,
65 null,
66 (s, p) -> null);
67
68 public static final BundlerParamInfo<UUID> UPGRADE_UUID =
69 new WindowsBundlerParam<>(
70 Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
71 UUID.class,
72 params -> UUID.randomUUID(),
73 (s, p) -> UUID.fromString(s));
74
75 public static final StandardBundlerParam<Boolean> EXE_SYSTEM_WIDE =
76 new StandardBundlerParam<>(
77 Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
78 Boolean.class,
79 params -> true, // default to system wide
80 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
81 : Boolean.valueOf(s)
82 );
83 public static final StandardBundlerParam<String> PRODUCT_VERSION =
84 new StandardBundlerParam<>(
85 "win.msi.productVersion",
86 String.class,
87 VERSION::fetchFrom,
88 (s, p) -> s
89 );
90
91 public static final StandardBundlerParam<Boolean> MENU_HINT =
92 new WindowsBundlerParam<>(
93 Arguments.CLIOptions.WIN_MENU_HINT.getId(),
94 Boolean.class,
95 params -> false,
96 (s, p) -> (s == null ||
97 "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
98 );
99
100 public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
101 new WindowsBundlerParam<>(
102 Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
103 Boolean.class,
104 params -> false,
105 (s, p) -> (s == null ||
106 "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
107 );
108
109 private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
110 private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss";
111 private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
112
113 public static final BundlerParamInfo<String>
114 TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(
115 "win.exe.iscc.exe",
116 String.class,
117 params -> {
118 for (String dirString : (System.getenv("PATH")
119 + ";C:\\Program Files (x86)\\Inno Setup 5;"
120 + "C:\\Program Files\\Inno Setup 5").split(";")) {
121 File f = new File(dirString.replace("\"", ""),
122 TOOL_INNO_SETUP_COMPILER);
123 if (f.isFile()) {
124 return f.toString();
125 }
126 }
127 return null;
128 },
129 null);
130
131 @Override
132 public String getName() {
133 return getString("exe.bundler.name");
134 }
339 }
340 }
341 }
342
343 return true;
344 }
345
346 public File bundle(Map<String, ? super Object> p, File outdir)
347 throws PackagerException {
348 if (!outdir.isDirectory() && !outdir.mkdirs()) {
349 throw new PackagerException("error.cannot-create-output-dir",
350 outdir.getAbsolutePath());
351 }
352 if (!outdir.canWrite()) {
353 throw new PackagerException("error.cannot-write-to-output-dir",
354 outdir.getAbsolutePath());
355 }
356
357 String tempDirectory = WindowsDefender.getUserTempDirectory();
358 if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
359 tempDirectory = TEMP_ROOT.fetchFrom(p).getAbsolutePath();
360 }
361 if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
362 tempDirectory)) {
363 Log.error(MessageFormat.format(
364 getString("message.potential.windows.defender.issue"),
365 tempDirectory));
366 }
367
368 // validate we have valid tools before continuing
369 String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
370 if (iscc == null || !new File(iscc).isFile()) {
371 Log.verbose(getString("error.iscc-not-found"));
372 Log.verbose(MessageFormat.format(
373 getString("message.iscc-file-string"), iscc));
374 throw new PackagerException("error.iscc-not-found");
375 }
376
377 File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
378 try {
379 imageDir.mkdirs();
504 data.put("ARCHITECTURE_BIT_MODE", "x64");
505
506 validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p);
507
508 validateValueAndPut(data, "APPLICATION_DESCRIPTION",
509 DESCRIPTION, p);
510
511 data.put("APPLICATION_SERVICE", "returnFalse");
512 data.put("APPLICATION_NOT_SERVICE", "returnFalse");
513 data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse");
514 data.put("START_ON_INSTALL", "");
515 data.put("STOP_ON_UNINSTALL", "");
516 data.put("RUN_AT_STARTUP", "");
517
518 String imagePathString =
519 WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString();
520 data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString));
521 Log.verbose("setting APPLICATION_IMAGE to " +
522 innosetupEscape(imagePathString) + " for InnoSetup");
523
524 StringBuilder addLaunchersCfg = new StringBuilder();
525 for (Map<String, ? super Object>
526 launcher : ADD_LAUNCHERS.fetchFrom(p)) {
527 String application_name = APP_NAME.fetchFrom(launcher);
528 if (MENU_HINT.fetchFrom(launcher)) {
529 // Name: "{group}\APPLICATION_NAME";
530 // Filename: "{app}\APPLICATION_NAME.exe";
531 // IconFilename: "{app}\APPLICATION_NAME.ico"
532 addLaunchersCfg.append("Name: \"{group}\\");
533 addLaunchersCfg.append(application_name);
534 addLaunchersCfg.append("\"; Filename: \"{app}\\");
535 addLaunchersCfg.append(application_name);
536 addLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
537 addLaunchersCfg.append(application_name);
538 addLaunchersCfg.append(".ico\"\r\n");
539 }
540 if (SHORTCUT_HINT.fetchFrom(launcher)) {
541 // Name: "{commondesktop}\APPLICATION_NAME";
542 // Filename: "{app}\APPLICATION_NAME.exe";
543 // IconFilename: "{app}\APPLICATION_NAME.ico"
544 addLaunchersCfg.append("Name: \"{commondesktop}\\");
545 addLaunchersCfg.append(application_name);
546 addLaunchersCfg.append("\"; Filename: \"{app}\\");
547 addLaunchersCfg.append(application_name);
548 addLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
549 addLaunchersCfg.append(application_name);
550 addLaunchersCfg.append(".ico\"\r\n");
551 }
552 }
553 data.put("ADD_LAUNCHERS", addLaunchersCfg.toString());
554
555 StringBuilder registryEntries = new StringBuilder();
556 String regName = APP_REGISTRY_NAME.fetchFrom(p);
557 List<Map<String, ? super Object>> fetchFrom =
558 FILE_ASSOCIATIONS.fetchFrom(p);
559 for (int i = 0; i < fetchFrom.size(); i++) {
560 Map<String, ? super Object> fileAssociation = fetchFrom.get(i);
561 String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
562 File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO
563
564 List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
565 String entryName = regName + "File";
566 if (i > 0) {
567 entryName += "." + i;
568 }
569
570 if (extensions == null) {
571 Log.verbose(getString(
572 "message.creating-association-with-null-extension"));
573 } else {
688 .append(APP_NAME.fetchFrom(p))
689 .append("\"\" \"\"%1\"\"\"\r\n");
690 } else {
691 registryEntries.append(
692 "Root: HKCU; Subkey: \"Software\\Classes\\")
693 .append(entryName)
694 .append("\\shell\\open\\command\"; ValueType: " +
695 "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
696 .append(APP_NAME.fetchFrom(p))
697 .append("\"\" \"\"%1\"\"\"\r\n");
698 }
699 }
700 if (registryEntries.length() > 0) {
701 data.put("FILE_ASSOCIATIONS",
702 "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" +
703 registryEntries.toString());
704 } else {
705 data.put("FILE_ASSOCIATIONS", "");
706 }
707
708 String iss = StandardBundlerParam.isRuntimeInstaller(p) ?
709 DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE;
710
711 Writer w = new BufferedWriter(new FileWriter(
712 getConfig_ExeProjectFile(p)));
713
714 String content = preprocessTextResource(
715 getConfig_ExeProjectFile(p).getName(),
716 getString("resource.inno-setup-project-file"),
717 iss, data, VERBOSE.fetchFrom(p),
718 RESOURCE_DIR.fetchFrom(p));
719 w.write(content);
720 w.close();
721 return true;
722 }
723
724 private final static String removeQuotes(String s) {
725 if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) {
726 // special case for '"XXX"' return 'XXX' not '-XXX-'
727 // note '"' and '""' are excluded from this special case
728 s = s.substring(1, s.length() - 1);
|