Skip to content

Commit

Permalink
8264057: [redo] JDK-8248904: Add support to jpackage for the Mac App …
Browse files Browse the repository at this point in the history
…Store.

Co-authored-by: Erwin Morrhey <erwin.morrhey@gluonhq.com>
Reviewed-by: kcr, asemenyuk
  • Loading branch information
Andy Herrick and Erwin Morrhey committed Mar 24, 2021
1 parent 57c3f27 commit deda80f
Show file tree
Hide file tree
Showing 22 changed files with 269 additions and 105 deletions.
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
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
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
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
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

0 comments on commit deda80f

Please sign in to comment.