From 1ec7e404fda43de33de040261f9010861783700f Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 2 Dec 2022 16:04:41 +0100 Subject: [PATCH] [Gradle] Implement MppIdeDependencyResolutionIT.testCommonizedPlatformDependencyResolution ^KT-55238 Verification Pending --- .../IdeaKotlinDependencyInstanceMatcher.kt | 17 +++ .../tcs/ideaDependencyMatcherBuilders.kt | 1 + .../build.gradle.kts | 5 + .../gradle/MppIdeDependencyResolutionIT.kt | 139 ++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/IdeaKotlinDependencyInstanceMatcher.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MppIdeDependencyResolutionIT.kt diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/IdeaKotlinDependencyInstanceMatcher.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/IdeaKotlinDependencyInstanceMatcher.kt new file mode 100644 index 00000000000..e4a51956443 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/IdeaKotlinDependencyInstanceMatcher.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.idea.testFixtures.tcs + +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency + +internal class IdeaKotlinDependencyInstanceMatcher(private val instance: IdeaKotlinDependency) : IdeaKotlinDependencyMatcher { + override val description: String + get() = instance.toString() + + override fun matches(dependency: IdeaKotlinDependency): Boolean { + return instance == dependency + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/ideaDependencyMatcherBuilders.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/ideaDependencyMatcherBuilders.kt index 6135ae0f900..5404e1a8a58 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/ideaDependencyMatcherBuilders.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/idea/testFixtures/tcs/ideaDependencyMatcherBuilders.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency fun buildIdeaKotlinDependencyMatchers(notation: Any?): List { return when (notation) { null -> return emptyList() + is IdeaKotlinDependency -> listOf(IdeaKotlinDependencyInstanceMatcher(notation)) is IdeaKotlinDependencyMatcher -> listOf(notation) is Iterable<*> -> notation.flatMap { buildIdeaKotlinDependencyMatchers(it) } else -> error("Can't build ${IdeaKotlinSourceDependencyMatcher::class.java.simpleName} from $notation") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts index 0387ea3cddb..b551b5829f1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts @@ -54,6 +54,11 @@ dependencies { requireCapability("org.jetbrains.kotlin:atomicfu-common") } } + + testImplementation(project(":kotlin-gradle-plugin-idea")) + testImplementation(testFixtures(project(":kotlin-gradle-plugin-idea"))) + testImplementation(project(":kotlin-gradle-plugin-idea-proto")) + testImplementation(project(":kotlin-gradle-plugin-model")) testImplementation(project(":kotlin-gradle-build-metrics")) testImplementation(project(":kotlin-project-model")) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MppIdeDependencyResolutionIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MppIdeDependencyResolutionIT.kt new file mode 100644 index 00000000000..db94101b039 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MppIdeDependencyResolutionIT.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle + +import org.gradle.testkit.runner.BuildResult +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.commonizer.CommonizerTarget +import org.jetbrains.kotlin.commonizer.identityString +import org.jetbrains.kotlin.gradle.idea.proto.tcs.IdeaKotlinDependency +import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinSerializationContext +import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinSerializationLogger +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency +import org.jetbrains.kotlin.gradle.idea.tcs.extras.isCommonized +import org.jetbrains.kotlin.gradle.idea.tcs.extras.isNativeDistribution +import org.jetbrains.kotlin.gradle.idea.tcs.extras.isNativeStdlib +import org.jetbrains.kotlin.gradle.idea.tcs.extras.klibExtra +import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches +import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.binaryCoordinates +import org.jetbrains.kotlin.gradle.plugin.ide.kotlinExtrasSerialization +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.konan.target.KonanTarget.* +import org.junit.jupiter.api.DisplayName +import java.io.File +import kotlin.test.assertEquals +import kotlin.test.fail + +@MppGradlePluginTests +@DisplayName("Multiplatform IDE dependency resolution") +class MppIdeDependencyResolutionIT : KGPBaseTest() { + @GradleTest + fun testCommonizedPlatformDependencyResolution(gradleVersion: GradleVersion) { + with(project("commonizeHierarchically", gradleVersion)) { + resolveIdeDependencies(":p1") { dependencies -> + if (task(":commonizeNativeDistribution") == null) fail("Missing :commonizeNativeDistribution task") + + fun Iterable.filterNativePlatformDependencies() = + filterIsInstance() + .filter { !it.isNativeStdlib } + .filter { it.isNativeDistribution } + .filter { it.binaryType == CLASSPATH_BINARY_TYPE } + + val nativeMainDependencies = dependencies.getDependenciesForSourceSet("nativeMain").filterNativePlatformDependencies() + val nativeTestDependencies = dependencies.getDependenciesForSourceSet("nativeTest").filterNativePlatformDependencies() + val linuxMainDependencies = dependencies.getDependenciesForSourceSet("linuxMain").filterNativePlatformDependencies() + val linuxTestDependencies = dependencies.getDependenciesForSourceSet("linuxTest").filterNativePlatformDependencies() + + /* Check test and main receive the same dependencies */ + run { + nativeMainDependencies.assertMatches(nativeTestDependencies) + linuxMainDependencies.assertMatches(linuxTestDependencies) + } + + /* Check all dependencies are marked as commonized and commonizer target match */ + run { + nativeMainDependencies.plus(linuxMainDependencies).forEach { dependency -> + if (!dependency.isCommonized) fail("$dependency is not marked as 'isCommonized'") + } + + val nativeMainTarget = CommonizerTarget( + LINUX_X64, LINUX_ARM64, MACOS_X64, MACOS_ARM64, IOS_X64, IOS_ARM64, IOS_SIMULATOR_ARM64, MINGW_X64, MINGW_X86 + ) + + nativeMainDependencies.forEach { dependency -> + assertEquals(nativeMainTarget.identityString, dependency.klibExtra?.commonizerTarget) + } + + val linuxMainTarget = CommonizerTarget(LINUX_X64, LINUX_ARM64) + linuxMainDependencies.forEach { dependency -> + assertEquals(linuxMainTarget.identityString, dependency.klibExtra?.commonizerTarget) + } + } + + /* Find posix library */ + run { + nativeMainDependencies.assertMatches( + binaryCoordinates(Regex("org\\.jetbrains\\.kotlin\\.native:platform.posix:.*")), + binaryCoordinates(Regex("org\\.jetbrains\\.kotlin\\.native:platform.*")) + ) + + linuxMainDependencies.assertMatches( + binaryCoordinates(Regex("org\\.jetbrains\\.kotlin\\.native:platform.posix:.*")), + binaryCoordinates(Regex("org\\.jetbrains\\.kotlin\\.native:platform.*")) + ) + } + } + } + } +} + +private fun TestProject.resolveIdeDependencies( + subproject: String? = null, + assertions: BuildResult.(dependencies: ResolvedIdeDependencies) -> Unit +) { + build("${subproject.orEmpty()}:resolveIdeDependencies") { + val subprojectPathPrefix = subproject?.removePrefix(":")?.replace(":", "/")?.plus("/") ?: "" + val output = projectPath.resolve("${subprojectPathPrefix}build/ide/dependencies/proto").toFile() + if (!output.isDirectory) fail("Missing output directory: $output") + + val dependenciesBySourceSetName = output.listFiles().orEmpty().associate { sourceSetDirectory -> + if (!sourceSetDirectory.isDirectory) fail("Expected $sourceSetDirectory to be directory") + val serializedDependencyFiles = sourceSetDirectory.listFiles().orEmpty() + val deserializedDependencies = serializedDependencyFiles.map { dependencyFile -> + deserializeIdeaKotlinDependencyOrFail(dependencyFile) + } + + sourceSetDirectory.name to deserializedDependencies.toSet() + } + + assertions(ResolvedIdeDependencies(dependenciesBySourceSetName)) + } +} + + +private fun deserializeIdeaKotlinDependencyOrFail(file: File): IdeaKotlinDependency { + return GradleIntegrationTestIdeaKotlinSerializationContext.IdeaKotlinDependency(file.readBytes()) + ?: fail("Failed to deserialize dependency. $file") +} + +private object GradleIntegrationTestIdeaKotlinSerializationContext : IdeaKotlinSerializationContext { + override val extrasSerializationExtension = kotlinExtrasSerialization + override val logger: IdeaKotlinSerializationLogger = object : IdeaKotlinSerializationLogger { + override fun report(severity: IdeaKotlinSerializationLogger.Severity, message: String, cause: Throwable?) { + println("$severity: $message") + if (cause != null) println(cause.stackTraceToString()) + } + } +} + +private class ResolvedIdeDependencies( + private val dependencies: Map> +) { + fun getDependenciesForSourceSet(sourceSetName: String) = dependencies[sourceSetName] + ?: fail("SourceSet with name $sourceSetName not found. Found: ${dependencies.keys}") +}