You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
9.6 KiB
239 lines
9.6 KiB
package org.jetbrains.compose.desktop.application.internal |
|
|
|
import org.gradle.api.* |
|
import org.gradle.api.file.DuplicatesStrategy |
|
import org.gradle.api.file.FileCollection |
|
import org.gradle.api.plugins.JavaPluginConvention |
|
import org.gradle.api.provider.Provider |
|
import org.gradle.api.tasks.JavaExec |
|
import org.gradle.api.tasks.TaskContainer |
|
import org.gradle.api.tasks.TaskProvider |
|
import org.gradle.jvm.tasks.Jar |
|
import org.jetbrains.compose.desktop.application.dsl.Application |
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat |
|
import org.jetbrains.compose.desktop.application.tasks.AbstractJPackageTask |
|
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension |
|
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType |
|
import java.io.File |
|
import java.util.* |
|
|
|
// todo: multiple launchers |
|
// todo: file associations |
|
// todo: use workers |
|
fun configureApplicationImpl(project: Project, app: Application) { |
|
if (app._isDefaultConfigurationEnabled) { |
|
if (project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) { |
|
project.configureFromMppPlugin(app) |
|
} else if (project.plugins.hasPlugin("org.jetbrains.kotlin.jvm")) { |
|
val mainSourceSet = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets.getByName("main") |
|
app.from(mainSourceSet) |
|
} |
|
} |
|
project.configurePackagingTasks(listOf(app)) |
|
project.configureWix() |
|
} |
|
|
|
internal fun Project.configureFromMppPlugin(mainApplication: Application) { |
|
val kotlinExt = extensions.getByType(KotlinMultiplatformExtension::class.java) |
|
var isJvmTargetConfigured = false |
|
kotlinExt.targets.all { target -> |
|
if (target.platformType == KotlinPlatformType.jvm) { |
|
if (!isJvmTargetConfigured) { |
|
mainApplication.from(target) |
|
isJvmTargetConfigured = true |
|
} else { |
|
logger.error("w: Default configuration for Compose Desktop Application is disabled: " + |
|
"multiple Kotlin JVM targets definitions are detected. " + |
|
"Specify, which target to use by using `compose.desktop.application.from(kotlinMppTarget)`") |
|
mainApplication.disableDefaultConfiguration() |
|
} |
|
} |
|
} |
|
} |
|
|
|
internal fun Project.configurePackagingTasks(apps: Collection<Application>) { |
|
for (app in apps) { |
|
configureRunTask(app) |
|
configurePackagingTasks(app) |
|
configurePackageUberJarForCurrentOS(app) |
|
} |
|
} |
|
|
|
internal fun Project.configurePackagingTasks(app: Application): TaskProvider<DefaultTask> { |
|
val packageFormats = app.nativeDistributions.targetFormats.map { targetFormat -> |
|
tasks.composeTask<AbstractJPackageTask>( |
|
taskName("package", app, targetFormat.name), |
|
args = listOf(targetFormat) |
|
) { |
|
configurePackagingTask(app) |
|
} |
|
} |
|
return tasks.composeTask<DefaultTask>(taskName("package", app)) { |
|
dependsOn(packageFormats) |
|
} |
|
} |
|
|
|
internal fun AbstractJPackageTask.configurePackagingTask(app: Application) { |
|
enabled = targetFormat.isCompatibleWithCurrentOS |
|
|
|
if (targetFormat != TargetFormat.AppImage) { |
|
configurePlatformSettings(app) |
|
} |
|
|
|
app.nativeDistributions.let { executables -> |
|
packageName.set(app._packageNameProvider(project)) |
|
packageDescription.set(provider { executables.description }) |
|
packageCopyright.set(provider { executables.copyright }) |
|
packageVendor.set(provider { executables.vendor }) |
|
packageVersion.set(app._packageVersionInternal(project)) |
|
} |
|
|
|
destinationDir.set(app.nativeDistributions.outputBaseDir.map { it.dir("${app.name}/${targetFormat.id}") }) |
|
javaHome.set(provider { app.javaHomeOrDefault() }) |
|
|
|
launcherMainJar.set(app.mainJar.orNull) |
|
app._fromFiles.forEach { files.from(it) } |
|
dependsOn(*app._dependenciesTaskNames.toTypedArray()) |
|
|
|
app._configurationSource?.let { configSource -> |
|
dependsOn(configSource.jarTaskName) |
|
files.from(configSource.runtimeClasspath) |
|
launcherMainJar.set(app.mainJar.orElse(configSource.jarTask(project).flatMap { it.archiveFile })) |
|
} |
|
|
|
modules.set(provider { app.nativeDistributions.modules }) |
|
launcherMainClass.set(provider { app.mainClass }) |
|
launcherJvmArgs.set(provider { app.jvmArgs }) |
|
launcherArgs.set(provider { app.args }) |
|
} |
|
|
|
internal fun AbstractJPackageTask.configurePlatformSettings(app: Application) { |
|
when (currentOS) { |
|
OS.Linux -> { |
|
app.nativeDistributions.linux.also { linux -> |
|
linuxShortcut.set(provider { linux.shortcut }) |
|
linuxAppCategory.set(provider { linux.appCategory }) |
|
linuxAppRelease.set(provider { linux.appRelease }) |
|
linuxDebMaintainer.set(provider { linux.debMaintainer }) |
|
linuxMenuGroup.set(provider { linux.menuGroup }) |
|
linuxPackageName.set(provider { linux.packageName }) |
|
linuxRpmLicenseType.set(provider { linux.rpmLicenseType }) |
|
iconFile.set(linux.iconFile) |
|
} |
|
} |
|
OS.Windows -> { |
|
app.nativeDistributions.windows.also { win -> |
|
winConsole.set(provider { win.console }) |
|
winDirChooser.set(provider { win.dirChooser }) |
|
winPerUserInstall.set(provider { win.perUserInstall }) |
|
winShortcut.set(provider { win.shortcut }) |
|
winMenu.set(provider { win.menu }) |
|
winMenuGroup.set(provider { win.menuGroup }) |
|
winUpgradeUuid.set(provider { win.upgradeUuid }) |
|
iconFile.set(win.iconFile) |
|
} |
|
} |
|
OS.MacOS -> { |
|
app.nativeDistributions.macOS.also { mac -> |
|
macPackageName.set(provider { mac.packageName }) |
|
macPackageIdentifier.set(provider { mac.packageIdentifier }) |
|
macSign.set(provider { mac.signing.sign }) |
|
macSigningKeyUserName.set(provider { mac.signing.keyUserName }) |
|
macSigningKeychain.set(project.layout.file(provider { mac.signing.keychain })) |
|
macBundleSigningPrefix.set(provider { mac.signing.bundlePrefix }) |
|
iconFile.set(mac.iconFile) |
|
} |
|
} |
|
} |
|
} |
|
|
|
private fun Project.configureRunTask(app: Application) { |
|
project.tasks.composeTask<JavaExec>(taskName("run", app)) { |
|
mainClass.set(provider { app.mainClass }) |
|
executable(javaExecutable(app.javaHomeOrDefault())) |
|
jvmArgs = app.jvmArgs |
|
args = app.args |
|
|
|
val cp = objects.fileCollection() |
|
// adding a null value will cause future invocations of `from` to throw an NPE |
|
app.mainJar.orNull?.let { cp.from(it) } |
|
cp.from(app._fromFiles) |
|
dependsOn(*app._dependenciesTaskNames.toTypedArray()) |
|
|
|
app._configurationSource?.let { configSource -> |
|
dependsOn(configSource.jarTaskName) |
|
cp.from(configSource.runtimeClasspath) |
|
} |
|
|
|
classpath = cp |
|
} |
|
} |
|
|
|
private fun Project.configurePackageUberJarForCurrentOS(app: Application) = |
|
project.tasks.composeTask<Jar>(taskName("package", app, "uberJarForCurrentOS")) { |
|
fun flattenJars(files: FileCollection): FileCollection = |
|
project.files({ |
|
files.map { if (it.isZipOrJar()) zipTree(it) else it } |
|
}) |
|
|
|
// adding a null value will cause future invocations of `from` to throw an NPE |
|
app.mainJar.orNull?.let { from(it) } |
|
from(flattenJars(app._fromFiles)) |
|
dependsOn(*app._dependenciesTaskNames.toTypedArray()) |
|
|
|
app._configurationSource?.let { configSource -> |
|
dependsOn(configSource.jarTaskName) |
|
from(flattenJars(configSource.runtimeClasspath)) |
|
} |
|
|
|
app.mainClass?.let { manifest.attributes["Main-Class"] = it } |
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE |
|
archiveAppendix.set(currentTarget.id) |
|
archiveBaseName.set(app._packageNameProvider(project)) |
|
archiveVersion.set(app._packageVersionInternal(project)) |
|
destinationDirectory.set(project.layout.buildDirectory.dir("compose/jars")) |
|
|
|
doLast { |
|
logger.lifecycle("The jar is written to ${archiveFile.get().asFile.canonicalPath}") |
|
} |
|
} |
|
|
|
private fun File.isZipOrJar() = |
|
name.endsWith(".jar", ignoreCase = true) |
|
|| name.endsWith(".zip", ignoreCase = true) |
|
|
|
private fun Application.javaHomeOrDefault(): String = |
|
javaHome ?: System.getProperty("java.home") |
|
|
|
private fun javaExecutable(javaHome: String): String { |
|
val executableName = if (currentOS == OS.Windows) "java.exe" else "java" |
|
return File(javaHome).resolve("bin/$executableName").absolutePath |
|
} |
|
|
|
private inline fun <reified T : Task> TaskContainer.composeTask( |
|
name: String, |
|
args: List<Any> = emptyList(), |
|
noinline configureFn: T.() -> Unit = {} |
|
) = register(name, T::class.java, *args.toTypedArray()).apply { |
|
configure { |
|
it.group = "compose desktop" |
|
it.configureFn() |
|
} |
|
} |
|
|
|
internal fun Application._packageNameProvider(project: Project): Provider<String> = |
|
project.provider { nativeDistributions.packageName ?: project.name } |
|
|
|
internal fun Application._packageVersionInternal(project: Project): Provider<String?> = |
|
project.provider { |
|
nativeDistributions.version |
|
?: project.version.toString().takeIf { it != "unspecified" } |
|
} |
|
|
|
@OptIn(ExperimentalStdlibApi::class) |
|
private fun taskName(action: String, app: Application, suffix: String? = null): String = |
|
listOf( |
|
action, |
|
app.name.takeIf { it != "main" }?.capitalize(Locale.ROOT), |
|
suffix?.capitalize(Locale.ROOT) |
|
).filterNotNull().joinToString("")
|
|
|