Skip to content

Commit

Permalink
Turn off Compose resources sync when third party resources plugins ar… (
Browse files Browse the repository at this point in the history
#3469)

* Turn off Compose resources sync when third party resources plugins are used

* Fix Gradle 7.x test kit check
  • Loading branch information
AlexeyTsvetkov authored Aug 8, 2023
1 parent e888495 commit 44376fd
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,27 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.Framework
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import java.io.File

private val incompatiblePlugins = listOf(
"dev.icerock.mobile.multiplatform-resources",
"io.github.skeptick.libres",
)

internal fun Project.configureSyncTask(mppExt: KotlinMultiplatformExtension) {
if (!IosGradleProperties.syncResources(providers).get()) return
fun reportSyncIsDisabled(reason: String) {
logger.info("Compose Multiplatform resource management for iOS is disabled: $reason")
}

if (!IosGradleProperties.syncResources(providers).get()) {
reportSyncIsDisabled("'${IosGradleProperties.SYNC_RESOURCES_PROPERTY}' value is 'false'")
return
}

for (incompatiblePluginId in incompatiblePlugins) {
if (project.plugins.hasPlugin(incompatiblePluginId)) {
reportSyncIsDisabled("resource management is not compatible with '$incompatiblePluginId'")
return
}
}

with (SyncIosResourcesContext(project, mppExt)) {
configureSyncResourcesTasks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ package org.jetbrains.compose.test.tests.integration
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.PreviewLogger
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.RemoteConnection
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.receiveConfigFromGradle
import org.jetbrains.compose.internal.utils.OS
import org.jetbrains.compose.internal.utils.currentOS
import org.jetbrains.compose.test.utils.*
import org.junit.jupiter.api.Assumptions

import java.net.ServerSocket
import java.net.Socket
Expand All @@ -17,8 +20,94 @@ import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlin.concurrent.thread
import org.junit.jupiter.api.Test
import java.io.File

class GradlePluginTest : GradlePluginTestBase() {
private data class IosTestEnv(
val targetBuildDir: File,
val appDir: File,
val envVars: Map<String, String>
)

enum class IosPlatform(val id: String) {
SIMULATOR("iphonesimulator"), IOS("iphoneos")
}
enum class IosArch(val id: String) {
X64("x86_64"), ARM64("arm64")
}

enum class IosBuildConfiguration(val id: String) {
DEBUG("Debug"), RELEASE("Release")
}

private fun iosTestEnv(
platform: IosPlatform = IosPlatform.SIMULATOR,
arch: IosArch = IosArch.X64,
configuration: IosBuildConfiguration = IosBuildConfiguration.DEBUG
): IosTestEnv {
val targetBuildDir = testWorkDir.resolve("build/ios/${configuration.id}-${platform.id}").apply { mkdirs() }
val appDir = targetBuildDir.resolve("App.app").apply { mkdirs() }
val envVars = mapOf(
"PLATFORM_NAME" to platform.id,
"ARCHS" to arch.id,
"BUILT_PRODUCTS_DIR" to targetBuildDir.canonicalPath,
"CONTENTS_FOLDER_PATH" to appDir.name,
)
return IosTestEnv(
targetBuildDir = targetBuildDir,
appDir = appDir,
envVars = envVars
)
}

@Test
fun iosResources() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
val iosTestEnv = iosTestEnv()
val testEnv = defaultTestEnvironment.copy(
// for some reason configuration cache + test kit + custom vars does not work
useGradleConfigurationCache = false,
additionalEnvVars = iosTestEnv.envVars
)

with(testProject(TestProjects.iosResources, testEnv)) {
gradle(":embedAndSignAppleFrameworkForXcode", "--dry-run").checks {
// This test is not intended to actually run embedAndSignAppleFrameworkForXcode.
// Instead, it should check that embedAndSign depends on syncComposeResources using dry run
check.taskSkipped(":syncComposeResourcesForIos")
check.taskSkipped(":embedAndSignAppleFrameworkForXcode")
}
gradle(":syncComposeResourcesForIos").checks {
check.taskSuccessful(":syncComposeResourcesForIos")
iosTestEnv.appDir.resolve("compose-resources/compose-multiplatform.xml").checkExists()
}
}
}

@Test
fun iosMokoResources() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
val iosTestEnv = iosTestEnv()
val testEnv = defaultTestEnvironment.copy(
// for some reason configuration cache + test kit + custom vars does not work
useGradleConfigurationCache = false,
additionalEnvVars = iosTestEnv.envVars
)
with(testProject(TestProjects.iosMokoResources, testEnv)) {
gradle(
":embedAndSignAppleFrameworkForXcode",
":copyFrameworkResourcesToApp",
"--dry-run",
"--info"
).checks {
// This test is not intended to actually run embedAndSignAppleFrameworkForXcode.
// Instead, it should check that the sync disables itself.
check.logContains("Compose Multiplatform resource management for iOS is disabled")
check.logDoesntContain(":syncComposeResourcesForIos")
}
}
}

@Test
fun skikoWasm() = with(
testProject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ data class TestEnvironment(
val workingDir: File,
val kotlinVersion: String = TestKotlinVersions.Default,
val composeGradlePluginVersion: String = TestProperties.composeGradlePluginVersion,
val mokoResourcesPluginVersion: String = "0.23.0",
val composeCompilerPlugin: String? = null,
val composeCompilerArgs: String? = null,
val composeVerbose: Boolean = true,
val useGradleConfigurationCache: Boolean = TestProperties.gradleConfigurationCache,
val additionalEnvVars: Map<String, String> = mapOf()
) {
private val placeholders = linkedMapOf(
"COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER" to composeGradlePluginVersion,
"KOTLIN_VERSION_PLACEHOLDER" to kotlinVersion,
"COMPOSE_COMPILER_PLUGIN_PLACEHOLDER" to composeCompilerPlugin,
"COMPOSE_COMPILER_PLUGIN_ARGS_PLACEHOLDER" to composeCompilerArgs,
"MOKO_RESOURCES_PLUGIN_VERSION_PLACEHOLDER" to mokoResourcesPluginVersion,
)

fun replacePlaceholdersInFile(file: File) {
Expand Down Expand Up @@ -121,6 +124,10 @@ class TestProject(
withGradleVersion(TestProperties.gradleVersionForTests)
withProjectDir(testEnvironment.workingDir)
withArguments(allArgs)
if (testEnvironment.additionalEnvVars.isNotEmpty()) {
val newEnv = HashMap(System.getenv() + testEnvironment.additionalEnvVars)
withEnvironment(newEnv)
}
forwardOutput()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ object TestProjects {
const val jsMpp = "misc/jsMpp"
const val skikoWasm = "misc/skikoWasm"
const val jvmPreview = "misc/jvmPreview"
const val iosResources = "misc/iosResources"
const val iosMokoResources = "misc/iosMokoResources"
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ internal class BuildResultChecks(private val result: BuildResult) {
taskOutcome(task, TaskOutcome.FROM_CACHE)
}

fun taskSkipped(task: String) {
// task outcome for skipped task is null in Gradle 7.x
if (result.task(task)?.outcome != null) {
taskOutcome(task, TaskOutcome.SKIPPED)
}
}

private fun taskOutcome(task: String, expectedOutcome: TaskOutcome) {
val actualOutcome = result.task(task)?.outcome
if (actualOutcome != expectedOutcome) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.compose"
id "dev.icerock.mobile.multiplatform-resources"
}

kotlin {
iosX64 {
binaries.framework {
baseName = "shared"
isStatic = true
}
}
iosArm64 {
binaries.framework {
baseName = "shared"
isStatic = true
}
}
iosSimulatorArm64 {
binaries.framework {
baseName = "shared"
isStatic = true
}
}

sourceSets {
def commonMain = named("commonMain") {
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation("dev.icerock.moko:resources-compose:MOKO_RESOURCES_PLUGIN_VERSION_PLACEHOLDER") // for compose multiplatform
}
}
def iosMain = create("iosMain") {
dependsOn(commonMain.get())
}
named("iosX64Main") {
dependsOn(iosMain)
}
named("iosArm64Main") {
dependsOn(iosMain)
}
named("iosSimulatorArm64Main") {
dependsOn(iosMain)
}
}
}

multiplatformResources {
multiplatformResourcesPackage = "org.example"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.jetbrains.compose.experimental.uikit.enabled=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pluginManagement {
plugins {
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER'
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER'
id 'dev.icerock.mobile.multiplatform-resources' version 'MOKO_RESOURCES_PLUGIN_VERSION_PLACEHOLDER'
}
repositories {
mavenLocal()
gradlePluginPortal()
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
}
}
rootProject.name = "iosResources"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable

@Composable
fun App() {
MaterialTheme {
Text("Hello, World!")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.compose"
}

kotlin {
iosX64 {
binaries.framework {
baseName = "shared"
isStatic = true
}
}
iosArm64 {
binaries.framework {
baseName = "shared"
isStatic = true
}
}
iosSimulatorArm64 {
binaries.framework {
baseName = "shared"
isStatic = true
}
}

sourceSets {
def commonMain = named("commonMain") {
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.components.resources)
}
}
def iosMain = create("iosMain") {
dependsOn(commonMain.get())
}
named("iosX64Main") {
dependsOn(iosMain)
}
named("iosArm64Main") {
dependsOn(iosMain)
}
named("iosSimulatorArm64Main") {
dependsOn(iosMain)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.jetbrains.compose.experimental.uikit.enabled=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pluginManagement {
plugins {
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER'
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER'
}
repositories {
mavenLocal()
gradlePluginPortal()
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
}
}
rootProject.name = "iosResources"
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource

@OptIn(ExperimentalResourceApi::class)
@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("compose-multiplatform.xml"),
null
)
}
}
}
}
Loading

0 comments on commit 44376fd

Please sign in to comment.