Skip to content

Commit ce62388

Browse files
committed
Migrate to Kotlin 1.9.23, Compose 1.6.1, Gradle 8.7
1 parent 5b7a034 commit ce62388

File tree

17 files changed

+237
-283
lines changed

17 files changed

+237
-283
lines changed

.idea/ktlint-plugin.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ Unless pausing is enabled, updates will be drawn as fast as possible. The deskto
2828

2929
#### How To Build And Run
3030

31-
JVM desktop application: `./gradlew clean runRelease`
31+
JVM desktop application: `./gradlew runRelease`
3232

33-
Js browser application: `./gradlew clean -Dapplication.useJs=true jsBrowserProductionRun` (requires some patience for bundles to load)
33+
Js browser application: `./gradlew -Dapplication.useJs=true jsBrowserProductionRun` (requires some patience for bundles to load)
3434

35-
Wasm browser application: `./gradlew clean wasmJsBrowserProductionRun` (requires some patience for bundles to load)
35+
Wasm browser application: `./gradlew wasmJsBrowserProductionRun` (requires some patience for bundles to load)
3636

37-
* If you'd like Wasm even faster, try aggressive optimization: `./gradlew clean -Dapplication.optimize=true wasmJsBrowserProductionRun`
37+
* If you'd like Wasm even faster, try aggressive optimization: `./gradlew -Dapplication.optimize=true wasmJsBrowserProductionRun`
3838

3939
#### What To Try
4040

@@ -52,6 +52,10 @@ Wasm browser application: `./gradlew clean wasmJsBrowserProductionRun` (requires
5252

5353
#### Changes
5454

55+
##### 2024-03-30
56+
57+
* Migrated to 1.9.23, Compose 1.6.1
58+
5559
##### 2023-11-07
5660

5761
* Unified UI across platforms, using Compose scrollbars and `MaterialTheme` everywhere.

build.gradle.kts

+67-212
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1+
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
12
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
23

34
plugins {
4-
kotlin("multiplatform")
5-
if (System.getProperty("application.useJs") == "true") {
6-
id("org.jetbrains.compose") version "1.5.11"
7-
} else {
8-
id("org.jetbrains.compose") version "1.5.10-dev-wasm02"
9-
}
5+
alias(libs.plugins.org.jetbrains.kotlin.multiplatform)
6+
alias(libs.plugins.org.jetbrains.compose)
107
}
118

129
buildscript {
1310
dependencies {
14-
classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:_")
11+
classpath(libs.org.jetbrains.kotlinx.atomicfu.gradle.plugin)
1512
}
1613
}
1714

@@ -24,42 +21,53 @@ val useJs = System.getProperty("application.useJs") == "true"
2421
val optimizeAggressively = System.getProperty("application.optimize") == "true"
2522

2623
kotlin {
27-
jvmToolchain(11)
24+
val jdkVersion = project.property("local.jdk.version").toString().toInt()
25+
26+
jvmToolchain(jdkVersion)
27+
28+
jvm {
29+
compilations.configureEach {
30+
kotlinOptions.freeCompilerArgs += listOf("-Xjdk-release=$jdkVersion")
31+
}
32+
}
2833

2934
jvm {
3035
withJava()
3136
}
3237

33-
if (useJs) {
34-
js {
35-
moduleName = "app"
36-
binaries.executable()
37-
browser {
38-
useCommonJs()
39-
commonWebpackConfig {
40-
outputFileName = "$moduleName.js"
41-
}
38+
js {
39+
moduleName = "app"
40+
binaries.executable()
41+
browser {
42+
useCommonJs()
43+
commonWebpackConfig {
44+
outputFileName = "$moduleName.js"
4245
}
4346
}
44-
} else {
45-
wasmJs {
46-
moduleName = "app"
47-
binaries.executable()
48-
browser {
49-
commonWebpackConfig {
50-
outputFileName = "$moduleName.js"
51-
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).copy(
47+
}
48+
49+
@OptIn(ExperimentalWasmDsl::class)
50+
wasmJs {
51+
moduleName = "app"
52+
binaries.executable()
53+
browser {
54+
commonWebpackConfig {
55+
outputFileName = "$moduleName.js"
56+
devServer =
57+
(devServer ?: KotlinWebpackConfig.DevServer()).copy(
5258
port = 8081,
53-
static = (devServer?.static ?: mutableListOf()).apply {
54-
// Serve sources to debug inside browser
55-
add(project.rootDir.path)
56-
}
59+
static =
60+
(devServer?.static ?: mutableListOf()).apply {
61+
// Serve sources to debug inside browser
62+
add(project.rootDir.path)
63+
}
5764
)
58-
}
5965
}
66+
}
6067

61-
applyBinaryen {
62-
binaryenArgs = mutableListOf(
68+
applyBinaryen {
69+
binaryenArgs =
70+
mutableListOf(
6371
"--enable-nontrapping-float-to-int",
6472
"--enable-gc",
6573
"--enable-reference-types",
@@ -70,7 +78,8 @@ kotlin {
7078
"--fast-math"
7179
)
7280

73-
binaryenArgs += if (optimizeAggressively) {
81+
binaryenArgs +=
82+
if (optimizeAggressively) {
7483
listOf(
7584
"--closed-world",
7685
// "--metrics",
@@ -87,26 +96,24 @@ kotlin {
8796
"-c" // Run passes while binary size decreases
8897
)
8998
}
90-
}
9199
}
92100
}
93101

94102
sourceSets {
95103
all {
96104
languageSettings {
97-
languageVersion = "2.0"
98105
progressiveMode = true
99106
optIn("kotlin.RequiresOptIn")
100107
}
101108
}
102109

103110
val commonMain by getting {
104111
dependencies {
105-
implementation(compose.runtime)
112+
implementation(compose.ui)
106113
implementation(compose.foundation)
107114
implementation(compose.material)
108-
implementation("org.jetbrains.kotlinx:atomicfu:_")
109-
implementation(KotlinX.datetime)
115+
implementation(libs.org.jetbrains.kotlinx.atomicfu)
116+
implementation(libs.org.jetbrains.kotlinx.kotlinx.datetime)
110117
}
111118
}
112119

@@ -116,193 +123,41 @@ kotlin {
116123
}
117124
}
118125

119-
if (useJs) {
120-
val jsMain by getting {
121-
dependencies {
122-
implementation(compose.web.core)
123-
}
126+
val jsMain by getting {
127+
dependencies {
128+
@Suppress("DEPRECATION")
129+
implementation(compose.web.core) // Required for Compose Web/Canvas on JS
124130
}
125131
}
126132
}
127133
}
128134

129135
compose {
130-
desktop.application.mainClass = "MainKt"
131-
experimental {
132-
web.application {}
136+
providers.gradleProperty("local.compose.kotlinCompilerPlugin").orNull?.let { composeKotlinCompilerPlugin ->
137+
kotlinCompilerPlugin.set(composeKotlinCompilerPlugin)
138+
val kotlinVersion = "${libs.plugins.org.jetbrains.kotlin.multiplatform.get().version}"
139+
kotlinCompilerPluginArgs.add("suppressKotlinVersionCompatibilityCheck=$kotlinVersion")
133140
}
134-
if (!useJs) {
135-
kotlinCompilerPlugin.set("1.5.4")
136-
}
137-
}
141+
// kotlinCompilerPluginArgs.add("reportsDestination=${layout.buildDirectory.file("reports")}")
138142

139-
if (!useJs) {
140-
rootProject.tasks {
141-
val hackNodeModuleImports by registering(Copy::class) {
142-
group = "kotlin browser"
143-
mustRunAfter("kotlinNpmInstall")
144-
from(buildDir.path + "/js/node_modules/@js-joda")
145-
into(buildDir.path + "/js/packages/app/kotlin/@js-joda")
146-
}
147-
for (dependent in listOf("wasmJsBrowserProductionRun", "wasmJsBrowserDevelopmentRun")) {
148-
named(dependent) {
149-
dependsOn(hackNodeModuleImports)
150-
}
151-
}
152-
}
153-
}
154-
155-
val yarnExecutablePath: String by lazy {
156-
with(rootProject.extensions.getByType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>()) {
157-
requireConfigured().executable.substringBeforeLast(".js")
158-
}
159-
}
143+
desktop.application.mainClass = "MainKt"
160144

161-
val nodeBinaryDirectory: String by lazy {
162-
with(rootProject.extensions.getByType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>()) {
163-
requireConfigured().nodeBinDir.toString()
145+
experimental {
146+
web.application {}
164147
}
165148
}
166149

167-
fun Exec.addNodePath() {
168-
environment("PATH", "${System.getenv("PATH")}:$nodeBinaryDirectory")
169-
}
170-
150+
// Hack to use kotlinx-datetime with Wasm
171151
rootProject.tasks {
172-
val yarnLockName = "yarn.lock"
173-
val yarnBuildDirectory = "$rootDir/build/js"
174-
val yarnLockBuildPath = "$yarnBuildDirectory/$yarnLockName"
175-
val yarnLockPrimaryName = "$yarnLockName.primary"
176-
val yarnLockStorageDirectory = "$rootDir/kotlin-js-store"
177-
val yarnLockPrimaryPath = "$yarnLockStorageDirectory/$yarnLockPrimaryName"
178-
179-
val yarnShowAuditReport by registering(Exec::class) {
180-
group = "nodejs"
181-
description = "Shows an audit report for npm packages, listing known vulnerabilities."
182-
workingDir = File(yarnBuildDirectory)
183-
addNodePath()
184-
commandLine = mutableListOf(yarnExecutablePath, "audit")
185-
}
186-
187-
val yarnShowOutdatedPackages by registering(Exec::class) {
188-
group = "nodejs"
189-
description = "Shows outdated npm packages."
190-
workingDir = File(yarnBuildDirectory)
191-
addNodePath()
192-
commandLine = mutableListOf(yarnExecutablePath, "outdated")
193-
isIgnoreExitValue = true
194-
}
195-
196-
val yarnLockUpdatePrimary by registering {
197-
group = "nodejs"
198-
description =
199-
"Updates '$yarnLockPrimaryName' from the build-generated '$yarnLockName'. Must be invoked manually."
200-
201-
doLast {
202-
copy {
203-
from(yarnLockBuildPath)
204-
rename { yarnLockPrimaryName }
205-
into(yarnLockStorageDirectory)
206-
}
152+
val hackNodeModuleImports by registering(Copy::class) {
153+
group = "kotlin browser"
154+
mustRunAfter("kotlinNpmInstall")
155+
from(layout.buildDirectory.file("js/node_modules/@js-joda"))
156+
into(layout.buildDirectory.file("js/packages/app/kotlin/@js-joda"))
157+
}
158+
for (dependent in listOf("wasmJsBrowserProductionRun", "wasmJsBrowserDevelopmentRun")) {
159+
named(dependent) {
160+
dependsOn(hackNodeModuleImports)
207161
}
208-
209-
inputs.file(yarnLockBuildPath).withPropertyName("inputFile")
210-
outputs.file(yarnLockPrimaryPath).withPropertyName("outputFile")
211-
}
212-
213-
val yarnLockRestore by registering {
214-
group = "nodejs"
215-
description = "Restores '$yarnLockName' from '$yarnLockPrimaryName' to ensure stable builds."
216-
217-
// Kotlin >=1.6.10 restores 'yarn.lock' from 'kotlin-js-store/yarn.lock', but also updates the latter
218-
// unconditionally, without any checks performed. To avoid green-lighting unchecked code, make sure
219-
// our version always gets precedence.
220-
mustRunAfter("kotlinRestoreYarnLock")
221-
222-
doLast {
223-
copy {
224-
from(yarnLockPrimaryPath)
225-
rename { yarnLockName }
226-
into(yarnBuildDirectory)
227-
}
228-
}
229-
230-
inputs.file(yarnLockPrimaryPath).withPropertyName("inputFile")
231-
outputs.file(yarnLockBuildPath).withPropertyName("outputFile")
232-
}
233-
234-
val yarnLockValidate by registering {
235-
group = "nodejs"
236-
description = (
237-
"Validates that the build directory's '$yarnLockName' corresponds" +
238-
" to '$yarnLockPrimaryName' in the project root directory."
239-
)
240-
dependsOn("kotlinNpmInstall")
241-
242-
doLast {
243-
val expected = File(yarnLockPrimaryPath).readText().trim()
244-
val actual = File(yarnLockBuildPath).readText().trim()
245-
246-
if (expected != actual) {
247-
// WORKAROUND https://youtrack.jetbrains.com/issue/IDEA-267343 –
248-
// 'idea diff ...' produces an exception and does not immediately complete, although it does
249-
// open the diff window.
250-
// Replace the following workaround with the code in comments when the issue is fixed.
251-
ProcessBuilder("idea", "diff", yarnLockPrimaryPath, yarnLockBuildPath)
252-
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
253-
.redirectError(ProcessBuilder.Redirect.DISCARD)
254-
.start()
255-
/* Replace with:
256-
exec {
257-
commandLine = mutableListOf("idea", "diff", yarnLockPrimaryPath, yarnLockBuildPath)
258-
}
259-
*/
260-
261-
throw AssertionError(
262-
"The build-generated '$yarnLockName' differs from '$yarnLockPrimaryName'" +
263-
" in the project root directory." +
264-
" Each difference indicates a dependency update which has not been confirmed by" +
265-
" running './gradlew :yarnLockUpdatePrimary'.\n" +
266-
"\tAn idea diff window has been opened.\n" +
267-
"\tTo explore differences later, please run:" +
268-
" idea diff '$yarnLockPrimaryPath' '$yarnLockBuildPath'\n" +
269-
"\tTo assess package risks, please run: gradlew :analyseSupplyChain"
270-
)
271-
}
272-
}
273-
}
274-
275-
named<org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask>("kotlinNpmInstall") {
276-
dependsOn(yarnLockRestore)
277-
finalizedBy(yarnLockValidate)
278-
279-
// Avoid package installation scripts vulnerability:
280-
// https://blog.npmjs.org/post/141702881055/package-install-scripts-vulnerability
281-
args += "--ignore-scripts"
282-
// To detect packages, which use installation scripts, see 'can-i-ignore-scripts', described here:
283-
// https://dev.to/naugtur/get-safe-and-remain-productive-with-can-i-ignore-scripts-2ddc
284-
}
285-
286-
val analyzeNpmSupplyChain by registering(Exec::class) {
287-
group = "nodejs"
288-
description = "Analyses the npm package supply chain, hinting on possible security risks."
289-
290-
dependsOn("kotlinNpmInstall")
291-
292-
val packagesToExclude = listOf("packages", "packages_imported").flatMap { packageDirectory ->
293-
File("$yarnBuildDirectory/$packageDirectory")
294-
.listFiles()?.mapNotNull { if (it.isDirectory) it.name else null } ?: listOf()
295-
}
296-
297-
commandLine = mutableListOf(
298-
"sca",
299-
"--exclude",
300-
packagesToExclude.joinToString("|"),
301-
"$yarnBuildDirectory/node_modules"
302-
)
303-
}
304-
305-
named("check") {
306-
dependsOn(":yarnLockValidate")
307162
}
308163
}

0 commit comments

Comments
 (0)