Skip to content

Commit

Permalink
Introduce ProGuard integration for Compose/Desktop packaging
Browse files Browse the repository at this point in the history
Resolves #1174
  • Loading branch information
AlexeyTsvetkov committed Sep 14, 2022
1 parent 59d4676 commit 544f773
Show file tree
Hide file tree
Showing 54 changed files with 1,332 additions and 577 deletions.
12 changes: 12 additions & 0 deletions examples/imageviewer/desktop/rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Ktor
-keep class io.ktor.** { *; }
-keepclassmembers class io.ktor.** { volatile <fields>; }
-keep class io.ktor.client.engine.cio.** { *; }
-keep class kotlinx.coroutines.** { *; }
-dontwarn kotlinx.atomicfu.**
-dontwarn io.netty.**
-dontwarn com.typesafe.**
-dontwarn org.slf4j.**

# Obfuscation breaks coroutines/ktor for some reason
-dontobfuscate
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ import org.jetbrains.compose.desktop.preview.internal.initializePreview
import org.jetbrains.compose.experimental.dsl.ExperimentalExtension
import org.jetbrains.compose.experimental.internal.checkExperimentalTargetsWithSkikoIsEnabled
import org.jetbrains.compose.experimental.internal.configureExperimental
import org.jetbrains.compose.internal.COMPOSE_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_JS_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.web.WebExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
Expand All @@ -46,7 +43,7 @@ class ComposePlugin : Plugin<Project> {
setUpGroovyDslExtensions(project)
}

project.initializePreview()
project.initializePreview(desktopExtension)
composeExtension.extensions.create("web", WebExtension::class.java)

project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.ExtensionAware
import org.jetbrains.compose.desktop.application.dsl.JvmApplication
import org.jetbrains.compose.desktop.application.internal.JvmApplicationInternal
import org.jetbrains.compose.desktop.application.dsl.NativeApplication
import javax.inject.Inject

Expand All @@ -17,7 +18,7 @@ abstract class DesktopExtension @Inject constructor(private val objectFactory: O
private set
val application: JvmApplication by lazy {
_isJvmApplicationInitialized = true
objectFactory.newInstance(JvmApplication::class.java, "main")
objectFactory.newInstance(JvmApplicationInternal::class.java, "main")
}
fun application(fn: Action<JvmApplication>) {
fn.execute(application)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,65 +8,27 @@ package org.jetbrains.compose.desktop.application.dsl
import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.SourceSet
import org.jetbrains.compose.desktop.application.internal.ConfigurationSource
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import java.util.*
import javax.inject.Inject

open class JvmApplication @Inject constructor(
@Suppress("unused")
val name: String,
objects: ObjectFactory
) {
internal var _configurationSource: ConfigurationSource? = null
private set
internal var _isDefaultConfigurationEnabled = true
private set
internal val _fromFiles = objects.fileCollection()
internal val _dependenciesTaskNames = ArrayList<String>()

fun from(from: SourceSet) {
_configurationSource = ConfigurationSource.GradleSourceSet(from)
}
fun from(from: KotlinTarget) {
check(from is KotlinJvmTarget) { "Non JVM Kotlin MPP targets are not supported: ${from.javaClass.canonicalName} " +
"is not subtype of ${KotlinJvmTarget::class.java.canonicalName}" }
_configurationSource = ConfigurationSource.KotlinMppTarget(from)
}
fun disableDefaultConfiguration() {
_isDefaultConfigurationEnabled = false
}

fun fromFiles(vararg files: Any) {
_fromFiles.from(*files)
}

fun dependsOn(vararg tasks: String) {
_dependenciesTaskNames.addAll(tasks)
}
fun dependsOn(vararg tasks: Task) {
tasks.mapTo(_dependenciesTaskNames) { it.path }
}

var mainClass: String? = null
val mainJar: RegularFileProperty = objects.fileProperty()
var javaHome: String? = null

val args: MutableList<String> = ArrayList()
fun args(vararg args: String) {
this.args.addAll(args)
}

val jvmArgs: MutableList<String> = ArrayList()
fun jvmArgs(vararg jvmArgs: String) {
this.jvmArgs.addAll(jvmArgs)
}

val nativeDistributions: JvmApplicationDistributions = objects.newInstance(JvmApplicationDistributions::class.java)
fun nativeDistributions(fn: Action<JvmApplicationDistributions>) {
fn.execute(nativeDistributions)
}
abstract class JvmApplication {
abstract fun from(from: SourceSet)
abstract fun from(from: KotlinTarget)
abstract fun disableDefaultConfiguration()
abstract fun dependsOn(vararg tasks: Task)
abstract fun dependsOn(vararg tasks: String)
abstract fun fromFiles(vararg files: Any)

abstract var mainClass: String?
abstract val mainJar: RegularFileProperty
abstract var javaHome: String
abstract val args: MutableList<String>
abstract fun args(vararg args: String)
abstract val jvmArgs: MutableList<String>
abstract fun jvmArgs(vararg jvmArgs: String)
abstract val nativeDistributions: JvmApplicationDistributions
abstract fun nativeDistributions(fn: Action<JvmApplicationDistributions>)
abstract val buildTypes: JvmApplicationBuildTypes
abstract fun buildTypes(fn: Action<JvmApplicationBuildTypes>)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

package org.jetbrains.compose.desktop.application.dsl

import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import org.jetbrains.compose.desktop.application.internal.new
import javax.inject.Inject

abstract class JvmApplicationBuildTypes @Inject constructor(
objects: ObjectFactory
) {
/**
* The default build type does not have a classifier
* to preserve compatibility with tasks, existing before
* the introduction of the release build type,
* e.g. we don't want to break existing packageDmg,
* createDistributable tasks after the introduction
* of packageReleaseDmg and createReleaseDistributable tasks.
*/
internal val default: JvmApplicationBuildType = objects.new("")

val release: JvmApplicationBuildType = objects.new<JvmApplicationBuildType>("release").apply {
proguard.isEnabled.set(true)
}
fun release(fn: Action<JvmApplicationBuildType>) {
fn.execute(release)
}
}

abstract class JvmApplicationBuildType @Inject constructor(
/**
* A classifier distinguishes tasks and directories of one build type from another.
* E.g. `release` build type produces packageReleaseDmg task.
*/
internal val classifier: String,
objects: ObjectFactory,
) {
val proguard: ProguardSettings = objects.new()
fun proguard(fn: Action<ProguardSettings>) {
fn.execute(proguard)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ package org.jetbrains.compose.desktop.application.dsl

import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import org.jetbrains.compose.desktop.application.internal.ConfigurationSource
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.konan.target.Family
import javax.inject.Inject

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

package org.jetbrains.compose.desktop.application.dsl

import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.jetbrains.compose.desktop.application.internal.notNullProperty
import org.jetbrains.compose.desktop.application.internal.nullableProperty
import javax.inject.Inject

private const val DEFAULT_PROGUARD_VERSION = "7.2.2"

abstract class ProguardSettings @Inject constructor(
objects: ObjectFactory,
) {
val version: Property<String> = objects.notNullProperty(DEFAULT_PROGUARD_VERSION)
val maxHeapSize: Property<String?> = objects.nullableProperty()
val configurationFiles: ConfigurableFileCollection = objects.fileCollection()
val isEnabled: Property<Boolean> = objects.notNullProperty(false)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

Expand All @@ -19,20 +19,25 @@ internal class ExternalToolRunner(
private val logsDir: Provider<Directory>,
private val execOperations: ExecOperations
) {
internal enum class LogToConsole {
Always,
Never,
OnlyWhenVerbose
}

operator fun invoke(
tool: File,
args: Collection<String>,
environment: Map<String, Any> = emptyMap(),
workingDir: File? = null,
checkExitCodeIsNormal: Boolean = true,
processStdout: Function1<String, Unit>? = null,
forceLogToFile: Boolean = false
logToConsole: LogToConsole = LogToConsole.OnlyWhenVerbose
): ExecResult {
val logsDir = logsDir.ioFile
logsDir.mkdirs()

val toolName = tool.nameWithoutExtension
val logToConsole = verbose.get() && !forceLogToFile
val outFile = logsDir.resolve("${toolName}-${currentTimeStamp()}-out.txt")
val errFile = logsDir.resolve("${toolName}-${currentTimeStamp()}-err.txt")

Expand All @@ -46,6 +51,12 @@ internal class ExternalToolRunner(
// check exit value later
spec.isIgnoreExitValue = true

@Suppress("NAME_SHADOWING")
val logToConsole = when (logToConsole) {
LogToConsole.Always -> true
LogToConsole.Never -> false
LogToConsole.OnlyWhenVerbose -> verbose.get()
}
if (logToConsole) {
spec.standardOutput = spec.standardOutput.alsoOutputTo(outFileStream)
spec.errorOutput = spec.errorOutput.alsoOutputTo(errFileStream)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/

package org.jetbrains.compose.desktop.application.internal

import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
import org.jetbrains.compose.desktop.application.dsl.JvmApplicationBuildType
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.internal.javaSourceSets
import org.jetbrains.compose.internal.joinDashLowercaseNonEmpty
import org.jetbrains.compose.internal.mppExt
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType

internal data class JvmApplicationContext(
val project: Project,
private val appInternal: JvmApplicationInternal,
val buildType: JvmApplicationBuildType,
private val taskGroup: String = composeDesktopTaskGroup
) {
val app: JvmApplicationData
get() = appInternal.data

val appDirName: String
get() = joinDashLowercaseNonEmpty(appInternal.name, buildType.classifier)

val appTmpDir: Provider<Directory>
get() = project.layout.buildDirectory.dir(
"compose/tmp/$appDirName"
)

fun <T : Task> T.useAppRuntimeFiles(fn: T.(JvmApplicationRuntimeFiles) -> Unit) {
val runtimeFiles = app.jvmApplicationRuntimeFilesProvider?.jvmApplicationRuntimeFiles(project)
?: JvmApplicationRuntimeFiles(
allRuntimeJars = app.fromFiles,
mainJar = app.mainJar,
taskDependencies = app.dependenciesTaskNames.toTypedArray()
)
runtimeFiles.configureUsageBy(this, fn)
}

val tasks = JvmTasks(project, buildType, taskGroup)

val packageNameProvider: Provider<String>
get() = project.provider { appInternal.nativeDistributions.packageName ?: project.name }

inline fun <reified T> provider(noinline fn: () -> T): Provider<T> =
project.provider(fn)

fun configureDefaultApp() {
if (project.plugins.hasPlugin(KOTLIN_MPP_PLUGIN_ID)) {
var isJvmTargetConfigured = false
project.mppExt.targets.all { target ->
if (target.platformType == KotlinPlatformType.jvm) {
if (!isJvmTargetConfigured) {
appInternal.from(target)
isJvmTargetConfigured = true
} else {
project.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)`")
appInternal.disableDefaultConfiguration()
}
}
}
} else if (project.plugins.hasPlugin(KOTLIN_JVM_PLUGIN_ID)) {
val mainSourceSet = project.javaSourceSets.getByName("main")
appInternal.from(mainSourceSet)
}
}
}
Loading

0 comments on commit 544f773

Please sign in to comment.