Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Packaging Plugin #6857

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
java: [ '11', '11.0.3', '15', '15.0.5']
java: ['11']
name: Test Java ${{ matrix.Java }}, ${{ matrix.os }}
steps:
- uses: actions/checkout@v4.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ repositories {
maven { url "https://jitpack.io" }
}

sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
vendor = JvmVendorSpec.AZUL
implementation = JvmImplementation.VENDOR_SPECIFIC
}
}

tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
package bisq.gradle.packaging

import bisq.gradle.packaging.jpackage.JPackageAppConfig
import bisq.gradle.packaging.jpackage.JPackageConfig
import bisq.gradle.packaging.jpackage.PackageFactory
import bisq.gradle.packaging.jpackage.package_formats.JPackagePackageFormatConfigs
import bisq.gradle.packaging.jpackage.package_formats.LinuxPackages
import bisq.gradle.packaging.jpackage.package_formats.MacPackage
import bisq.gradle.packaging.jpackage.package_formats.WindowsPackage
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.util.concurrent.TimeUnit
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.*
import java.io.File

abstract class JPackageTask : DefaultTask() {

@get:InputDirectory
abstract val jdkDirectory: DirectoryProperty

@get:InputDirectory
abstract val jarsDirectory: DirectoryProperty
abstract val distDirFile: Property<File>

@get:Input
abstract val mainJar: Property<String>
@get:InputFile
abstract val mainJarFile: RegularFileProperty

@get:Input
abstract val mainClassName: Property<String>

@get:Input
abstract val jvmArgs: SetProperty<String>

@get:Input
abstract val appVersion: Property<String>

@get:InputDirectory
abstract val packageResourcesDir: DirectoryProperty

@get:InputDirectory
abstract val runtimeImageDirectory: DirectoryProperty

Expand All @@ -32,20 +47,43 @@ abstract class JPackageTask : DefaultTask() {
@TaskAction
fun run() {
val jPackagePath = jdkDirectory.asFile.get().toPath().resolve("bin").resolve("jpackage")
val processBuilder = ProcessBuilder(
jPackagePath.toAbsolutePath().toString(),
"--dest", outputDirectory.asFile.get().absolutePath,
"--name", "Bisq",

"--input", jarsDirectory.asFile.get().absolutePath,
"--main-jar", mainJar.get(),
"--main-class", mainClassName.get(),
val jPackageConfig = JPackageConfig(
inputDirPath = distDirFile.get().toPath().resolve("lib"),
outputDirPath = outputDirectory.asFile.get().toPath(),
runtimeImageDirPath = runtimeImageDirectory.asFile.get().toPath(),

appConfig = JPackageAppConfig(
appVersion = appVersion.get(),
mainJarFileName = mainJarFile.asFile.get().name,
mainClassName = mainClassName.get(),
jvmArgs = jvmArgs.get()
),

"--runtime-image", runtimeImageDirectory.asFile.get().absolutePath
packageFormatConfigs = getPackageFormatConfigs()
)

processBuilder.inheritIO()
val process = processBuilder.start()
process.waitFor(15, TimeUnit.MINUTES)
val packageFactory = PackageFactory(jPackagePath, jPackageConfig)
packageFactory.createPackages()
}

private fun getPackageFormatConfigs(): JPackagePackageFormatConfigs {
val packagePath = packageResourcesDir.asFile.get().toPath()
return when (getOS()) {
OS.WINDOWS -> {
val resourcesPath = packagePath.resolve("windows")
WindowsPackage(resourcesPath)
}

OS.MAC_OS -> {
val resourcesPath = packagePath.resolve("macosx")
MacPackage(resourcesPath)
}

OS.LINUX -> {
val resourcesPath = packagePath.resolve("linux")
LinuxPackages(resourcesPath)
}
}
}
}
32 changes: 32 additions & 0 deletions build-logic/packaging/src/main/kotlin/bisq/gradle/packaging/OS.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package bisq.gradle.packaging

import java.util.*

enum class OS {
LINUX, MAC_OS, WINDOWS
}

fun getOS(): OS {
val osName = System.getProperty("os.name").toLowerCase(Locale.US)
if (isLinux(osName)) {
return OS.LINUX
} else if (isMacOs(osName)) {
return OS.MAC_OS
} else if (isWindows(osName)) {
return OS.WINDOWS
}

throw IllegalStateException("Running on unsupported OS: $osName")
}

private fun isLinux(osName: String): Boolean {
return osName.contains("linux")
}

private fun isMacOs(osName: String): Boolean {
return osName.contains("mac") || osName.contains("darwin")
}

private fun isWindows(osName: String): Boolean {
return osName.contains("win")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,69 @@ package bisq.gradle.packaging

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.Directory
import org.gradle.api.plugins.JavaApplication
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.tasks.Jar
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.jvm.toolchain.JvmImplementation
import org.gradle.jvm.toolchain.JvmVendorSpec
import org.gradle.kotlin.dsl.findByType
import org.gradle.kotlin.dsl.register
import java.io.File
import javax.inject.Inject

class PackagingPlugin @Inject constructor(private val javaToolchainService: JavaToolchainService) : Plugin<Project> {

companion object {
const val APP_VERSION = "1.9.12"
}

class PackagingPlugin : Plugin<Project> {
override fun apply(project: Project) {
val installDistTask: TaskProvider<Sync> = project.tasks.named("installDist", Sync::class.java)
val jarTask: TaskProvider<Jar> = project.tasks.named("jar", Jar::class.java)

val javaApplicationExtension = project.extensions.findByType<JavaApplication>()
checkNotNull(javaApplicationExtension) { "Can't find JavaApplication extension." }

project.tasks.register<JPackageTask>("generateInstaller") {
jdkDirectory.set(getJdk17Directory())

distDirFile.set(installDistTask.map { it.destinationDir })
mainJarFile.set(jarTask.flatMap { it.archiveFile })

mainClassName.set(javaApplicationExtension.mainClass)
jvmArgs.set(javaApplicationExtension.applicationDefaultJvmArgs)

appVersion.set(APP_VERSION)

val packageResourcesDirFile = File(project.projectDir, "package")
packageResourcesDir.set(packageResourcesDirFile)

runtimeImageDirectory.set(getProjectJdkDirectory(project))
outputDirectory.set(project.layout.buildDirectory.dir("packaging/jpackage/packages"))
}
}

private fun getProjectJdkDirectory(project: Project): Provider<Directory> {
val javaExtension = project.extensions.findByType<JavaPluginExtension>()
checkNotNull(javaExtension) { "Can't find JavaPluginExtension extension." }

val toolchain = javaExtension.toolchain
val projectLauncherProvider = javaToolchainService.launcherFor(toolchain)
return projectLauncherProvider.map { it.metadata.installationPath }
}

private fun getJdk17Directory(): Provider<Directory> {
val jdk17LauncherProvider = javaToolchainService.launcherFor {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.AZUL)
implementation.set(JvmImplementation.VENDOR_SPECIFIC)
}
return jdk17LauncherProvider.map { it.metadata.installationPath }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package bisq.gradle.packaging.jpackage

data class JPackageAppConfig(
val appVersion: String,
val mainJarFileName: String,
val mainClassName: String,
val jvmArgs: Set<String>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package bisq.gradle.packaging.jpackage

import bisq.gradle.packaging.jpackage.package_formats.JPackagePackageFormatConfigs
import java.nio.file.Path

data class JPackageConfig(
val inputDirPath: Path,
val outputDirPath: Path,
val runtimeImageDirPath: Path,
val appConfig: JPackageAppConfig,
val packageFormatConfigs: JPackagePackageFormatConfigs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package bisq.gradle.packaging.jpackage

import java.nio.file.Path
import java.time.Year
import java.util.concurrent.TimeUnit


class PackageFactory(private val jPackagePath: Path, private val jPackageConfig: JPackageConfig) {

fun createPackages() {
val jPackageCommonArgs: List<String> = createCommonArguments(jPackageConfig.appConfig)

val packageFormatConfigs = jPackageConfig.packageFormatConfigs
val perPackageCommand = packageFormatConfigs.packageFormats
.map { packageFormatConfigs.createArgumentsForJPackage(it) + listOf("--type", it.fileExtension) }

val absoluteBinaryPath = jPackagePath.toAbsolutePath().toString()
perPackageCommand.forEach { customCommands ->
val processBuilder = ProcessBuilder(absoluteBinaryPath)
.inheritIO()

val allCommands = processBuilder.command()
allCommands.addAll(jPackageCommonArgs)
allCommands.addAll(customCommands)

val process: Process = processBuilder.start()
process.waitFor(15, TimeUnit.MINUTES)
}
}

private fun createCommonArguments(appConfig: JPackageAppConfig): List<String> =
mutableListOf(
"--dest", jPackageConfig.outputDirPath.toAbsolutePath().toString(),

"--name", "Bisq",
"--description", "A decentralized bitcoin exchange network.",
"--copyright", "Copyright © 2013-${Year.now()} - The Bisq developers",
"--vendor", "Bisq",

"--app-version", appConfig.appVersion,

"--input", jPackageConfig.inputDirPath.toAbsolutePath().toString(),
"--main-jar", appConfig.mainJarFileName,

"--main-class", appConfig.mainClassName,
"--java-options", appConfig.jvmArgs.joinToString(separator = " "),

"--runtime-image", jPackageConfig.runtimeImageDirPath.toAbsolutePath().toString()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package bisq.gradle.packaging.jpackage.package_formats

interface JPackagePackageFormatConfigs {
val packageFormats: Set<PackageFormat>
fun createArgumentsForJPackage(packageFormat: PackageFormat): List<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package bisq.gradle.packaging.jpackage.package_formats

import java.nio.file.Path

class LinuxPackages(private val resourcesPath: Path) : JPackagePackageFormatConfigs {
override val packageFormats = setOf(PackageFormat.DEB, PackageFormat.RPM)

override fun createArgumentsForJPackage(packageFormat: PackageFormat): List<String> {
val arguments = mutableListOf(
"--icon",
resourcesPath.resolve("icon.png")
.toAbsolutePath().toString(),

"--linux-package-name", "bisq",
"--linux-app-release", "1",

"--linux-menu-group", "Network",
"--linux-shortcut",

"--linux-deb-maintainer",
"noreply@bisq.network",
)

if (packageFormat == PackageFormat.DEB) {
arguments.add("--linux-deb-maintainer")
arguments.add("noreply@bisq.network")
}

if (packageFormat == PackageFormat.RPM) {
arguments.add("--linux-rpm-license-type")
arguments.add("AGPLv3")
}

return arguments
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package bisq.gradle.packaging.jpackage.package_formats

import java.nio.file.Path

class MacPackage(private val resourcesPath: Path) : JPackagePackageFormatConfigs {
override val packageFormats = setOf(PackageFormat.DMG)

override fun createArgumentsForJPackage(packageFormat: PackageFormat): List<String> =
mutableListOf(
"--resource-dir", resourcesPath.toAbsolutePath().toString(),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package bisq.gradle.packaging.jpackage.package_formats

enum class PackageFormat(val fileExtension: String) {
DEB("deb"),
DMG("dmg"),
EXE("exe"),
RPM("rpm")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package bisq.gradle.packaging.jpackage.package_formats

import java.nio.file.Path

class WindowsPackage(private val resourcesPath: Path) : JPackagePackageFormatConfigs {
override val packageFormats = setOf(PackageFormat.EXE)

override fun createArgumentsForJPackage(packageFormat: PackageFormat): List<String> =
mutableListOf(
"--icon", resourcesPath.resolve("Bisq.ico").toAbsolutePath().toString(),
"--resource-dir", resourcesPath.toAbsolutePath().toString(),

"--win-dir-chooser",
"--win-per-user-install",
"--win-menu",
"--win-shortcut",
)
}
Loading