Skip to content

Commit

Permalink
Mac bundle jre (#138)
Browse files Browse the repository at this point in the history
* added preliminary support for bundling jre in mac app
bundleJVM: true is the property in the jdeploy object of package.json to make this happen.
Addd integration test for this.

* added support for custom jdeployHome.
Added integration test for launcher

* attempt to fix pipeline test

* added better logging for integration test

* more debug logging

* list files in bundles if failed to find bundle in integration test

* attempt to fix launcher integration test

* fixed launcher test on windows

* updated windows launcher with fix for move command

* pinned snapcodejava test to version 2024.7.13 so tests don't break when app is updated

* added tests for bundled JRE and codesigning launcher on mac

---------

Co-authored-by: Steve Hannah <[email protected]>
  • Loading branch information
shannah and Steve Hannah authored Jul 24, 2024
1 parent 4641545 commit 5a5ac67
Show file tree
Hide file tree
Showing 48 changed files with 1,663 additions and 20 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
distribution: 'adopt'
cache: maven
- name: Test Linux
env:
JDEPLOY_TEST_JVM_DOWNLOADS: true
run: bash build_and_test.sh


Expand All @@ -35,6 +37,8 @@ jobs:
cache: maven

- name: Test Mac
env:
JDEPLOY_TEST_JVM_DOWNLOADS: true
run: bash build_and_test.sh


Expand All @@ -51,6 +55,8 @@ jobs:
cache: maven

- name: Build Windows
env:
JDEPLOY_TEST_JVM_DOWNLOADS: true
run: bash build_and_test.sh
shell: bash

Expand Down
92 changes: 90 additions & 2 deletions cli/src/main/java/ca/weblite/jdeploy/JDeploy.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package ca.weblite.jdeploy;

import ca.weblite.jdeploy.app.AppInfo;
import ca.weblite.jdeploy.app.JVMSpecification;
import ca.weblite.jdeploy.appbundler.Bundler;
import ca.weblite.jdeploy.appbundler.BundlerSettings;
import ca.weblite.jdeploy.cli.controllers.CheerpjController;
Expand All @@ -32,6 +33,8 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.*;
import java.util.List;
import java.util.jar.Attributes;
Expand Down Expand Up @@ -78,6 +81,8 @@ public class JDeploy {
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
}

private static final int DEFAULT_JAVA_VERSION = 11;

private static final String BUNDLE_MAC_X64 = "mac-x64";
private static final String BUNDLE_MAC_ARM64 = "mac-arm64";
private static final String BUNDLE_WIN = "win";
Expand Down Expand Up @@ -266,7 +271,18 @@ public Map getPackageJsonMap() {
if (packageJsonMap == null) {
try {
JSONParser p = new JSONParser();
packageJsonMap = (Map)p.parseJSON(new StringReader(FileUtils.readFileToString(getPackageJsonFile(), "UTF-8")));
packageJsonMap = (Map)p.parseJSON(
new StringReader(
FileUtils.readFileToString(getPackageJsonFile(),
"UTF-8"
)
)
);
if (packageJsonMap.containsKey("jdeploy")) {
((Map)packageJsonMap.get("jdeploy")).putAll(getJdeployConfigOverrides());
} else {
packageJsonMap.put("jdeploy", getJdeployConfigOverrides());
}
} catch (IOException ex) {
Logger.getLogger(JDeploy.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
Expand All @@ -275,6 +291,24 @@ public Map getPackageJsonMap() {
return packageJsonMap;
}

private Map<String,?> getJdeployConfigOverrides() {
Map<String,?> overrides = new HashMap<String,Object>();
if (System.getenv("JDEPLOY_CONFIG") != null) {
System.out.println("Found JDEPLOY_CONFIG environment variable");
System.out.println("Injecting jdeploy config overrides from environment variable");
System.out.println(System.getenv("JDEPLOY_CONFIG"));
try {
JSONParser p = new JSONParser();
Map m = (Map)p.parseJSON(new StringReader(System.getenv("JDEPLOY_CONFIG")));
overrides.putAll(m);
} catch (IOException ex) {
Logger.getLogger(JDeploy.class.getName()).log(Level.SEVERE, null, ex);
}
}

return overrides;
}

/**
* Get package.json root object as a map.
*/
Expand Down Expand Up @@ -1153,7 +1187,7 @@ public void bundleSplash() throws IOException {
}

public String processJdeployTemplate(String jdeployContents) {
jdeployContents = jdeployContents.replace("{{JAVA_VERSION}}", String.valueOf(getJavaVersion(11)));
jdeployContents = jdeployContents.replace("{{JAVA_VERSION}}", String.valueOf(getJavaVersion(DEFAULT_JAVA_VERSION)));
jdeployContents = jdeployContents.replace("{{PORT}}", String.valueOf(getPort(0)));
if (getWar(null) != null) {
jdeployContents = jdeployContents.replace("{{WAR_PATH}}", new File(getWar(null)).getName());
Expand Down Expand Up @@ -1495,6 +1529,29 @@ private void loadAppInfo(AppInfo appInfo) throws IOException {
appInfo.setCodeSignSettings(AppInfo.CodeSignSettings.CodeSign);
}

if (rj().getAsBoolean("bundleJVM")) {
appInfo.setUseBundledJVM(true);
JVMSpecification jvmSpec = new JVMSpecification();
jvmSpec.javaVersion = getJavaVersion(DEFAULT_JAVA_VERSION);
jvmSpec.javafx = "true".equals(getString("javafx", "false"));
jvmSpec.jdk = "true".equals(getString("jdk", "false"));
appInfo.setJVMSpecification(jvmSpec);
}

if (mj().get("jdeployHome") != null) {
System.out.println("Setting jdeployHome to "+rj().getAsString("jdeployHome"));
appInfo.setJdeployHome(rj().getAsString("jdeployHome"));
}
if (mj().get("jdeployHomeLinux") != null) {
appInfo.setLinuxJdeployHome(rj().getAsString("jdeployHomeLinux"));
}
if (mj().get("jdeployHomeMac") != null) {
appInfo.setMacJdeployHome(rj().getAsString("jdeployHomeMac"));
}
if (mj().get("jdeployHomeWindows") != null) {
appInfo.setWindowsJdeployHome(rj().getAsString("jdeployHomeWindows"));
}

String jarPath = getString("jar", null);
if (jarPath != null) {
JarFile jarFile = new JarFile(new File(directory, toNativePath(jarPath)));
Expand Down Expand Up @@ -2434,11 +2491,23 @@ public static void main(String[] args) {
File jDeployDir = new File("jdeploy");
if (jDeployDir.exists()) {
System.out.println("Deleting "+jDeployDir);
try {
makeWritable(jDeployDir.toPath());
} catch (IOException ex) {
System.err.println("Failed to make "+jDeployDir+" writable. "+ex.getMessage());
ex.printStackTrace();
}
FileUtils.deleteDirectory(jDeployDir);
}
jDeployDir = new File("jdeploy-bundle");
if (jDeployDir.exists()) {
System.out.println("Deleting "+jDeployDir);
try {
makeWritable(jDeployDir.toPath());
} catch (IOException ex) {
System.err.println("Failed to make "+jDeployDir+" writable. "+ex.getMessage());
ex.printStackTrace();
}
FileUtils.deleteDirectory(jDeployDir);
}
if (args.length > 1) {
Expand Down Expand Up @@ -2498,6 +2567,25 @@ public static void main(String[] args) {
}
}

private static void makeWritable(Path path) throws IOException {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Set<PosixFilePermission> perms = EnumSet.allOf(PosixFilePermission.class);
Files.setPosixFilePermissions(file, perms);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Set<PosixFilePermission> perms = EnumSet.allOf(PosixFilePermission.class);
Files.setPosixFilePermissions(dir, perms);
dir.toFile().setWritable(true, false);
return FileVisitResult.CONTINUE;
}
});
}

private void githubInit(String[] githubInitArgs) {
GitHubRepositoryInitializerCLIController controller = new GitHubRepositoryInitializerCLIController(getPackageJsonFile(), githubInitArgs);
controller.run();
Expand Down
2 changes: 1 addition & 1 deletion installer/tests/snapcodejava/.jdeploy-files/app.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<app
package="snapcodejava"
version="latest"
version="2024.7.13"
title="Snapcode Java"
fork="false"
/>
6 changes: 6 additions & 0 deletions shared/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@
<artifactId>feather</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>

</dependencies>
<repositories>
Expand Down
80 changes: 79 additions & 1 deletion shared/src/main/java/ca/weblite/jdeploy/app/AppInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ public class AppInfo {
private String windowsInstallerUrl;
private String linuxInstallerUrl;
private String githubRepositoryUrl;

private String macJdeployHome;

private String windowsJdeployHome;

private String linuxJdeployHome;

private String jdeployHome;
private String tagLine;
private String title;
private String description;
Expand All @@ -51,8 +59,20 @@ public class AppInfo {

private Set<String> urlSchemes;

/**
* Indicates that the app should use a dedicated JVM rather than the default shared JVM.
* This is required for Java 8 on Windows (at least) since Java 8 didn't provide a way to
* use a JRE at an arbitrary location. This is no longer necessary in Java 9+, but we may
* want to employ its use for other reasons.
*/
private boolean usePrivateJVM = false;

/**
* Indicates that the build should include an embedded JRE as part of the app bundle.
*/
private boolean useBundledJVM = false;

private JVMSpecification jvmSpecification;

public void addUrlScheme(String scheme) {
if (urlSchemes == null) urlSchemes = new HashSet<>();
Expand Down Expand Up @@ -163,6 +183,38 @@ public void setMacAppBundleId(String macAppBundleId) {
}
}

public void setMacJdeployHome(String macJdeployHome) {
this.macJdeployHome = macJdeployHome;
}

public String getMacJdeployHome() {
return macJdeployHome;
}

public void setWindowsJdeployHome(String windowsJdeployHome) {
this.windowsJdeployHome = windowsJdeployHome;
}

public String getWindowsJdeployHome() {
return windowsJdeployHome;
}

public void setLinuxJdeployHome(String linuxJdeployHome) {
this.linuxJdeployHome = linuxJdeployHome;
}

public String getLinuxJdeployHome() {
return linuxJdeployHome;
}

public void setJdeployHome(String jdeployHome) {
this.jdeployHome = jdeployHome;
}

public String getJdeployHome() {
return jdeployHome;
}

public CodeSignSettings getCodeSignSettings() {
return codeSignSettings;
}
Expand Down Expand Up @@ -320,6 +372,21 @@ public void setUsePrivateJVM(boolean usePrivateJVM) {
this.usePrivateJVM = usePrivateJVM;
}

public boolean isUseBundledJVM() {
return useBundledJVM;
}

public void setUseBundledJVM(boolean useBundledJVM) {
this.useBundledJVM = useBundledJVM;
}

public void setJVMSpecification(JVMSpecification spec) {
jvmSpecification = spec;
}

public JVMSpecification getJVMSpecification() {
return jvmSpecification;
}

public static enum Updates {
Auto,
Expand Down Expand Up @@ -1192,6 +1259,8 @@ public AppInfo copy() {
out.setWindowsInstallerUrl(getWindowsInstallerUrl());
out.setLinuxAppUrl(getLinuxAppUrl());
out.setLinuxInstallerUrl(getLinuxInstallerUrl());
out.setUsePrivateJVM(isUsePrivateJVM());
out.setUseBundledJVM(isUseBundledJVM());
out.codeSignSettings = codeSignSettings;
out.macAppBundleId = macAppBundleId;
if (permissions != null) {
Expand Down Expand Up @@ -1228,6 +1297,11 @@ public AppInfo copy() {
}
}

out.jdeployHome = jdeployHome;
out.macJdeployHome = macJdeployHome;
out.windowsJdeployHome = windowsJdeployHome;
out.linuxJdeployHome = linuxJdeployHome;

return out;
}

Expand Down Expand Up @@ -1325,7 +1399,11 @@ private boolean equalsImpl(AppInfo o) {
macAppBundleId, o.macAppBundleId,
npmPackage, o.npmPackage,
npmVersion, o.npmVersion,
npmAllowPrerelease, o.npmAllowPrerelease
npmAllowPrerelease, o.npmAllowPrerelease,
jdeployHome, o.jdeployHome,
macJdeployHome, o.macJdeployHome,
windowsJdeployHome, o.windowsJdeployHome,
linuxJdeployHome, o.linuxJdeployHome,

});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ca.weblite.jdeploy.app;

public class JVMSpecification {
public int javaVersion;
public boolean jdk;
public boolean javafx;
}
Loading

0 comments on commit 5a5ac67

Please sign in to comment.