bisq/desktop/package/package.gradle
cd2357 0a1b581d32
Remove --runtime-image option using separate JDK
Remove the --runtime-image jpackage option, which was packaging Java 11 in the resulting installers. Reason is that jpackage 15 does not properly package the Java 11 runtime, but contains necessary features for signing and notarizing the app. On the other hand, jpackage 14 does package the Java 11 runtime correctly in the resulting installers, but does not support proper signing and notarizing.
2020-10-08 13:29:40 +02:00

287 lines
13 KiB
Groovy

import java.time.LocalDateTime
import org.apache.tools.ant.taskdefs.condition.Os
import static groovy.io.FileType.*
task jpackageSanityChecks {
description 'Interactive sanity checks on the version of the code that will be packaged'
doLast {
executeCmd("git --no-pager log -5 --oneline")
ant.input(message: "Above you see the current HEAD and its recent history.\n" +
"Is this the right commit for packaging? (y=continue, n=abort)",
addproperty: "sanity-check-1",
validargs: "y,n")
if (ant.properties['sanity-check-1'] == 'n') {
ant.fail('Aborting')
}
executeCmd("git status --short --branch")
ant.input(message: "Above you see any local changes that are not in the remote branch.\n" +
"If you have any local changes, please abort, get them merged, get the latest branch and try again.\n" +
"Continue with packaging? (y=continue, n=abort)",
addproperty: "sanity-check-2",
validargs: "y,n")
if (ant.properties['sanity-check-2'] == 'n') {
ant.fail('Aborting')
}
// TODO Evtl check programmatically in gradle (i.e. fail if below v11)
executeCmd("java --version")
ant.input(message: "Above you see the installed java version, which will be used to compile and build Bisq.\n" +
"Is this java version ok for that? (y=continue, n=abort)",
addproperty: "sanity-check-3",
validargs: "y,n")
if (ant.properties['sanity-check-3'] == 'n') {
ant.fail('Aborting')
}
}
}
task getJavaBinariesDownloadURLs {
description 'Find out which JDK will be used for jpackage and prepare to download it'
dependsOn 'jpackageSanityChecks'
doLast {
// The build directory will be deleted next time the clean task runs
// Therefore, we can use it to store any temp files (separate JDK for jpackage, etc) and resulting build artefacts
// We create a temp folder in the build directory which holds all jpackage-related artefacts (not just the final installers)
String tempRootDirName = 'temp-' + LocalDateTime.now().getNano()
File tempRootDir = new File(project.buildDir, tempRootDirName)
tempRootDir.mkdirs()
ext.tempRootDir = tempRootDir
println "Created temp root folder " + tempRootDir
File binariesFolderPath = new File(tempRootDir, "binaries")
binariesFolderPath.mkdirs();
ext.binariesFolderPath = binariesFolderPath
// TODO Extend script logic to alternatively allow a local (separate, v14+) JDK for jpackage
// TODO Another option is to use the local JDK for everything: build jars and use jpackage (but then it has to be v14+)
// It seems only the latest version of each JDK is available for download
// Therefore, if at any future AdoptOpenJDK release these download links won't work, simply update them
// See https://adoptopenjdk.net/releases.html?variant=openjdk15&jvmVariant=hotspot for latest download URLs
// The links must be for: x64 + JDK + zip (for windows) or tar.gz (for mac/linux)
Map jdk15DownloadURLs = [
'linux' : 'https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15%2B36/OpenJDK15U-jdk_x64_linux_hotspot_15_36.tar.gz',
'mac' : 'https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15%2B36/OpenJDK15U-jdk_x64_mac_hotspot_15_36.tar.gz',
'windows' : 'https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15%2B36/OpenJDK15U-jdk_x64_windows_hotspot_15_36.zip'
// TODO For some reason, using "--runtime-image jdk-11" does NOT work with a v15 jpackage, but works with v14
// v14
// 'linux' : 'https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.2%2B12/OpenJDK14U-jdk_x64_linux_hotspot_14.0.2_12.tar.gz',
// 'mac' : 'https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.2%2B12/OpenJDK14U-jdk_x64_mac_hotspot_14.0.2_12.tar.gz',
// 'windows' : 'https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.2%2B12/OpenJDK14U-jdk_x64_windows_hotspot_14.0.2_12.zip'
]
String osKey
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
osKey = 'windows'
} else if (Os.isFamily(Os.FAMILY_MAC)) {
osKey = 'mac'
} else {
osKey = 'linux'
}
ext.jdk15DownloadURL = jdk15DownloadURLs[osKey]
// The download URLs can be manually overwritten here, if necessary
// ext.jdk15DownloadURL = ...
}
}
task retrieveAndExtractJavaBinaries {
description 'Retrieve necessary Java binaries and extract them'
dependsOn 'getJavaBinariesDownloadURLs'
doLast {
File tempRootDir = getJavaBinariesDownloadURLs.property("tempRootDir")
// Folder where the jpackage JDK archive will be downloaded and extracted
String jdkForJpackageDirName = "jdk-jpackage"
File jdkForJpackageDir = new File(tempRootDir, jdkForJpackageDirName)
jdkForJpackageDir.mkdirs();
String jdkForJpackageArchiveURL = getJavaBinariesDownloadURLs.property('jdk15DownloadURL')
String jdkForJpackageArchiveFileName = jdkForJpackageArchiveURL.tokenize('/').last()
File jdkForJpackageFile = new File(jdkForJpackageDir, jdkForJpackageArchiveFileName)
// Download necessary JDK binaries
ext.downloadArchive(jdkForJpackageArchiveURL, jdkForJpackageFile)
// Extract them
String jpackageBinaryFileName
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
ext.extractArchiveZip(jdkForJpackageFile, jdkForJpackageDir)
jpackageBinaryFileName = 'jpackage.exe'
} else {
ext.extractArchiveTarGz(jdkForJpackageFile, jdkForJpackageDir)
jpackageBinaryFileName = 'jpackage'
}
// Find jpackage in the newly extracted JDK
// Don't rely on hardcoded paths to reach it, because the path depends on the version and platform
jdkForJpackageDir.traverse(type: FILES, nameFilter: jpackageBinaryFileName) {
println 'Using jpackage binary from ' + it
ext.jpackageFilePath = it.path
}
}
ext.downloadArchive = {String archiveURL, File destinationArchiveFile ->
println "Downloading ${archiveURL}"
ant.get(src: archiveURL, dest: destinationArchiveFile)
println 'Download saved to ' + destinationArchiveFile
}
ext.extractArchiveTarGz = {File tarGzFile, File destinationDir ->
println "Extracting tar.gz ${tarGzFile}"
// Gradle's tar extraction preserves permissions (crucial for jpackage to function correctly)
copy {
from tarTree(resources.gzip(tarGzFile))
into destinationDir
}
println "Extracted to ${destinationDir}"
}
ext.extractArchiveZip = {File zipFile, File destinationDir ->
println "Extracting zip ${zipFile}..."
ant.unzip(src: zipFile, dest: destinationDir)
println "Extracted to ${destinationDir}"
}
}
task packageInstallers {
description 'Call jpackage to prepare platform-specific binaries for this platform'
dependsOn 'retrieveAndExtractJavaBinaries'
// Clean all previous artefacts and create a fresh shadowJar for the installers
dependsOn rootProject.clean
dependsOn ':desktop:shadowJar'
doLast {
String jPackageFilePath = retrieveAndExtractJavaBinaries.property('jpackageFilePath')
String licenseFilePath = "${rootProject.projectDir}/LICENSE"
File binariesFolderPath = file(getJavaBinariesDownloadURLs.property('binariesFolderPath'))
File tempRootDir = getJavaBinariesDownloadURLs.property("tempRootDir")
// The jpackateTempDir stores temp files used by jpackage for building the installers
// It can be inspected in order to troubleshoot the packaging process
File jpackateTempDir = new File(tempRootDir, "jpackage-temp")
jpackateTempDir.mkdirs();
// ALL contents of this folder will be included in the resulting installers
// However, the fat jar is the only one we need
// Therefore, this location should point to a folder that ONLY contains the fat jar
// If later we will need to include other non-jar resources, we can do that by adding --resource-dir to the jpackage opts
String fatJarFolderPath = "${project(':desktop').buildDir}/libs"
String mainJarName = file(fatJarFolderPath).list()[0]
// TODO For non-modular applications: use jlink to create a custom runtime containing only the modules required
// See jpackager argument documentation:
// https://docs.oracle.com/en/java/javase/15/docs/specs/man/jpackage.html
// For mac, valid versions are one to three integers separated by dot
// TODO Special handling for mac? Or always remove -SNAPSHOT suffix?
String appVersion = '1.1.1'
println "Packaging Bisq version ${appVersion}"
String commonOpts = (
// Generic options
" --dest \"${binariesFolderPath}\"" +
" --name Bisq" +
" --description \"A decentralized bitcoin exchange network.\"" +
" --app-version ${appVersion}" +
" --copyright \"© 2020 Bisq\"" +
" --vendor Bisq" +
" --temp \"${jpackateTempDir}\"" +
// Options for creating the application image
" --input ${fatJarFolderPath}" +
// Options for creating the application launcher
" --main-jar ${mainJarName}" +
" --main-class bisq.desktop.app.BisqAppMain" +
" --java-options -Xss1280k" +
" --java-options -XX:MaxRAM=4g" +
" --java-options -Djava.net.preferIPv4Stack=true" +
// Warning: this will cause guice reflection exceptions and lead to issues with the guice internal cache
// resulting in the UI not loading
// " --java-options -Djdk.module.illegalAccess=deny" +
// Options for creating the application package
" --license-file \"${licenseFilePath}\""
)
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
String windowsOpts = (
" --icon ${project(':desktop').projectDir}/package/windows/Bisq.ico" +
" --win-per-user-install" +
" --win-menu" +
" --win-shortcut"
)
// TODO How does the current package/windows/Bisq.iss play into this?
// TODO Do msi or exe? Or both? For pros and cons see https://stackoverflow.com/a/1925819
executeCmd(jPackageFilePath + commonOpts + windowsOpts + " --type exe")
executeCmd(jPackageFilePath + commonOpts + windowsOpts + " --type msi")
} else if (Os.isFamily(Os.FAMILY_MAC)) {
// TODO Include signing args, like --mac-sign / -mac-signing-keychain / --mac-signing-key-user-name
// See https://docs.oracle.com/en/java/javase/14/jpackage/override-jpackage-resources.html
// for details of "--resource-dir"
String macOpts = (
" --resource-dir \"${project(':desktop').projectDir}/package/macosx\""
)
executeCmd(jPackageFilePath + commonOpts + macOpts + " --type dmg")
} else {
String linuxOpts = (
" --icon ${project(':desktop').projectDir}/package/linux/icon.png" +
" --linux-package-name bisq-build-test" +
" --linux-app-release ${appVersion}" +
" --linux-menu-group Network" +
" --linux-shortcut"
)
executeCmd(jPackageFilePath + commonOpts + linuxOpts +
" --linux-deb-maintainer contact@bisq.network" +
" --type deb")
executeCmd(jPackageFilePath + commonOpts + linuxOpts +
" --linux-rpm-license-type AGPLv3" + // https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
" --type rpm")
}
println "The binaries are ready:"
binariesFolderPath.traverse {
println it.path
}
}
}
def executeCmd(String cmd) {
String shell
String shellArg
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
shell = 'cmd'
shellArg = '/c'
} else {
shell = 'bash'
shellArg = '-c'
}
println "Executing command:\n${cmd}\n"
// See "Executing External Processes" section of
// http://docs.groovy-lang.org/next/html/documentation/
def commands = [shell, shellArg, cmd]
def process = commands.execute(null, project.rootDir)
if (process.waitFor() == 0) {
println "Command output (stdout):\n${process.text}"
} else {
println "Command output (stderr):\n${process.err.text}"
}
}