diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/PlatformSettings.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/PlatformSettings.kt index dd05d501241..a02a1b0fe9c 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/PlatformSettings.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/PlatformSettings.kt @@ -6,6 +6,7 @@ package org.jetbrains.compose.desktop.application.dsl import org.gradle.api.Action +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory import java.io.File @@ -35,6 +36,7 @@ abstract class AbstractMacOSPlatformSettings : AbstractPlatformSettings() { var dmgPackageBuildVersion: String? = null var appCategory: String? = null var minimumSystemVersion: String? = null + var layeredIconDir: DirectoryProperty = objects.directoryProperty() /** diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/InfoPlistBuilder.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/InfoPlistBuilder.kt index aca1d8e8588..9c4f0ce8367 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/InfoPlistBuilder.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/InfoPlistBuilder.kt @@ -101,6 +101,7 @@ internal object PlistKeys { val CFBundleTypeOSTypes by this val CFBundleExecutable by this val CFBundleIconFile by this + val CFBundleIconName by this val CFBundleIdentifier by this val CFBundleInfoDictionaryVersion by this val CFBundleName by this diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/MacAssetsTool.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/MacAssetsTool.kt new file mode 100644 index 00000000000..c32745921d9 --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/MacAssetsTool.kt @@ -0,0 +1,105 @@ +package org.jetbrains.compose.desktop.application.internal + +import org.gradle.api.logging.Logger +import org.jetbrains.compose.internal.utils.MacUtils +import java.io.File +import java.io.StringReader +import javax.xml.parsers.DocumentBuilderFactory + +internal class MacAssetsTool(private val runTool: ExternalToolRunner, private val logger: Logger) { + + fun compileAssets(iconDir: File, workingDir: File, minimumSystemVersion: String?): File { + val toolVersion = checkAssetsToolVersion() + logger.info("compile mac assets is starting, supported actool version:$toolVersion") + + val result = runTool( + tool = MacUtils.xcrun, + args = listOf( + "actool", + iconDir.absolutePath, // Input asset catalog + "--compile", workingDir.absolutePath, + "--app-icon", iconDir.name.removeSuffix(".icon"), + "--enable-on-demand-resources", "NO", + "--development-region", "en", + "--target-device", "mac", + "--platform", "macosx", + "--enable-icon-stack-fallback-generation=disabled", + "--include-all-app-icons", + "--minimum-deployment-target", minimumSystemVersion ?: "10.13", + "--output-partial-info-plist", "/dev/null" + ), + ) + + if (result.exitValue != 0) { + error("Could not compile the layered icons directory into Assets.car.") + } + if (!assetsFile(workingDir).exists()) { + error("Could not find Assets.car in the working directory.") + } + return workingDir.resolve("Assets.car") + } + + fun assetsFile(workingDir: File): File = workingDir.resolve("Assets.car") + + private fun checkAssetsToolVersion(): String { + val requiredVersion = 26.0 + var outputContent = "" + val result = runTool( + tool = MacUtils.xcrun, + args = listOf("actool", "--version"), + processStdout = { outputContent = it }, + ) + + if (result.exitValue != 0) { + error("Could not get actool version: Command `xcrun actool -version` exited with code ${result.exitValue}\nStdOut: $outputContent\n") + } + + val versionString: String? = try { + val dbFactory = DocumentBuilderFactory.newInstance() + // Disable DTD loading to prevent XXE vulnerabilities and issues with network access or missing DTDs + dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false) + dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + val dBuilder = dbFactory.newDocumentBuilder() + val xmlInput = org.xml.sax.InputSource(StringReader(outputContent)) + val doc = dBuilder.parse(xmlInput) + doc.documentElement.normalize() // Recommended practice + val nodeList = doc.getElementsByTagName("key") + var version: String? = null + for (i in 0 until nodeList.length) { + if ("short-bundle-version" == nodeList.item(i).textContent) { + // Find the next sibling element which should be + var nextSibling = nodeList.item(i).nextSibling + while (nextSibling != null && nextSibling.nodeType != org.w3c.dom.Node.ELEMENT_NODE) { + nextSibling = nextSibling.nextSibling + } + if (nextSibling != null && nextSibling.nodeName == "string") { + version = nextSibling.textContent + break + } + } + } + version + } catch (e: Exception) { + error("Could not parse actool version XML from output: '$outputContent'. Error: ${e.message}") + } + + if (versionString == null) { + error("Could not extract short-bundle-version from actool output: '$outputContent'. Assuming it meets requirements.") + } + + val majorVersion = versionString.split(".").firstOrNull()?.toIntOrNull() + if (majorVersion == null) { + error("Could not get actool major version from version string '$versionString' . Output was: '$outputContent'. Assuming it meets requirements.") + } + + if (majorVersion < requiredVersion) { + error( + "Unsupported actool version: $versionString. " + + "Version $requiredVersion or higher is required. " + + "Please update your Xcode Command Line Tools." + ) + } else { + return versionString + } + } +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt index 35dec15a2fb..65126be8b97 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt @@ -413,6 +413,7 @@ internal fun JvmApplicationContext.configurePlatformSettings( packageTask.iconFile.set(mac.iconFile.orElse(defaultResources.get { macIcon })) packageTask.installationPath.set(mac.installationPath) packageTask.fileAssociations.set(provider { mac.fileAssociations }) + packageTask.macLayeredIcons.set(mac.layeredIconDir) } } } 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 254e77cfd23..9e632ee5235 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 @@ -76,6 +76,7 @@ private fun configureNativeApplication( } composeResourcesDirs.setFrom(binaryResources) } + macLayeredIcons.set(app.distributions.macOS.layeredIconDir) } if (TargetFormat.Dmg in app.distributions.targetFormats) { diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt index d1935673dbd..019e36bf1b8 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt @@ -37,6 +37,7 @@ import org.jetbrains.compose.desktop.application.internal.InfoPlistBuilder.InfoP import org.jetbrains.compose.desktop.application.internal.InfoPlistBuilder.InfoPlistValue.InfoPlistMapValue import org.jetbrains.compose.desktop.application.internal.InfoPlistBuilder.InfoPlistValue.InfoPlistStringValue import org.jetbrains.compose.desktop.application.internal.JvmRuntimeProperties +import org.jetbrains.compose.desktop.application.internal.MacAssetsTool import org.jetbrains.compose.desktop.application.internal.MacSigner import org.jetbrains.compose.desktop.application.internal.MacSignerImpl import org.jetbrains.compose.desktop.application.internal.NoCertificateSigner @@ -293,7 +294,11 @@ abstract class AbstractJPackageTask @Inject constructor( @get:Input internal val fileAssociations: SetProperty = objects.setProperty(FileAssociation::class.java) - + + @get:InputDirectory + @get:Optional + internal val macLayeredIcons: DirectoryProperty = objects.directoryProperty() + private val iconMapping by lazy { val icons = fileAssociations.get().mapNotNull { it.iconFile }.distinct() if (icons.isEmpty()) return@lazy emptyMap() @@ -344,6 +349,8 @@ abstract class AbstractJPackageTask @Inject constructor( } else null } + private val macAssetsTool by lazy { MacAssetsTool(runExternalTool, logger) } + @get:LocalState protected val signDir: Provider = project.layout.buildDirectory.dir("compose/tmp/sign") @@ -600,12 +607,28 @@ abstract class AbstractJPackageTask @Inject constructor( fileOperations.clearDirs(jpackageResources) if (currentOS == OS.MacOS) { + val systemVersion = macMinimumSystemVersion.orNull ?: "10.13" + + macLayeredIcons.ioFileOrNull?.let { layeredIcon -> + if (layeredIcon.exists()) { + runCatching { + macAssetsTool.compileAssets( + layeredIcon, + workingDir.ioFile, + systemVersion + ) + }.onFailure { + logger.warn("Can not compile layered icon: ${it.message}") + } + } + } + InfoPlistBuilder(macExtraPlistKeysRawXml.orNull) .also { setInfoPlistValues(it) } .writeToFile(jpackageResources.ioFile.resolve("Info.plist")) if (macAppStore.orNull == true) { - val systemVersion = macMinimumSystemVersion.orNull ?: "10.13" + val productDefPlistXml = """ os @@ -642,6 +665,12 @@ abstract class AbstractJPackageTask @Inject constructor( val appDir = destinationDir.ioFile.resolve("${packageName.get()}.app") val runtimeDir = appDir.resolve("Contents/runtime") + macAssetsTool.assetsFile(workingDir.ioFile).apply { + if (exists()) { + copyTo(appDir.resolve("Contents/Resources/Assets.car")) + } + } + // Add the provisioning profile macRuntimeProvisioningProfile.ioFileOrNull?.copyTo( target = runtimeDir.resolve("Contents/embedded.provisionprofile"), @@ -739,6 +768,10 @@ abstract class AbstractJPackageTask @Inject constructor( ) } } + + if (macAssetsTool.assetsFile(workingDir.ioFile).exists()) { + macLayeredIcons.orNull?.let { plist[PlistKeys.CFBundleIconName] = it.asFile.name.removeSuffix(".icon") } + } } } 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 44686e3e9df..7612f39ae9c 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,16 +6,19 @@ package org.jetbrains.compose.desktop.application.tasks import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.api.tasks.Optional import org.jetbrains.compose.desktop.application.internal.InfoPlistBuilder +import org.jetbrains.compose.desktop.application.internal.MacAssetsTool import org.jetbrains.compose.desktop.application.internal.PlistKeys import org.jetbrains.compose.internal.utils.ioFile import org.jetbrains.compose.internal.utils.notNullProperty import org.jetbrains.compose.internal.utils.nullableProperty import java.io.File +import kotlin.getValue private const val KOTLIN_NATIVE_MIN_SUPPORTED_MAC_OS = "10.13" @@ -49,6 +52,12 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac @get:PathSensitive(PathSensitivity.ABSOLUTE) val composeResourcesDirs: ConfigurableFileCollection = objects.fileCollection() + @get:InputDirectory + @get:Optional + internal val macLayeredIcons: DirectoryProperty = objects.directoryProperty() + + private val macAssetsTool by lazy { MacAssetsTool(runExternalTool, logger) } + override fun createPackage(destinationDir: File, workingDir: File) { val packageName = packageName.get() val appDir = destinationDir.resolve("$packageName.app").apply { mkdirs() } @@ -60,6 +69,18 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac executable.ioFile.copyTo(appExecutableFile) appExecutableFile.setExecutable(true) + macLayeredIcons.orNull?.let { + runCatching { + macAssetsTool.compileAssets( + iconDir = it.asFile, + workingDir = workingDir, + minimumSystemVersion = minimumSystemVersion.getOrElse(KOTLIN_NATIVE_MIN_SUPPORTED_MAC_OS) + ) + }.onFailure { error -> + logger.warn("Can not compile layered icon: ${error.message}") + } + } + val appIconFile = appResourcesDir.resolve("$packageName.icns") iconFile.ioFile.copyTo(appIconFile) @@ -74,6 +95,15 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac copySpec.into(appResourcesDir.resolve("compose-resources").apply { mkdirs() }) } } + + macAssetsTool.assetsFile(workingDir).let { + if (it.exists()) { + fileOperations.copy { copySpec -> + copySpec.from(it) + copySpec.into(appResourcesDir) + } + } + } } private fun InfoPlistBuilder.setupInfoPlist(executableName: String) { @@ -90,5 +120,9 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac this[PlistKeys.NSHumanReadableCopyright] = copyright.orNull this[PlistKeys.NSSupportsAutomaticGraphicsSwitching] = "true" this[PlistKeys.NSHighResolutionCapable] = "true" + + if (macAssetsTool.assetsFile(workingDir.ioFile).exists()) { + macLayeredIcons.orNull?.let { this[PlistKeys.CFBundleIconName] = it.asFile.name.removeSuffix(".icon") } + } } } \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt index b0f6e04b7c1..01a9c1e84c6 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt @@ -12,6 +12,7 @@ import org.jetbrains.compose.internal.utils.currentArch import org.jetbrains.compose.internal.utils.currentOS import org.jetbrains.compose.internal.utils.currentTarget import org.jetbrains.compose.internal.utils.uppercaseFirstChar +import org.jetbrains.compose.test.utils.ChecksWrapper import org.jetbrains.compose.test.utils.GradlePluginTestBase import org.jetbrains.compose.test.utils.JDK_11_BYTECODE_VERSION import org.jetbrains.compose.test.utils.ProcessRunResult @@ -366,6 +367,77 @@ class DesktopApplicationTest : GradlePluginTestBase() { } } + @Test + fun testMacLayeredIcon() { + Assumptions.assumeTrue(currentOS == OS.MacOS) + + with(testProject("application/macLayeredIcon")) { + val supportedString = "compile mac assets is starting, supported actool version:" + + fun ChecksWrapper.checkContent(buildDir: String) { + if (check.log.contains(supportedString)) { + val targetAssetsFile = file("${buildDir}/Test Layered Icon.app/Contents/Resources/Assets.car") + targetAssetsFile.checkExists() + } else { + Assert.assertTrue(check.log.contains("Can not compile layered icon:")) + } + } + + gradle(":packageDistributionForCurrentOS").checks { + checkContent(buildDir = "build/compose/binaries/main/app") + } + gradle(":createDistributableNativeDebugMacosX64").checks { + checkContent(buildDir = "build/compose/binaries/main/native-macosX64-debug-app-image") + } + } + } + + @Test + fun testMacLayeredIconRemove() { + Assumptions.assumeTrue(currentOS == OS.MacOS) + + with(testProject("application/macLayeredIcon")) { + val supportedString = "compile mac assets is starting, supported actool version:" + val unSupportedString = "Can not compile layered icon:" + + fun ChecksWrapper.checkContent(buildDir: String, hasLayeredIcon: Boolean = true) { + if (hasLayeredIcon) { + if (check.log.contains(supportedString)) { + val targetAssetsFile = + file("${buildDir}/Test Layered Icon.app/Contents/Resources/Assets.car") + targetAssetsFile.checkExists() + } else { + Assert.assertTrue(check.log.contains(unSupportedString)) + } + } else { + val targetAssetsFile = + file("${buildDir}/Test Layered Icon.app/Contents/Resources/Assets.car") + targetAssetsFile.checkNotExists() + Assert.assertTrue(!check.log.contains(supportedString) && !check.log.contains(unSupportedString)) + } + } + + gradle(":packageDistributionForCurrentOS").checks { + checkContent(buildDir = "build/compose/binaries/main/app") + } + gradle(":createDistributableNativeDebugMacosX64").checks { + checkContent(buildDir = "build/compose/binaries/main/native-macosX64-debug-app-image") + } + + file("build.gradle.kts").modify { + it.replace("layeredIconDir.set(project.file(\"subdir/kotlin_icon_big.icon\"))", "") + } + + gradle(":packageDistributionForCurrentOS").checks { + checkContent(buildDir = "build/compose/binaries/main/app", hasLayeredIcon = false) + } + gradle(":createDistributableNativeDebugMacosX64").checks { + checkContent(buildDir = "build/compose/binaries/main/native-macosX64-debug-app-image", hasLayeredIcon = false) + } + + } + } + private fun macSignProject( identity: String, keychainFilename: String, diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/Kotlin_icon_big.ico b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/Kotlin_icon_big.ico new file mode 100644 index 00000000000..3e9c11bb6d7 Binary files /dev/null and b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/Kotlin_icon_big.ico differ diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/Kotlin_icon_big.png b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/Kotlin_icon_big.png new file mode 100644 index 00000000000..1f93755e752 Binary files /dev/null and b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/Kotlin_icon_big.png differ diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/build.gradle.kts b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/build.gradle.kts new file mode 100644 index 00000000000..f257ec79ed5 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/build.gradle.kts @@ -0,0 +1,84 @@ +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 { + + jvm() + + 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) + } + } + + jvmMain { + dependencies { + implementation(compose.desktop.currentOs) + } + } + } +} + +compose.desktop { + val projectName = "Test Layered Icon" + application { + mainClass = "MainKt" + nativeDistributions { + packageName = projectName + packageVersion = "1.0.0" + targetFormats(TargetFormat.Dmg) + macOS { + dockName = "CustomDockName" + minimumSystemVersion = "12.0" + iconFile.set(project.file("subdir/Kotlin_icon_big.icns")) + layeredIconDir.set(project.file("subdir/kotlin_icon_big.icon")) + } + } + } + + nativeApplication { + + targets( + targets = kotlin.targets.filter { + it.platformType == KotlinPlatformType.native && + it.name.contains("macos") + }.toTypedArray() + ) + distributions { + targetFormats(TargetFormat.Dmg) + macOS { + packageName = projectName + packageVersion = "1.0.0" + iconFile.set(project.file("subdir/Kotlin_icon_big.icns")) + layeredIconDir.set(project.file("subdir/kotlin_icon_big.icon")) + } + } + } +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/gradle.properties b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/gradle.properties new file mode 100644 index 00000000000..5ead9e4ff64 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/gradle.properties @@ -0,0 +1 @@ +org.jetbrains.compose.experimental.macos.enabled=true \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/settings.gradle.kts b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/settings.gradle.kts new file mode 100644 index 00000000000..1bc1e8e917b --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/settings.gradle.kts @@ -0,0 +1,25 @@ +rootProject.name = "macLayeredIcon" +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.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/application/macLayeredIcon/src/jvmMain/kotlin/main.kt b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/src/jvmMain/kotlin/main.kt new file mode 100644 index 00000000000..857863db50b --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/src/jvmMain/kotlin/main.kt @@ -0,0 +1,3 @@ +fun main() { + println("Hello, from Mac OS!") +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/src/macosMain/kotlin/main.kt b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/src/macosMain/kotlin/main.kt new file mode 100644 index 00000000000..9b247094e44 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/src/macosMain/kotlin/main.kt @@ -0,0 +1,4 @@ + +fun main() { + println("Hello, from Mac OS!") +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/Kotlin_icon_big.icns b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/Kotlin_icon_big.icns new file mode 100644 index 00000000000..fedf6a3f2ff Binary files /dev/null and b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/Kotlin_icon_big.icns differ diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/kotlin_icon_big.icon/Assets/Kotlin_icon_big.png b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/kotlin_icon_big.icon/Assets/Kotlin_icon_big.png new file mode 100644 index 00000000000..1f93755e752 Binary files /dev/null and b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/kotlin_icon_big.icon/Assets/Kotlin_icon_big.png differ diff --git a/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/kotlin_icon_big.icon/icon.json b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/kotlin_icon_big.icon/icon.json new file mode 100644 index 00000000000..6c9ca59672f --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/application/macLayeredIcon/subdir/kotlin_icon_big.icon/icon.json @@ -0,0 +1,49 @@ +{ + "fill" : { + "automatic-gradient" : "extended-gray:1.00000,1.00000" + }, + "groups" : [ + { + "layers" : [ + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : "automatic" + } + ], + "image-name" : "Kotlin_icon_big.png", + "name" : "Kotlin_icon_big", + "position" : { + "scale" : 0.8, + "translation-in-points" : [ + -389.921875, + -310.6953125 + ] + } + } + ], + "position" : { + "scale" : 1, + "translation-in-points" : [ + 401.8984375, + 308.7890625 + ] + }, + "shadow" : { + "kind" : "neutral", + "opacity" : 0.5 + }, + "translucency" : { + "enabled" : true, + "value" : 0.5 + } + } + ], + "supported-platforms" : { + "circles" : [ + "watchOS" + ], + "squares" : "shared" + } +} \ No newline at end of file