Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ 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 }
}
composeResourcesDirs.setFrom(binaryResources)
}
}

if (TargetFormat.Dmg in app.distributions.targetFormats) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package org.jetbrains.compose.desktop.application.tasks

import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
Expand Down Expand Up @@ -43,6 +44,11 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac
@get:Optional
val minimumSystemVersion: Property<String?> = objects.nullableProperty()

@get:InputFiles
@get:Optional
@get:PathSensitive(PathSensitivity.ABSOLUTE)
val composeResourcesDirs: ConfigurableFileCollection = objects.fileCollection()

override fun createPackage(destinationDir: File, workingDir: File) {
val packageName = packageName.get()
val appDir = destinationDir.resolve("$packageName.app").apply { mkdirs() }
Expand All @@ -61,6 +67,13 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac
setupInfoPlist(executableName = appExecutableFile.name)
writeToFile(contentsDir.resolve("Info.plist"))
}

if (!composeResourcesDirs.isEmpty) {
fileOperations.copy { copySpec ->
copySpec.from(composeResourcesDirs)
copySpec.into(appResourcesDir.resolve("compose-resources").apply { mkdirs() })
}
}
}

private fun InfoPlistBuilder.setupInfoPlist(executableName: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1026,6 +1027,88 @@ class ResourcesTest : GradlePluginTestBase() {
}
}

@Test
fun macosExecutableResources() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
with(testProject("misc/macosNativeResources")) {
val appName = "Test Resources"
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 macosExecutableResourcesWithResourceChanged() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
with(testProject("misc/macosNativeResources")) {
val appName = "Test Resources"
val taskName = ":createDistributableNativeDebugMacosX64"
val comment = "<!-- Test resources changed -->"
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx8096M
org.jetbrains.compose.experimental.macos.enabled=true
Original file line number Diff line number Diff line change
@@ -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()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="600dp"
android:height="600dp"
android:viewportWidth="600"
android:viewportHeight="600">
<path
android:pathData="M301.21,418.53C300.97,418.54 300.73,418.56 300.49,418.56C297.09,418.59 293.74,417.72 290.79,416.05L222.6,377.54C220.63,376.43 219,374.82 217.85,372.88C216.7,370.94 216.09,368.73 216.07,366.47L216.07,288.16C216.06,287.32 216.09,286.49 216.17,285.67C216.38,283.54 216.91,281.5 217.71,279.6L199.29,268.27L177.74,256.19C175.72,260.43 174.73,265.23 174.78,270.22L174.79,387.05C174.85,393.89 178.57,400.2 184.53,403.56L286.26,461.02C290.67,463.51 295.66,464.8 300.73,464.76C300.91,464.76 301.09,464.74 301.27,464.74C301.24,449.84 301.22,439.23 301.22,439.23L301.21,418.53Z"
android:fillColor="#041619"
android:fillType="nonZero"/>
<path
android:pathData="M409.45,242.91L312.64,188.23C303.64,183.15 292.58,183.26 283.68,188.51L187.92,245C183.31,247.73 179.93,251.62 177.75,256.17L177.74,256.19L199.29,268.27L217.71,279.6C217.83,279.32 217.92,279.02 218.05,278.74C218.24,278.36 218.43,277.98 218.64,277.62C219.06,276.88 219.52,276.18 220.04,275.51C221.37,273.8 223.01,272.35 224.87,271.25L289.06,233.39C290.42,232.59 291.87,231.96 293.39,231.51C295.53,230.87 297.77,230.6 300,230.72C302.98,230.88 305.88,231.73 308.47,233.2L373.37,269.85C375.54,271.08 377.49,272.68 379.13,274.57C379.68,275.19 380.18,275.85 380.65,276.53C380.86,276.84 381.05,277.15 381.24,277.47L397.79,266.39L420.34,252.93L420.31,252.88C417.55,248.8 413.77,245.35 409.45,242.91Z"
android:fillColor="#37BF6E"
android:fillType="nonZero"/>
<path
android:pathData="M381.24,277.47C381.51,277.92 381.77,278.38 382.01,278.84C382.21,279.24 382.39,279.65 382.57,280.06C382.91,280.88 383.19,281.73 383.41,282.59C383.74,283.88 383.92,285.21 383.93,286.57L383.93,361.1C383.96,363.95 383.35,366.77 382.16,369.36C381.93,369.86 381.69,370.35 381.42,370.83C379.75,373.79 377.32,376.27 374.39,378L310.2,415.87C307.47,417.48 304.38,418.39 301.21,418.53L301.22,439.23C301.22,439.23 301.24,449.84 301.27,464.74C306.1,464.61 310.91,463.3 315.21,460.75L410.98,404.25C419.88,399 425.31,389.37 425.22,379.03L425.22,267.85C425.17,262.48 423.34,257.34 420.34,252.93L397.79,266.39L381.24,277.47Z"
android:fillColor="#3870B2"
android:fillType="nonZero"/>
<path
android:pathData="M177.75,256.17C179.93,251.62 183.31,247.73 187.92,245L283.68,188.51C292.58,183.26 303.64,183.15 312.64,188.23L409.45,242.91C413.77,245.35 417.55,248.8 420.31,252.88L420.34,252.93L498.59,206.19C494.03,199.46 487.79,193.78 480.67,189.75L320.86,99.49C306.01,91.1 287.75,91.27 273.07,99.95L114.99,193.2C107.39,197.69 101.81,204.11 98.21,211.63L177.74,256.19L177.75,256.17ZM301.27,464.74C301.09,464.74 300.91,464.76 300.73,464.76C295.66,464.8 290.67,463.51 286.26,461.02L184.53,403.56C178.57,400.2 174.85,393.89 174.79,387.05L174.78,270.22C174.73,265.23 175.72,260.43 177.74,256.19L98.21,211.63C94.86,218.63 93.23,226.58 93.31,234.82L93.31,427.67C93.42,438.97 99.54,449.37 109.4,454.92L277.31,549.77C284.6,553.88 292.84,556.01 301.2,555.94L301.2,555.8C301.39,543.78 301.33,495.26 301.27,464.74Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M498.59,206.19L420.34,252.93C423.34,257.34 425.17,262.48 425.22,267.85L425.22,379.03C425.31,389.37 419.88,399 410.98,404.25L315.21,460.75C310.91,463.3 306.1,464.61 301.27,464.74C301.33,495.26 301.39,543.78 301.2,555.8L301.2,555.94C309.48,555.87 317.74,553.68 325.11,549.32L483.18,456.06C497.87,447.39 506.85,431.49 506.69,414.43L506.69,230.91C506.6,222.02 503.57,213.5 498.59,206.19Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M301.2,555.94C292.84,556.01 284.6,553.88 277.31,549.76L109.4,454.92C99.54,449.37 93.42,438.97 93.31,427.67L93.31,234.82C93.23,226.58 94.86,218.63 98.21,211.63C101.81,204.11 107.39,197.69 114.99,193.2L273.07,99.95C287.75,91.27 306.01,91.1 320.86,99.49L480.67,189.75C487.79,193.78 494.03,199.46 498.59,206.19C503.57,213.5 506.6,222.02 506.69,230.91L506.69,414.43C506.85,431.49 497.87,447.39 483.18,456.06L325.11,549.32C317.74,553.68 309.48,555.87 301.2,555.94Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
</vector>
Original file line number Diff line number Diff line change
@@ -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
)
}
}
}
}
Loading
Loading