Skip to content
This repository has been archived by the owner on Aug 27, 2022. It is now read-only.
/ lanai Public archive

Commit

Permalink
8248904: Add support to jpackage for the Mac App Store
Browse files Browse the repository at this point in the history
Reviewed-by: asemenyuk, almatvee, kizune, kcr
  • Loading branch information
Andy Herrick committed Mar 16, 2021
1 parent dc93138 commit 11c8c78
Show file tree
Hide file tree
Showing 23 changed files with 269 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
},
(s, p) -> Path.of(s));

final static String DEFAULT_ICON = "java32.png";
final static String DEFAULT_ICON = "JavaApp.png";

LinuxAppImageBuilder(Path imageOutDir) {
super(imageOutDir);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Optional;
import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER;
import static jdk.jpackage.internal.MacAppImageBuilder.APP_STORE;
import static jdk.jpackage.internal.StandardBundlerParam.MAIN_CLASS;
import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
Expand All @@ -41,7 +42,7 @@ public MacAppBundler() {
setParamsValidator(MacAppBundler::doValidate);
}

private static final String TEMPLATE_BUNDLE_ICON = "java.icns";
private static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns";

public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
new StandardBundlerParam<>(
Expand All @@ -62,11 +63,20 @@ public MacAppBundler() {
"mac.signing-key-developer-id-app",
String.class,
params -> {
String result = MacBaseInstallerBundler.findKey(
"Developer ID Application: ",
SIGNING_KEY_USER.fetchFrom(params),
SIGNING_KEYCHAIN.fetchFrom(params),
VERBOSE.fetchFrom(params));
String user = SIGNING_KEY_USER.fetchFrom(params);
String keychain = SIGNING_KEYCHAIN.fetchFrom(params);
String result = null;
if (APP_STORE.fetchFrom(params)) {
result = MacBaseInstallerBundler.findKey(
"3rd Party Mac Developer Application: ",
user, keychain);
}
// if either not signing for app store or couldn't find
if (result == null) {
result = MacBaseInstallerBundler.findKey(
"Developer ID Application: ", user, keychain);
}

if (result != null) {
MacCertificate certificate = new MacCertificate(result);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
private static final ResourceBundle I18N = ResourceBundle.getBundle(
"jdk.jpackage.internal.resources.MacResources");

private static final String TEMPLATE_BUNDLE_ICON = "java.icns";
private static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns";
private static final String OS_TYPE_CODE = "APPL";
private static final String TEMPLATE_INFO_PLIST_LITE =
"Info-lite.plist.template";
Expand Down Expand Up @@ -102,6 +102,13 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
params -> null,
(s, p) -> s);

public static final BundlerParamInfo<String> APP_CATEGORY =
new StandardBundlerParam<>(
Arguments.CLIOptions.MAC_CATEGORY.getId(),
String.class,
params -> "utilities",
(s, p) -> s);

public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
new StandardBundlerParam<>(
Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
Expand Down Expand Up @@ -146,6 +153,38 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
null : Boolean.valueOf(s)
);

public static final StandardBundlerParam<Boolean> APP_STORE =
new StandardBundlerParam<>(
Arguments.CLIOptions.MAC_APP_STORE.getId(),
Boolean.class,
params -> false,
// valueOf(null) is false, we actually do want null in some cases
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
null : Boolean.valueOf(s)
);

public static final BundlerParamInfo<Path> ENTITLEMENTS =
new StandardBundlerParam<>(
Arguments.CLIOptions.MAC_ENTITLEMENTS.getId(),
Path.class,
params -> {
try {
Path path = CONFIG_ROOT.fetchFrom(params).resolve(
getLauncherName(params) + ".entitlements");
String defPath = (APP_STORE.fetchFrom(params) ?
"sandbox.plist" : "entitlements.plist");
createResource(defPath, params)
.setCategory(I18N.getString("resource.entitlements"))
.saveToFile(path);
return path;
} catch (IOException ioe) {
Log.verbose(ioe);
}
return null;
},
(s, p) -> Path.of(s)
);

private static final StandardBundlerParam<String> FA_MAC_CFBUNDLETYPEROLE =
new StandardBundlerParam<>(
Arguments.MAC_CFBUNDLETYPEROLE,
Expand Down Expand Up @@ -336,27 +375,14 @@ private void sign(Map<String, ? super Object> params) throws IOException {
String signingIdentity =
DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params);
if (signingIdentity != null) {
prepareEntitlements(params);
signAppBundle(params, root, signingIdentity,
BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params),
getConfig_Entitlements(params));
ENTITLEMENTS.fetchFrom(params));
}
restoreKeychainList(params);
}
}

static Path getConfig_Entitlements(Map<String, ? super Object> params) {
return CONFIG_ROOT.fetchFrom(params).resolve(
getLauncherName(params) + ".entitlements");
}

static void prepareEntitlements(Map<String, ? super Object> params)
throws IOException {
createResource("entitlements.plist", params)
.setCategory(I18N.getString("resource.entitlements"))
.saveToFile(getConfig_Entitlements(params));
}

private static String getLauncherName(Map<String, ? super Object> params) {
return APP_NAME.fetchFrom(params);
}
Expand Down Expand Up @@ -391,8 +417,17 @@ private void writeRuntimeInfoPlist(Path file,
String name = StandardBundlerParam.isRuntimeInstaller(params) ?
getBundleName(params): "Java Runtime Image";
data.put("CF_BUNDLE_NAME", name);
data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params));
data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params));
String ver = VERSION.fetchFrom(params);
String sver = ver;
int index = ver.indexOf(".");
if (index > 0 && ((index + 1) < ver.length())) {
index = ver.indexOf(".", index + 1);
if (index > 0 ) {
sver = ver.substring(0, index);
}
}
data.put("CF_BUNDLE_VERSION", ver);
data.put("CF_BUNDLE_SHORT_VERSION_STRING", sver);

createResource(TEMPLATE_RUNTIME_INFO_PLIST, params)
.setPublicName("Runtime-Info.plist")
Expand Down Expand Up @@ -443,6 +478,8 @@ private void writeInfoPlist(Path file, Map<String, ? super Object> params)
data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params));
data.put("DEPLOY_BUNDLE_SHORT_VERSION", VERSION.fetchFrom(params));
data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION", VERSION.fetchFrom(params));
data.put("DEPLOY_APP_CATEGORY", "public.app-category." +
APP_CATEGORY.fetchFrom(params));

StringBuilder bundleDocumentTypes = new StringBuilder();
StringBuilder exportedTypes = new StringBuilder();
Expand Down Expand Up @@ -643,9 +680,8 @@ static void signAppBundle(
}
}).filter(p -> Files.isRegularFile(p) &&
(Files.isExecutable(p) || p.toString().endsWith(".dylib"))
&& !(p.toString().endsWith(appExecutable)
|| p.toString().contains("/Contents/runtime")
|| p.toString().contains("/Contents/Frameworks"))
&& !(p.toString().contains("dylib.dSYM/Contents"))
&& !(p.toString().endsWith(appExecutable))
).forEach(p -> {
// noinspection ThrowableResultOfMethodCallIgnored
if (toThrow.get() != null) return;
Expand All @@ -654,12 +690,30 @@ static void signAppBundle(
if (Files.isSymbolicLink(p)) {
Log.verbose(MessageFormat.format(I18N.getString(
"message.ignoring.symlink"), p.toString()));
} else if (isFileSigned(p)) {
// executable or lib already signed
Log.verbose(MessageFormat.format(I18N.getString(
"message.already.signed"), p.toString()));
} else {
List<String> args = new ArrayList<>();
List<String> args;
// runtime and Framework files will be signed below
// but they need to be unsigned first here
if ((p.toString().contains("/Contents/runtime")) ||
(p.toString().contains("/Contents/Frameworks"))) {

args = new ArrayList<>();
args.addAll(Arrays.asList("/usr/bin/codesign",
"--remove-signature", p.toString()));
try {
Set<PosixFilePermission> oldPermissions =
Files.getPosixFilePermissions(p);
p.toFile().setWritable(true, true);
ProcessBuilder pb = new ProcessBuilder(args);
IOUtils.exec(pb);
Files.setPosixFilePermissions(p,oldPermissions);
} catch (IOException ioe) {
Log.verbose(ioe);
toThrow.set(ioe);
return;
}
}
args = new ArrayList<>();
args.addAll(Arrays.asList("/usr/bin/codesign",
"--timestamp",
"--options", "runtime",
Expand All @@ -670,25 +724,19 @@ static void signAppBundle(
args.add("--keychain");
args.add(keyChain);
}

if (Files.isExecutable(p)) {
if (entitlements != null) {
args.add("--entitlements");
args.add(entitlements.toString());
}
}

args.add(p.toString());

try {
Set<PosixFilePermission> oldPermissions =
Files.getPosixFilePermissions(p);
p.toFile().setWritable(true, true);

ProcessBuilder pb = new ProcessBuilder(args);

IOUtils.exec(pb);

Files.setPosixFilePermissions(p, oldPermissions);
} catch (IOException ioe) {
toThrow.set(ioe);
Expand Down Expand Up @@ -721,6 +769,10 @@ static void signAppBundle(
args.add("--keychain");
args.add(keyChain);
}
if (entitlements != null) {
args.add("--entitlements");
args.add(entitlements.toString());
}
args.add(path.toString());
ProcessBuilder pb = new ProcessBuilder(args);

Expand Down Expand Up @@ -758,6 +810,7 @@ static void signAppBundle(
"--options", "runtime",
"--force",
"-s", signingIdentity,
"--prefix", identifierPrefix,
"-vvvv"));

if (keyChain != null && !keyChain.isEmpty()) {
Expand All @@ -778,20 +831,6 @@ static void signAppBundle(
IOUtils.exec(pb);
}

private static boolean isFileSigned(Path file) {
ProcessBuilder pb =
new ProcessBuilder("/usr/bin/codesign",
"--verify", file.toString());

try {
IOUtils.exec(pb);
} catch (IOException ex) {
return false;
}

return true;
}

private static String extractBundleIdentifier(Map<String, Object> params) {
if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,13 @@ public String getBundleType() {
return "INSTALLER";
}

public static String findKey(String keyPrefix, String teamName, String keychainName,
boolean verbose) {
String key = (teamName.startsWith(keyPrefix)
|| teamName.startsWith("3rd Party Mac Developer"))
? teamName : (keyPrefix + teamName);
if (Platform.getPlatform() != Platform.MAC) {
return null;
}
public static String findKey(String keyPrefix, String teamName, String keychainName) {

boolean useAsIs = teamName.startsWith(keyPrefix)
|| teamName.startsWith("Developer ID")
|| teamName.startsWith("3rd Party Mac");

String key = (useAsIs) ? teamName : (keyPrefix + teamName);

try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class MacDmgBundler extends MacBaseInstallerBundler {
static final String BACKGROUND_IMAGE_FOLDER =".background";
static final String BACKGROUND_IMAGE = "background.tiff";
static final String DEFAULT_DMG_SETUP_SCRIPT = "DMGsetup.scpt";
static final String TEMPLATE_BUNDLE_ICON = "java.icns";
static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns";

static final String DEFAULT_LICENSE_PLIST="lic_template.plist";

Expand Down
Loading

0 comments on commit 11c8c78

Please sign in to comment.