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

1 comment on commit deda80f

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.