From 1b9547c007642ad9adc0b000456926578a87e978 Mon Sep 17 00:00:00 2001 From: sanlorng Date: Tue, 16 Sep 2025 19:36:19 +0800 Subject: [PATCH 1/5] feat: Support Compose Resources in native macOS applications This commit introduces support for Compose Resources in native macOS applications. Key changes: - Added `composeResourcesDirs` property to `AbstractNativeMacApplicationPackageAppDirTask` to include resources during app packaging. - Updated `configureNativeApplication.kt` to pass Compose resource directories from Kotlin Native binaries to the packaging task. - Included a new test project `misc/macosNativeResources` to verify the integration. - Added `macosExecutableResources` test in `ResourcesTest.kt` to ensure resources are correctly packaged into the `.app` bundle. --- .../internal/configureNativeApplication.kt | 8 +++ ...ctNativeMacApplicationPackageAppDirTask.kt | 14 +++++ .../test/tests/integration/ResourcesTest.kt | 16 +++++ .../macosNativeResources/build.gradle.kts | 58 +++++++++++++++++++ .../macosNativeResources/gradle.properties | 2 + .../macosNativeResources/settings.gradle.kts | 26 +++++++++ .../drawable/compose-multiplatform.xml | 36 ++++++++++++ .../src/commonMain/kotlin/App.kt | 37 ++++++++++++ .../composeResources/drawable/icon.xml | 36 ++++++++++++ .../src/macosMain/kotlin/main.kt | 2 + 10 files changed, 235 insertions(+) create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/build.gradle.kts create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/gradle.properties create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/settings.gradle.kts create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/composeResources/drawable/compose-multiplatform.xml create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/kotlin/App.kt create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/composeResources/drawable/icon.xml create mode 100644 gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/kotlin/main.kt diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt index 2cf2c7961c7..233d5b35665 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt @@ -18,6 +18,7 @@ import org.jetbrains.compose.desktop.tasks.AbstractUnpackDefaultComposeApplicati import org.jetbrains.compose.internal.utils.OS import org.jetbrains.compose.internal.utils.currentOS import org.jetbrains.compose.internal.utils.joinLowerCamelCase +import org.jetbrains.compose.internal.utils.provider import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind @@ -70,6 +71,13 @@ private fun configureNativeApplication( copyright.set(project.provider { app.distributions.copyright ?: "Copyright (C) ${Calendar.getInstance().get(Calendar.YEAR)}" }) + if (binary.outputKind == NativeOutputKind.EXECUTABLE) { + val binaryResources = (binary.compilation.associatedCompilations + binary.compilation).flatMap { compilation -> + compilation.allKotlinSourceSets.map { it.resources } + } + inputs.files(binaryResources) + composeResourcesDirs.set(provider { binaryResources }) + } } if (TargetFormat.Dmg in app.distributions.targetFormats) { diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt index 1b051e8505b..3113bb71e23 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt @@ -6,6 +6,8 @@ package org.jetbrains.compose.desktop.application.tasks import org.gradle.api.file.RegularFileProperty +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.api.tasks.Optional @@ -43,6 +45,11 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac @get:Optional val minimumSystemVersion: Property = objects.nullableProperty() + @get:InputFiles + @get:Optional + @get:PathSensitive(PathSensitivity.ABSOLUTE) + val composeResourcesDirs: ListProperty = objects.listProperty(SourceDirectorySet::class.java) + override fun createPackage(destinationDir: File, workingDir: File) { val packageName = packageName.get() val appDir = destinationDir.resolve("$packageName.app").apply { mkdirs() } @@ -61,6 +68,13 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac setupInfoPlist(executableName = appExecutableFile.name) writeToFile(contentsDir.resolve("Info.plist")) } + + composeResourcesDirs.orNull?.let { resources -> + fileOperations.copy { copySpec -> + copySpec.from(resources) + copySpec.into(appResourcesDir.resolve("compose-resources").apply { mkdirs() }) + } + } } private fun InfoPlistBuilder.setupInfoPlist(executableName: String) { diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index 0a2019c2370..f293435fd54 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -1026,6 +1026,22 @@ class ResourcesTest : GradlePluginTestBase() { } } + @Test + fun macosExecutableResources() { + Assumptions.assumeTrue(currentOS == OS.MacOS) + with(testProject("misc/macosNativeResources")) { + val appName = "Test Resources" + gradle(":createDistributableNativeDebugMacosX64", "--dry-run").checks { + check.taskSkipped(":createDistributableNativeDebugMacosX64") + } + gradle(":createDistributableNativeDebugMacosX64").checks { + val targetResourcesDir = "build/compose/binaries/main/native-macosX64-debug-app-image/${appName}.app/Contents/Resources" + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/compose-multiplatform.xml").checkExists() + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/icon.xml").checkExists() + } + } + } + @Test fun iosTestResources() { Assumptions.assumeTrue(currentOS == OS.MacOS) diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/build.gradle.kts b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/build.gradle.kts new file mode 100644 index 00000000000..1eee194b905 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/build.gradle.kts @@ -0,0 +1,58 @@ +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + kotlin("multiplatform") + kotlin("plugin.compose") + id("org.jetbrains.compose") +} + +kotlin { + + listOf( + macosX64(), + macosArm64(), + ).forEach { + it.binaries { + executable { + entryPoint = "main" + freeCompilerArgs += listOf( + "-linker-options", + "-framework", + "-linker-option", + "Metal", + "-Xdisable-phases=VerifyBitcode" + ) + } + } + } + + sourceSets { + commonMain { + dependencies { + implementation(compose.runtime) + implementation(compose.material) + implementation(compose.components.resources) + } + } + } +} + +compose.desktop { + nativeApplication { + targets( + targets = kotlin.targets.filter { + it.platformType == KotlinPlatformType.native && + it.name.contains("macos") + }.toTypedArray() + ) + distributions { + macOS { + targetFormats(TargetFormat.Dmg) + packageName = "Test Resources" + packageVersion = "1.0.0" + } + } + } +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/gradle.properties b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/gradle.properties new file mode 100644 index 00000000000..dc1dcfb61b8 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8096M +org.jetbrains.compose.experimental.macos.enabled=true \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/settings.gradle.kts b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/settings.gradle.kts new file mode 100644 index 00000000000..a9407f5c395 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/settings.gradle.kts @@ -0,0 +1,26 @@ +rootProject.name = "appleResources" +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + google() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://packages.jetbrains.team/maven/p/kt/dev") + } + plugins { + id("org.jetbrains.kotlin.multiplatform").version("KOTLIN_VERSION_PLACEHOLDER") + id("org.jetbrains.kotlin.plugin.compose").version("KOTLIN_VERSION_PLACEHOLDER") + id("org.jetbrains.kotlin.native.cocoapods").version("KOTLIN_VERSION_PLACEHOLDER") + id("org.jetbrains.compose").version("COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER") + } +} +dependencyResolutionManagement { + repositories { + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://packages.jetbrains.team/maven/p/kt/dev") + mavenCentral() + gradlePluginPortal() + google() + mavenLocal() + } +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/composeResources/drawable/compose-multiplatform.xml b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/composeResources/drawable/compose-multiplatform.xml new file mode 100644 index 00000000000..d7bf7955f44 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/composeResources/drawable/compose-multiplatform.xml @@ -0,0 +1,36 @@ + + + + + + + + diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/kotlin/App.kt b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/kotlin/App.kt new file mode 100644 index 00000000000..504f3cf5137 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/commonMain/kotlin/App.kt @@ -0,0 +1,37 @@ +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import org.jetbrains.compose.resources.painterResource +import appleresources.generated.resources.* + +@Composable +fun App() { + MaterialTheme { + var greetingText by remember { mutableStateOf("Hello, World!") } + var showImage by remember { mutableStateOf(false) } + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Button(onClick = { + showImage = !showImage + }) { + Text(greetingText) + } + AnimatedVisibility(showImage) { + Image( + painterResource(Res.drawable.compose_multiplatform), + null + ) + } + } + } +} diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/composeResources/drawable/icon.xml b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/composeResources/drawable/icon.xml new file mode 100644 index 00000000000..d7bf7955f44 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/composeResources/drawable/icon.xml @@ -0,0 +1,36 @@ + + + + + + + + diff --git a/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/kotlin/main.kt b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/kotlin/main.kt new file mode 100644 index 00000000000..46f2e8b1c56 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/macosNativeResources/src/macosMain/kotlin/main.kt @@ -0,0 +1,2 @@ + +fun main() {} \ No newline at end of file From 205b7d2ada165ac30756ede55c3697e416f7e94c Mon Sep 17 00:00:00 2001 From: sanlorng Date: Thu, 18 Sep 2025 21:07:11 +0800 Subject: [PATCH 2/5] Refactor: Use FileCollection for composeResourcesDirs This commit changes the type of `composeResourcesDirs` in `AbstractNativeMacApplicationPackageAppDirTask` from `ListProperty` to `Property`. This change simplifies the configuration of native application packaging by directly using `project.files` to gather resource directories from Kotlin source sets. --- .../application/internal/configureNativeApplication.kt | 4 ++-- .../tasks/AbstractNativeMacApplicationPackageAppDirTask.kt | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt index 233d5b35665..ad9b338f082 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt @@ -72,9 +72,9 @@ private fun configureNativeApplication( app.distributions.copyright ?: "Copyright (C) ${Calendar.getInstance().get(Calendar.YEAR)}" }) if (binary.outputKind == NativeOutputKind.EXECUTABLE) { - val binaryResources = (binary.compilation.associatedCompilations + binary.compilation).flatMap { compilation -> + val binaryResources = project.files((binary.compilation.associatedCompilations + binary.compilation).flatMap { compilation -> compilation.allKotlinSourceSets.map { it.resources } - } + }) inputs.files(binaryResources) composeResourcesDirs.set(provider { binaryResources }) } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt index 3113bb71e23..daa014976da 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt @@ -5,9 +5,8 @@ package org.jetbrains.compose.desktop.application.tasks +import org.gradle.api.file.FileCollection import org.gradle.api.file.RegularFileProperty -import org.gradle.api.file.SourceDirectorySet -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.api.tasks.Optional @@ -48,7 +47,7 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac @get:InputFiles @get:Optional @get:PathSensitive(PathSensitivity.ABSOLUTE) - val composeResourcesDirs: ListProperty = objects.listProperty(SourceDirectorySet::class.java) + val composeResourcesDirs: Property = objects.nullableProperty() override fun createPackage(destinationDir: File, workingDir: File) { val packageName = packageName.get() From 558c25b089a4d0ea2896648cee1aee0580b8fa07 Mon Sep 17 00:00:00 2001 From: sanlorng Date: Thu, 18 Sep 2025 22:38:20 +0800 Subject: [PATCH 3/5] The `composeResourcesDirs` property in `AbstractNativeMacApplicationPackageAppDirTask` was changed from `Property` to `ConfigurableFileCollection`. --- .../application/internal/configureNativeApplication.kt | 7 +++---- .../AbstractNativeMacApplicationPackageAppDirTask.kt | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt index ad9b338f082..18f59673658 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt @@ -18,7 +18,6 @@ import org.jetbrains.compose.desktop.tasks.AbstractUnpackDefaultComposeApplicati import org.jetbrains.compose.internal.utils.OS import org.jetbrains.compose.internal.utils.currentOS import org.jetbrains.compose.internal.utils.joinLowerCamelCase -import org.jetbrains.compose.internal.utils.provider import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind @@ -72,11 +71,11 @@ private fun configureNativeApplication( app.distributions.copyright ?: "Copyright (C) ${Calendar.getInstance().get(Calendar.YEAR)}" }) if (binary.outputKind == NativeOutputKind.EXECUTABLE) { - val binaryResources = project.files((binary.compilation.associatedCompilations + binary.compilation).flatMap { compilation -> + val binaryResources = (binary.compilation.associatedCompilations + binary.compilation).flatMap { compilation -> compilation.allKotlinSourceSets.map { it.resources } - }) + } inputs.files(binaryResources) - composeResourcesDirs.set(provider { binaryResources }) + composeResourcesDirs.setFrom(binaryResources) } } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt index daa014976da..44686e3e9df 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt @@ -5,7 +5,7 @@ package org.jetbrains.compose.desktop.application.tasks -import org.gradle.api.file.FileCollection +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* @@ -47,7 +47,7 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac @get:InputFiles @get:Optional @get:PathSensitive(PathSensitivity.ABSOLUTE) - val composeResourcesDirs: Property = objects.nullableProperty() + val composeResourcesDirs: ConfigurableFileCollection = objects.fileCollection() override fun createPackage(destinationDir: File, workingDir: File) { val packageName = packageName.get() @@ -68,9 +68,9 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac writeToFile(contentsDir.resolve("Info.plist")) } - composeResourcesDirs.orNull?.let { resources -> + if (!composeResourcesDirs.isEmpty) { fileOperations.copy { copySpec -> - copySpec.from(resources) + copySpec.from(composeResourcesDirs) copySpec.into(appResourcesDir.resolve("compose-resources").apply { mkdirs() }) } } From 518d8b111fc076c2fb16cd95635d0968d669656c Mon Sep 17 00:00:00 2001 From: Sanlorng Date: Tue, 30 Sep 2025 11:18:02 +0800 Subject: [PATCH 4/5] Remove useless code --- .../desktop/application/internal/configureNativeApplication.kt | 1 - .../jetbrains/compose/test/tests/integration/ResourcesTest.kt | 3 --- 2 files changed, 4 deletions(-) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt index 18f59673658..254e77cfd23 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureNativeApplication.kt @@ -74,7 +74,6 @@ private fun configureNativeApplication( val binaryResources = (binary.compilation.associatedCompilations + binary.compilation).flatMap { compilation -> compilation.allKotlinSourceSets.map { it.resources } } - inputs.files(binaryResources) composeResourcesDirs.setFrom(binaryResources) } } diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index f293435fd54..ad03f27a59a 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -1031,9 +1031,6 @@ class ResourcesTest : GradlePluginTestBase() { Assumptions.assumeTrue(currentOS == OS.MacOS) with(testProject("misc/macosNativeResources")) { val appName = "Test Resources" - gradle(":createDistributableNativeDebugMacosX64", "--dry-run").checks { - check.taskSkipped(":createDistributableNativeDebugMacosX64") - } gradle(":createDistributableNativeDebugMacosX64").checks { val targetResourcesDir = "build/compose/binaries/main/native-macosX64-debug-app-image/${appName}.app/Contents/Resources" file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/compose-multiplatform.xml").checkExists() From 40466b66e5fe61b5ab686bf5a2028be452874f57 Mon Sep 17 00:00:00 2001 From: Sanlorng Date: Tue, 30 Sep 2025 22:35:29 +0800 Subject: [PATCH 5/5] Test cases: check macOS executable resources are updated on change Added integration tests to verify that: - Modifying a resource triggers an update in the `.app` bundle. - Deleting a resource removes it from the `.app` bundle. --- .../test/tests/integration/ResourcesTest.kt | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index ad03f27a59a..7fe128f87cd 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -13,6 +13,7 @@ import org.jetbrains.compose.test.utils.TestProject import org.jetbrains.compose.test.utils.assertEqualTextFiles import org.jetbrains.compose.test.utils.assertNotEqualTextFiles import org.jetbrains.compose.test.utils.checkExists +import org.jetbrains.compose.test.utils.checkNotExists import org.jetbrains.compose.test.utils.checks import org.jetbrains.compose.test.utils.modify import org.junit.jupiter.api.Assumptions @@ -1039,6 +1040,75 @@ class ResourcesTest : GradlePluginTestBase() { } } + @Test + fun macosExecutableResourcesWithResourceChanged() { + Assumptions.assumeTrue(currentOS == OS.MacOS) + with(testProject("misc/macosNativeResources")) { + val appName = "Test Resources" + val taskName = ":createDistributableNativeDebugMacosX64" + val comment = "" + val fileNames = listOf( + "compose-multiplatform.xml", + "icon.xml" + ) + val targetResourcesDir = "build/compose/binaries/main/native-macosX64-debug-app-image/${appName}.app/Contents/Resources/compose-resources/composeResources/appleresources.generated.resources/drawable/" + gradle(taskName).checks { + fileNames.forEach { name -> + check(!file(targetResourcesDir + name).readText().startsWith(comment)) { + "The resources file contains the test content before change" + } + } + } + + listOf( + "src/commonMain/composeResources/drawable/compose-multiplatform.xml", + "src/macosMain/composeResources/drawable/icon.xml" + ).forEach { path -> + file(path).modify { + comment + it + } + } + gradle(taskName).checks { + check.taskSuccessful(taskName) + fileNames.forEach { name -> + check(file(targetResourcesDir + name).readText().startsWith(comment)) { + "The resources file does not contain the test content after changed" + } + } + } + } + } + + @Test + fun macosExecutableResourcesWithResourceDeleted() { + Assumptions.assumeTrue(currentOS == OS.MacOS) + with(testProject("misc/macosNativeResources")) { + val appName = "Test Resources" + val taskName = ":createDistributableNativeDebugMacosX64" + + val targetResource = "src/commonMain/composeResources/drawable/compose-multiplatform2.xml" + file(targetResource).apply { + check(createNewFile()) + writeText(file(targetResource.replace("compose-multiplatform2", "compose-multiplatform")).readText()) + } + + gradle(taskName).checks { + val targetResourcesDir = "build/compose/binaries/main/native-macosX64-debug-app-image/${appName}.app/Contents/Resources" + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/compose-multiplatform.xml").checkExists() + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/compose-multiplatform2.xml").checkExists() + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/icon.xml").checkExists() + } + check(file(targetResource).delete()) + gradle(taskName).checks { + check.taskSuccessful(taskName) + val targetResourcesDir = "build/compose/binaries/main/native-macosX64-debug-app-image/${appName}.app/Contents/Resources" + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/compose-multiplatform.xml").checkExists() + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/compose-multiplatform2.xml").checkNotExists() + file("$targetResourcesDir/compose-resources/composeResources/appleresources.generated.resources/drawable/icon.xml").checkExists() + } + } + } + @Test fun iosTestResources() { Assumptions.assumeTrue(currentOS == OS.MacOS)