30 import java.math.BigInteger;
31 import java.text.MessageFormat;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.ResourceBundle;
38
39 import static jdk.jpackage.internal.StandardBundlerParam.*;
40 import static jdk.jpackage.internal.MacBaseInstallerBundler.*;
41 import jdk.jpackage.internal.AbstractAppImageBuilder;
42
43 public class MacAppBundler extends AbstractImageBundler {
44
45 private static final ResourceBundle I18N = ResourceBundle.getBundle(
46 "jdk.jpackage.internal.resources.MacResources");
47
48 private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
49
50 private static Map<String, String> getMacCategories() {
51 Map<String, String> map = new HashMap<>();
52 map.put("Business", "public.app-category.business");
53 map.put("Developer Tools", "public.app-category.developer-tools");
54 map.put("Education", "public.app-category.education");
55 map.put("Entertainment", "public.app-category.entertainment");
56 map.put("Finance", "public.app-category.finance");
57 map.put("Games", "public.app-category.games");
58 map.put("Graphics & Design", "public.app-category.graphics-design");
59 map.put("Healthcare & Fitness",
60 "public.app-category.healthcare-fitness");
61 map.put("Lifestyle", "public.app-category.lifestyle");
62 map.put("Medical", "public.app-category.medical");
63 map.put("Music", "public.app-category.music");
64 map.put("News", "public.app-category.news");
65 map.put("Photography", "public.app-category.photography");
66 map.put("Productivity", "public.app-category.productivity");
67 map.put("Reference", "public.app-category.reference");
68 map.put("Social Networking", "public.app-category.social-networking");
69 map.put("Sports", "public.app-category.sports");
70 map.put("Travel", "public.app-category.travel");
80 map.put("Casino Games", "public.app-category.casino-games");
81 map.put("Dice Games", "public.app-category.dice-games");
82 map.put("Educational Games", "public.app-category.educational-games");
83 map.put("Family Games", "public.app-category.family-games");
84 map.put("Kids Games", "public.app-category.kids-games");
85 map.put("Music Games", "public.app-category.music-games");
86 map.put("Puzzle Games", "public.app-category.puzzle-games");
87 map.put("Racing Games", "public.app-category.racing-games");
88 map.put("Role Playing Games", "public.app-category.role-playing-games");
89 map.put("Simulation Games", "public.app-category.simulation-games");
90 map.put("Sports Games", "public.app-category.sports-games");
91 map.put("Strategy Games", "public.app-category.strategy-games");
92 map.put("Trivia Games", "public.app-category.trivia-games");
93 map.put("Word Games", "public.app-category.word-games");
94
95 return map;
96 }
97
98 public static final EnumeratedBundlerParam<String> MAC_CATEGORY =
99 new EnumeratedBundlerParam<>(
100 I18N.getString("param.category-name"),
101 I18N.getString("param.category-name.description"),
102 Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(),
103 String.class,
104 params -> params.containsKey(CATEGORY.getID())
105 ? CATEGORY.fetchFrom(params)
106 : "Unknown",
107 (s, p) -> s,
108 getMacCategories(),
109 false //strict - for MacStoreBundler this should be strict
110 );
111
112 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
113 new StandardBundlerParam<>(
114 I18N.getString("param.cfbundle-name.name"),
115 I18N.getString("param.cfbundle-name.description"),
116 Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(),
117 String.class,
118 params -> null,
119 (s, p) -> s);
120
121 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
122 new StandardBundlerParam<>(
123 I18N.getString("param.cfbundle-identifier.name"),
124 I18N.getString("param.cfbundle-identifier.description"),
125 Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
126 String.class,
127 IDENTIFIER::fetchFrom,
128 (s, p) -> s);
129
130 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION =
131 new StandardBundlerParam<>(
132 I18N.getString("param.cfbundle-version.name"),
133 I18N.getString("param.cfbundle-version.description"),
134 "mac.CFBundleVersion",
135 String.class,
136 p -> {
137 String s = VERSION.fetchFrom(p);
138 if (validCFBundleVersion(s)) {
139 return s;
140 } else {
141 return "100";
142 }
143 },
144 (s, p) -> s);
145
146 public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
147 new StandardBundlerParam<>(
148 I18N.getString("param.default-icon-icns"),
149 I18N.getString("param.default-icon-icns.description"),
150 ".mac.default.icns",
151 String.class,
152 params -> TEMPLATE_BUNDLE_ICON,
153 (s, p) -> s);
154
155 public static final BundlerParamInfo<String> DEVELOPER_ID_APP_SIGNING_KEY =
156 new StandardBundlerParam<>(
157 I18N.getString("param.signing-key-developer-id-app.name"),
158 I18N.getString("param.signing-key-developer-id-app.description"),
159 "mac.signing-key-developer-id-app",
160 String.class,
161 params -> {
162 String result = MacBaseInstallerBundler.findKey(
163 "Developer ID Application: "
164 + SIGNING_KEY_USER.fetchFrom(params),
165 SIGNING_KEYCHAIN.fetchFrom(params),
166 VERBOSE.fetchFrom(params));
167 if (result != null) {
168 MacCertificate certificate = new MacCertificate(result,
169 VERBOSE.fetchFrom(params));
170
171 if (!certificate.isValid()) {
172 Log.error(MessageFormat.format(I18N.getString(
173 "error.certificate.expired"), result));
174 }
175 }
176
177 return result;
178 },
179 (s, p) -> s);
180
181 public static final BundlerParamInfo<String> BUNDLE_ID_SIGNING_PREFIX =
182 new StandardBundlerParam<>(
183 I18N.getString("param.bundle-id-signing-prefix.name"),
184 I18N.getString("param.bundle-id-signing-prefix.description"),
185 Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(),
186 String.class,
187 params -> IDENTIFIER.fetchFrom(params) + ".",
188 (s, p) -> s);
189
190 public static final BundlerParamInfo<File> ICON_ICNS =
191 new StandardBundlerParam<>(
192 I18N.getString("param.icon-icns.name"),
193 I18N.getString("param.icon-icns.description"),
194 "icon.icns",
195 File.class,
196 params -> {
197 File f = ICON.fetchFrom(params);
198 if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
199 Log.error(MessageFormat.format(
200 I18N.getString("message.icon-not-icns"), f));
201 return null;
202 }
203 return f;
204 },
205 (s, p) -> new File(s));
206
207 public static boolean validCFBundleVersion(String v) {
208 // CFBundleVersion (String - iOS, OS X) specifies the build version
209 // number of the bundle, which identifies an iteration (released or
210 // unreleased) of the bundle. The build version number should be a
211 // string comprised of three non-negative, period-separated integers
212 // with the first integer being greater than zero. The string should
213 // only contain numeric (0-9) and period (.) characters. Leading zeros
289 I18N.getString("error.invalid-cfbundle-version"),
290 I18N.getString("error.invalid-cfbundle-version.advice"));
291 }
292
293 // reject explicitly set sign to true and no valid signature key
294 if (Optional.ofNullable(MacAppImageBuilder.
295 SIGN_BUNDLE.fetchFrom(p)).orElse(Boolean.FALSE)) {
296 String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(p);
297 if (signingIdentity == null) {
298 throw new ConfigException(
299 I18N.getString("error.explicit-sign-no-cert"),
300 I18N.getString("error.explicit-sign-no-cert.advice"));
301 }
302 }
303
304 return true;
305 }
306
307 File doBundle(Map<String, ? super Object> p, File outputDirectory,
308 boolean dependentTask) throws PackagerException {
309 if (RUNTIME_INSTALLER.fetchFrom(p)) {
310 return doJreBundle(p, outputDirectory, dependentTask);
311 } else {
312 return doAppBundle(p, outputDirectory, dependentTask);
313 }
314 }
315
316 File doJreBundle(Map<String, ? super Object> p, File outputDirectory,
317 boolean dependentTask) throws PackagerException {
318 try {
319 File rootDirectory = createRoot(p, outputDirectory, dependentTask,
320 APP_NAME.fetchFrom(p), "macapp-image-builder");
321 AbstractAppImageBuilder appBuilder = new MacAppImageBuilder(p,
322 APP_NAME.fetchFrom(p), outputDirectory.toPath());
323 File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
324 if (predefined == null ) {
325 JLinkBundlerHelper.generateJre(p, appBuilder);
326 } else {
327 return predefined;
328 }
329 return rootDirectory;
330 } catch (PackagerException pe) {
331 throw pe;
332 } catch (Exception ex) {
333 Log.verbose(ex);
334 throw new PackagerException(ex);
335 }
336 }
337
338 File doAppBundle(Map<String, ? super Object> p, File outputDirectory,
339 boolean dependentTask) throws PackagerException {
340 try {
341 File rootDirectory = createRoot(p, outputDirectory, dependentTask,
342 APP_NAME.fetchFrom(p) + ".app", "macapp-image-builder");
343 AbstractAppImageBuilder appBuilder =
344 new MacAppImageBuilder(p, outputDirectory.toPath());
345 if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) {
346 JLinkBundlerHelper.execute(p, appBuilder);
347 } else {
348 StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder);
349 }
350 return rootDirectory;
351 } catch (PackagerException pe) {
352 throw pe;
353 } catch (Exception ex) {
354 Log.verbose(ex);
355 throw new PackagerException(ex);
356 }
357 }
358
359 /////////////////////////////////////////////////////////////////////////
360 // Implement Bundler
361 /////////////////////////////////////////////////////////////////////////
362
|
30 import java.math.BigInteger;
31 import java.text.MessageFormat;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.ResourceBundle;
38
39 import static jdk.jpackage.internal.StandardBundlerParam.*;
40 import static jdk.jpackage.internal.MacBaseInstallerBundler.*;
41 import jdk.jpackage.internal.AbstractAppImageBuilder;
42
43 public class MacAppBundler extends AbstractImageBundler {
44
45 private static final ResourceBundle I18N = ResourceBundle.getBundle(
46 "jdk.jpackage.internal.resources.MacResources");
47
48 private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
49
50 public static Map<String, String> getMacCategories() {
51 Map<String, String> map = new HashMap<>();
52 map.put("Business", "public.app-category.business");
53 map.put("Developer Tools", "public.app-category.developer-tools");
54 map.put("Education", "public.app-category.education");
55 map.put("Entertainment", "public.app-category.entertainment");
56 map.put("Finance", "public.app-category.finance");
57 map.put("Games", "public.app-category.games");
58 map.put("Graphics & Design", "public.app-category.graphics-design");
59 map.put("Healthcare & Fitness",
60 "public.app-category.healthcare-fitness");
61 map.put("Lifestyle", "public.app-category.lifestyle");
62 map.put("Medical", "public.app-category.medical");
63 map.put("Music", "public.app-category.music");
64 map.put("News", "public.app-category.news");
65 map.put("Photography", "public.app-category.photography");
66 map.put("Productivity", "public.app-category.productivity");
67 map.put("Reference", "public.app-category.reference");
68 map.put("Social Networking", "public.app-category.social-networking");
69 map.put("Sports", "public.app-category.sports");
70 map.put("Travel", "public.app-category.travel");
80 map.put("Casino Games", "public.app-category.casino-games");
81 map.put("Dice Games", "public.app-category.dice-games");
82 map.put("Educational Games", "public.app-category.educational-games");
83 map.put("Family Games", "public.app-category.family-games");
84 map.put("Kids Games", "public.app-category.kids-games");
85 map.put("Music Games", "public.app-category.music-games");
86 map.put("Puzzle Games", "public.app-category.puzzle-games");
87 map.put("Racing Games", "public.app-category.racing-games");
88 map.put("Role Playing Games", "public.app-category.role-playing-games");
89 map.put("Simulation Games", "public.app-category.simulation-games");
90 map.put("Sports Games", "public.app-category.sports-games");
91 map.put("Strategy Games", "public.app-category.strategy-games");
92 map.put("Trivia Games", "public.app-category.trivia-games");
93 map.put("Word Games", "public.app-category.word-games");
94
95 return map;
96 }
97
98 public static final EnumeratedBundlerParam<String> MAC_CATEGORY =
99 new EnumeratedBundlerParam<>(
100 Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(),
101 String.class,
102 params -> "Unknown",
103 (s, p) -> s,
104 getMacCategories(),
105 false //strict - for MacStoreBundler this should be strict
106 );
107
108 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
109 new StandardBundlerParam<>(
110 Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(),
111 String.class,
112 params -> null,
113 (s, p) -> s);
114
115 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
116 new StandardBundlerParam<>(
117 Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
118 String.class,
119 IDENTIFIER::fetchFrom,
120 (s, p) -> s);
121
122 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION =
123 new StandardBundlerParam<>(
124 "mac.CFBundleVersion",
125 String.class,
126 p -> {
127 String s = VERSION.fetchFrom(p);
128 if (validCFBundleVersion(s)) {
129 return s;
130 } else {
131 return "100";
132 }
133 },
134 (s, p) -> s);
135
136 public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
137 new StandardBundlerParam<>(
138 ".mac.default.icns",
139 String.class,
140 params -> TEMPLATE_BUNDLE_ICON,
141 (s, p) -> s);
142
143 public static final BundlerParamInfo<String> DEVELOPER_ID_APP_SIGNING_KEY =
144 new StandardBundlerParam<>(
145 "mac.signing-key-developer-id-app",
146 String.class,
147 params -> {
148 String result = MacBaseInstallerBundler.findKey(
149 "Developer ID Application: "
150 + SIGNING_KEY_USER.fetchFrom(params),
151 SIGNING_KEYCHAIN.fetchFrom(params),
152 VERBOSE.fetchFrom(params));
153 if (result != null) {
154 MacCertificate certificate = new MacCertificate(result,
155 VERBOSE.fetchFrom(params));
156
157 if (!certificate.isValid()) {
158 Log.error(MessageFormat.format(I18N.getString(
159 "error.certificate.expired"), result));
160 }
161 }
162
163 return result;
164 },
165 (s, p) -> s);
166
167 public static final BundlerParamInfo<String> BUNDLE_ID_SIGNING_PREFIX =
168 new StandardBundlerParam<>(
169 Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(),
170 String.class,
171 params -> IDENTIFIER.fetchFrom(params) + ".",
172 (s, p) -> s);
173
174 public static final BundlerParamInfo<File> ICON_ICNS =
175 new StandardBundlerParam<>(
176 "icon.icns",
177 File.class,
178 params -> {
179 File f = ICON.fetchFrom(params);
180 if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
181 Log.error(MessageFormat.format(
182 I18N.getString("message.icon-not-icns"), f));
183 return null;
184 }
185 return f;
186 },
187 (s, p) -> new File(s));
188
189 public static boolean validCFBundleVersion(String v) {
190 // CFBundleVersion (String - iOS, OS X) specifies the build version
191 // number of the bundle, which identifies an iteration (released or
192 // unreleased) of the bundle. The build version number should be a
193 // string comprised of three non-negative, period-separated integers
194 // with the first integer being greater than zero. The string should
195 // only contain numeric (0-9) and period (.) characters. Leading zeros
271 I18N.getString("error.invalid-cfbundle-version"),
272 I18N.getString("error.invalid-cfbundle-version.advice"));
273 }
274
275 // reject explicitly set sign to true and no valid signature key
276 if (Optional.ofNullable(MacAppImageBuilder.
277 SIGN_BUNDLE.fetchFrom(p)).orElse(Boolean.FALSE)) {
278 String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(p);
279 if (signingIdentity == null) {
280 throw new ConfigException(
281 I18N.getString("error.explicit-sign-no-cert"),
282 I18N.getString("error.explicit-sign-no-cert.advice"));
283 }
284 }
285
286 return true;
287 }
288
289 File doBundle(Map<String, ? super Object> p, File outputDirectory,
290 boolean dependentTask) throws PackagerException {
291 if (StandardBundlerParam.isRuntimeInstaller(p)) {
292 return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
293 } else {
294 return doAppBundle(p, outputDirectory, dependentTask);
295 }
296 }
297
298 File doAppBundle(Map<String, ? super Object> p, File outputDirectory,
299 boolean dependentTask) throws PackagerException {
300 try {
301 File rootDirectory = createRoot(p, outputDirectory, dependentTask,
302 APP_NAME.fetchFrom(p) + ".app");
303 AbstractAppImageBuilder appBuilder =
304 new MacAppImageBuilder(p, outputDirectory.toPath());
305 if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) {
306 JLinkBundlerHelper.execute(p, appBuilder);
307 } else {
308 StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder);
309 }
310 return rootDirectory;
311 } catch (PackagerException pe) {
312 throw pe;
313 } catch (Exception ex) {
314 Log.verbose(ex);
315 throw new PackagerException(ex);
316 }
317 }
318
319 /////////////////////////////////////////////////////////////////////////
320 // Implement Bundler
321 /////////////////////////////////////////////////////////////////////////
322
|