[Gradle] Migrate KlibBasedMppIT to new test DSL

^KT-65528 In Progress
This commit is contained in:
Yahor Berdnikau
2024-02-05 16:26:59 +01:00
committed by Space Team
parent 74c02a77fe
commit 55bbaec3f9
4 changed files with 431 additions and 299 deletions
@@ -19,9 +19,10 @@ class K2HierarchicalMppIT : HierarchicalMppIT() {
override val defaultBuildOptions: BuildOptions get() = super.defaultBuildOptions.copy(languageVersion = "2.0")
}
@Ignore
@MppGradlePluginTests
@DisplayName("KLibs in K2")
class K2KlibBasedMppIT : KlibBasedMppIT() {
override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(languageVersion = "2.0")
override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions.copyEnsuringK2()
}
@Ignore
@@ -5,240 +5,401 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.modify
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.replaceText
import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assume
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.OS
import org.junit.jupiter.api.io.TempDir
import java.io.File
import java.nio.file.Path
import java.util.*
import java.util.zip.ZipFile
import kotlin.test.Test
import kotlin.io.path.appendText
import kotlin.io.path.deleteRecursively
import kotlin.io.path.isDirectory
import kotlin.io.path.writeText
import kotlin.test.assertFalse
import kotlin.test.assertTrue
open class KlibBasedMppIT : BaseGradleIT() {
override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT
companion object {
private const val MODULE_GROUP = "com.example"
}
@DisplayName("KLibs in K1")
@MppGradlePluginTests
open class KlibBasedMppIT : KGPBaseTest() {
@Test
fun testBuildWithProjectDependency() = testBuildWithDependency {
gradleBuildScript().appendText(
"\n" + """
dependencies {
commonMainImplementation(project("$dependencyModuleName"))
}
""".trimIndent()
)
}
override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions.copyEnsuringK1()
@Test
fun testPublishingAndConsumptionWithEmptySourceSet() = testBuildWithDependency {
// KT-36674
projectDir.resolve("$dependencyModuleName/src/windowsMain").run {
assertTrue { isDirectory }
deleteRecursively()
}
publishProjectDepAndAddDependency(validateHostSpecificPublication = false)
}
@Test
fun testCommonSourceSetsInTransitiveDependencies() = with(Project("common-klib-lib-and-app")) {
// On macOS KT-41083 is also validated by publishing a lib with host specific source sets depending on another lib with host-specific source sets
setupWorkingDir()
val projectDepName = "dependency"
val publishedGroup = "published"
val producerProjectName = "producer"
embedProject(this, renameTo = projectDepName)
projectDir.resolve("$projectDepName/src").walkTopDown().filter { it.extension == "kt" }.forEach { ktFile ->
// Avoid FQN duplicates between producer & consumer
ktFile.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.lib") }
}
gradleBuildScript(projectDepName).appendText(
"""
${"\n"}
group = "$publishedGroup"
""".trimIndent()
)
gradleBuildScript().modify {
transformBuildScriptWithPluginsDsl(it) +
"""
${"\n"}
dependencies { "commonMainImplementation"(project(":$projectDepName")) }
group = "$publishedGroup"
""".trimIndent()
}
gradleSettingsScript().appendText("\nrootProject.name = \"$producerProjectName\"")
build("publish") {
assertSuccessful()
}
// Then consume the published project. To do that, rename the modules so that Gradle chooses the published ones given the original
// Maven coordinates and doesn't resolve them as project dependencies.
val localGroup = "local"
gradleBuildScript(projectDepName).appendText("""${"\n"}group = "$localGroup"""")
gradleBuildScript().appendText(
"""
${"\n"}
repositories { maven("${'$'}rootDir/repo") }
dependencies { "commonMainImplementation"("$publishedGroup:$producerProjectName:1.0") }
group = "$localGroup"
""".trimIndent()
)
val commonModules = listOf(
"published-producer-1.0-commonMain-[\\w-]+.klib",
"published-dependency-1.0-commonMain-[\\w-]+.klib",
).map(::Regex)
val hostSpecificModules = listOf(
"published-producer-1.0-iosMain-[\\w-]+.klib",
"published-dependency-1.0-iosMain-[\\w-]+.klib",
).map(::Regex)
val windowsAndLinuxModules = listOf(
"published-producer-1.0-windowsAndLinuxMain-[\\w-]+.klib",
"published-dependency-1.0-windowsAndLinuxMain-[\\w-]+.klib",
).map(::Regex)
checkTaskCompileClasspath(
"compileWindowsAndLinuxMainKotlinMetadata",
checkModulesInClasspath = commonModules + windowsAndLinuxModules,
checkModulesNotInClasspath = hostSpecificModules
)
// The consumer should correctly receive the klibs of the host-specific source sets
if (HostManager.hostIsMac) {
checkTaskCompileClasspath(
"compileIosMainKotlinMetadata",
checkModulesInClasspath = commonModules + hostSpecificModules,
checkModulesNotInClasspath = windowsAndLinuxModules
@DisplayName("Could be compiled with project dependency")
@GradleTest
fun testBuildWithProjectDependency(
gradleVersion: GradleVersion,
@TempDir localRepo: Path,
) {
testBuildWithDependency(gradleVersion, localRepo) {
buildGradleKts.appendText(
"""
|
|dependencies {
| commonMainImplementation(project("$dependencyModuleName"))
|}
""".trimMargin()
)
}
}
@Test
fun testHostSpecificBuildWithPublishedDependency() = testBuildWithDependency {
// Host-specific dependencies are only possible on macOS
Assume.assumeTrue(HostManager.hostIsMac)
publishProjectDepAndAddDependency(validateHostSpecificPublication = true)
@DisplayName("KT-36674: Could be compiled with empty source set")
@GradleTest
fun testPublishingAndConsumptionWithEmptySourceSet(
gradleVersion: GradleVersion,
@TempDir localRepo: Path,
) {
testBuildWithDependency(gradleVersion, localRepo) {
subProject(dependencyModuleName)
.kotlinSourcesDir("windowsMain")
.parent
.run {
assertTrue(isDirectory())
deleteRecursively()
}
publishProjectDepAndAddDependency(validateHostSpecificPublication = false)
}
}
private fun Project.publishProjectDepAndAddDependency(validateHostSpecificPublication: Boolean) {
@DisplayName("Compiles with common sources in transitive dependencies")
@GradleTest
fun testCommonSourceSetsInTransitiveDependencies(
gradleVersion: GradleVersion,
@TempDir localRepo: Path,
) {
project(
"common-klib-lib-and-app",
gradleVersion,
localRepoDir = localRepo
) {
// On macOS KT-41083 is also validated by publishing a lib with host specific source sets depending on another lib with host-specific source sets
val projectDepName = "dependency"
val publishedGroup = "published"
val producerProjectName = "producer"
includeOtherProjectAsSubmodule(
otherProjectName = "common-klib-lib-and-app",
newSubmoduleName = projectDepName,
isKts = true,
localRepoDir = localRepo
)
subProject(projectDepName)
.projectPath
.allKotlinFiles
.forEach { ktFile ->
// Avoid FQN duplicates between producer & consumer
ktFile.replaceText("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.lib")
}
subProject(projectDepName).buildGradleKts.appendText(
"""
|
|group = "$publishedGroup"
""".trimMargin()
)
buildGradleKts.appendText(
"""
|
|dependencies { "commonMainImplementation"(project(":$projectDepName")) }
|group = "$publishedGroup"
""".trimMargin()
)
settingsGradleKts.appendText(
"""
|
|rootProject.name = "$producerProjectName"
|
""".trimMargin()
)
build("publish")
// Then consume the published project. To do that, rename the modules so that Gradle chooses the published ones given the original
// Maven coordinates and doesn't resolve them as project dependencies.
val localGroup = "local"
subProject(projectDepName).buildGradleKts.appendText(
"""
|
|group = "$localGroup"
""".trimMargin()
)
buildGradleKts.appendText(
"""
|
|repositories { maven("${'$'}rootDir/repo") }
|dependencies { "commonMainImplementation"("$publishedGroup:$producerProjectName:1.0") }
|group = "$localGroup"
""".trimMargin()
)
val commonModules = listOf(
"published-producer-1.0-commonMain-[\\w-]+.klib",
"published-dependency-1.0-commonMain-[\\w-]+.klib",
).map(::Regex)
val hostSpecificModules = listOf(
"published-producer-1.0-iosMain-[\\w-]+.klib",
"published-dependency-1.0-iosMain-[\\w-]+.klib",
).map(::Regex)
val windowsAndLinuxModules = listOf(
"published-producer-1.0-windowsAndLinuxMain-[\\w-]+.klib",
"published-dependency-1.0-windowsAndLinuxMain-[\\w-]+.klib",
).map(::Regex)
checkTaskCompileClasspath(
"compileWindowsAndLinuxMainKotlinMetadata",
checkModulesInClasspath = commonModules + windowsAndLinuxModules,
checkModulesNotInClasspath = hostSpecificModules
)
// The consumer should correctly receive the klibs of the host-specific source sets
if (HostManager.hostIsMac) {
checkTaskCompileClasspath(
"compileIosMainKotlinMetadata",
checkModulesInClasspath = commonModules + hostSpecificModules,
checkModulesNotInClasspath = windowsAndLinuxModules
)
}
}
}
// Host-specific dependencies are only possible on macOS
@OsCondition(
supportedOn = [OS.MAC],
enabledOnCI = [OS.MAC],
)
@DisplayName("Works with host specific dependencies")
@GradleTest
fun testHostSpecificBuildWithPublishedDependency(
gradleVersion: GradleVersion,
@TempDir localRepo: Path
) {
testBuildWithDependency(gradleVersion, localRepo) {
publishProjectDepAndAddDependency(validateHostSpecificPublication = true)
}
}
@DisplayName("Works with Kotlin native transitive dependencies")
@GradleTest
fun testKotlinNativeImplPublishedDeps(
gradleVersion: GradleVersion,
@TempDir localRepo: Path
) {
testKotlinNativeImplementationDependencies(gradleVersion, localRepo) {
build(":$transitiveDepModuleName:publish", ":$dependencyModuleName:publish")
buildGradleKts.appendText(
"""
|
|repositories {
| maven("${'$'}rootDir/repo")
|}
|
|dependencies {
| commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0")
|}
""".trimMargin()
)
listOf(transitiveDepModuleName, dependencyModuleName).forEach {
// prevent Gradle from linking the above dependency to the project:
subProject(it).buildGradleKts.appendText(
"""
|
|group = "com.some.other.group"
""".trimMargin()
)
}
}
}
@DisplayName("Works with project dependencies containing Kotlin Native target")
@GradleTest
fun testKotlinNativeImplProjectDeps(
gradleVersion: GradleVersion,
@TempDir localRepo: Path,
) {
testKotlinNativeImplementationDependencies(gradleVersion, localRepo) {
buildGradleKts.appendText(
"""
|
|dependencies {
| "commonMainImplementation"(project(":$dependencyModuleName"))
|}
""".trimMargin()
)
}
}
@DisplayName("KT-38746: Should not disable compilation of shared source set")
@GradleTest
fun testAvoidSkippingSharedNativeSourceSetKt38746(gradleVersion: GradleVersion) {
project("hierarchical-all-native", gradleVersion) {
val targetNames = listOf(
// Try different alphabetical ordering of the targets to ensure that the behavior doesn't depend on it,
// as with 'first target'
listOf("a1", "a2", "a3"),
listOf("a3", "a1", "a2"),
listOf("a2", "a3", "a1"),
)
val targetParamNames = listOf("mingwTargetName", "linuxTargetName", "macosTargetName", "currentHostTargetName")
for (names in targetNames) {
val currentHostTargetName = when {
HostManager.hostIsMingw -> names[0]
HostManager.hostIsLinux -> names[1]
HostManager.hostIsMac -> names[2]
else -> error("unexpected host")
}
val params = targetParamNames.zip(names + currentHostTargetName) { k, v -> "-P$k=$v" }
build(":clean", ":compileCurrentHostAndLinuxKotlinMetadata", *params.toTypedArray()) {
assertTasksExecuted(":compileCurrentHostAndLinuxKotlinMetadata", ":compileAllNativeKotlinMetadata")
}
}
}
}
private fun TestProject.publishProjectDepAndAddDependency(validateHostSpecificPublication: Boolean) {
build(":$dependencyModuleName:publish") {
assertSuccessful()
if (validateHostSpecificPublication)
checkPublishedHostSpecificMetadata(this@build)
if (validateHostSpecificPublication) checkPublishedHostSpecificMetadata(this@publishProjectDepAndAddDependency)
}
gradleBuildScript().appendText(
"\n" + """
repositories {
maven("${'$'}rootDir/repo")
}
dependencies {
commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0")
}
""".trimIndent()
buildGradleKts.appendText(
"""
|
|repositories {
| maven("${'$'}rootDir/repo")
|}
|
|dependencies {
| commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0")
|}
""".trimMargin()
)
// prevent Gradle from linking the above dependency to the project:
gradleBuildScript(dependencyModuleName).appendText("\ngroup = \"some.other.group\"")
subProject(dependencyModuleName)
.buildGradleKts
.appendText(
"""
|
|group = "some.other.group"
""".trimMargin()
)
}
private val dependencyModuleName = "project-dep"
private val transitiveDepModuleName = "transitive-dep"
private fun testBuildWithDependency(configureDependency: Project.() -> Unit) = with(Project("common-klib-lib-and-app")) {
embedProject(Project("common-klib-lib-and-app"), renameTo = dependencyModuleName)
private fun testBuildWithDependency(
gradleVersion: GradleVersion,
localRepo: Path,
configureDependency: TestProject.() -> Unit
) {
project("common-klib-lib-and-app", gradleVersion, localRepoDir = localRepo) {
includeOtherProjectAsSubmodule(
otherProjectName = "common-klib-lib-and-app",
newSubmoduleName = dependencyModuleName,
isKts = true,
localRepoDir = localRepo,
)
projectDir.resolve("$dependencyModuleName/src/commonMain/kotlin/TestKt37832.kt").writeText(
"package com.example.test.kt37832" + "\n" + "class MyException : RuntimeException()"
)
subProject(dependencyModuleName)
.kotlinSourcesDir("commonMain")
.resolve("TestKt37832.kt")
.writeText(
"""
|package com.example.test.kt37832
|
|class MyException : RuntimeException()
""".trimMargin()
)
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
projectDir.resolve(dependencyModuleName + "/src").walkTopDown().filter { it.extension == "kt" }.forEach { file ->
file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.projectdep") }
}
configureDependency()
projectDir.resolve("src/commonMain/kotlin/LibUsage.kt").appendText(
"\n" + """
package com.h0tk3y.hmpp.klib.demo.test
import com.projectdep.LibCommonMainExpect as ProjectDepExpect
private fun useProjectDep() {
ProjectDepExpect()
}
""".trimIndent()
)
projectDir.resolve("src/linuxMain/kotlin/LibLinuxMainUsage.kt").appendText(
"\n" + """
package com.h0tk3y.hmpp.klib.demo.test
import com.projectdep.libLinuxMainFun as libFun
private fun useProjectDep() {
libFun()
}
""".trimIndent()
)
val tasksToExecute = listOf(
":compileJvmAndJsMainKotlinMetadata",
":compileLinuxMainKotlinMetadata",
) + (if (HostManager.hostIsMac) listOf(":compileIosMainKotlinMetadata") else emptyList())
build("assemble") {
assertSuccessful()
assertTasksExecuted(*tasksToExecute.toTypedArray())
assertFileExists("build/classes/kotlin/metadata/commonMain/default/manifest")
assertFileExists("build/classes/kotlin/metadata/jvmAndJsMain/default/manifest")
assertFileExists("build/classes/kotlin/metadata/linuxMain/klib/${projectName}_linuxMain")
// Check that the common and JVM+JS source sets don't receive the Kotlin/Native stdlib in the classpath:
run {
fun getClasspath(taskPath: String): Iterable<String> {
val argsPrefix = " $taskPath Kotlin compiler args:"
return output.lines().single { argsPrefix in it }
.substringAfter("-classpath ").substringBefore(" -").split(File.pathSeparator)
subProject(dependencyModuleName)
.projectPath
.allKotlinFiles
.forEach { file ->
file.replaceText("package com.h0tk3y.hmpp.klib.demo", "package com.projectdep")
}
fun classpathHasKNStdlib(classpath: Iterable<String>) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") }
configureDependency()
kotlinSourcesDir("commonMain").resolve("LibUsage.kt").writeText(
"""
|
|package com.h0tk3y.hmpp.klib.demo.test
|
|import com.projectdep.LibCommonMainExpect as ProjectDepExpect
|
|private fun useProjectDep() {
| ProjectDepExpect()
|}
""".trimMargin()
)
kotlinSourcesDir("linuxMain").resolve("LibLinuxMainUsage.kt").writeText(
"""
|
|package com.h0tk3y.hmpp.klib.demo.test
|
|import com.projectdep.libLinuxMainFun as libFun
|
|private fun useProjectDep() {
| libFun()
|}
""".trimMargin()
)
val tasksToExecute = listOfNotNull(
":compileJvmAndJsMainKotlinMetadata",
":compileLinuxMainKotlinMetadata",
if (HostManager.hostIsMac) ":compileIosMainKotlinMetadata" else null
)
build("assemble") {
assertTasksExecuted(tasksToExecute)
assertFileInProjectExists("build/classes/kotlin/metadata/commonMain/default/manifest")
assertFileInProjectExists("build/classes/kotlin/metadata/jvmAndJsMain/default/manifest")
assertDirectoryInProjectExists("build/classes/kotlin/metadata/linuxMain/klib/${projectName}_linuxMain")
// Check that the common and JVM+JS source sets don't receive the Kotlin/Native stdlib in the classpath:
assertFalse(classpathHasKNStdlib(getClasspath(":compileCommonMainKotlinMetadata")))
assertFalse(classpathHasKNStdlib(getClasspath(":compileJvmAndJsMainKotlinMetadata")))
}
}
}
private fun checkPublishedHostSpecificMetadata(compiledProject: CompiledProject) = with(compiledProject) {
val groupDir = project.projectDir.resolve("repo/com/example")
private fun classpathHasKNStdlib(classpath: Iterable<String>) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") }
assertTasksExecuted(
":$dependencyModuleName:compileIosMainKotlinMetadata")
private fun BuildResult.getClasspath(taskPath: String): Iterable<String> {
val argsPrefix = " $taskPath Kotlin compiler args:"
return output.lines().single { argsPrefix in it }
.substringAfter("-classpath ").substringBefore(" -").split(File.pathSeparator)
}
private fun BuildResult.checkPublishedHostSpecificMetadata(project: TestProject) {
val groupDir = project.projectPath.resolve("repo/com/example")
assertTasksExecuted(":$dependencyModuleName:compileIosMainKotlinMetadata")
// Check that the metadata JAR doesn't contain the host-specific source set entries, but contains the shared-Native source set
// that can be built on every host:
ZipFile(groupDir.resolve("$dependencyModuleName/1.0/$dependencyModuleName-1.0-all.jar")).use { metadataJar ->
assertTrue { metadataJar.entries().asSequence().none { it.name.startsWith("iosMain") } }
assertTrue { metadataJar.entries().asSequence().any { it.name.startsWith("linuxMain") } }
}
ZipFile(groupDir.resolve("$dependencyModuleName/1.0/$dependencyModuleName-1.0-all.jar").toFile())
.use { metadataJar ->
assertTrue(metadataJar.entries().asSequence().none { it.name.startsWith("iosMain") })
assertTrue(metadataJar.entries().asSequence().any { it.name.startsWith("linuxMain") })
}
// Then check that in the host-specific modules, there's a metadata artifact that contains the host-specific source set but not the
// common source sets:
val hostSpecificTargets = when {
HostManager.hostIsMac -> listOf("iosArm64", "iosX64")
else -> error("Host doesn't support host-specific metadata")
@@ -246,99 +407,64 @@ open class KlibBasedMppIT : BaseGradleIT() {
hostSpecificTargets.forEach { targetName ->
val moduleName = "$dependencyModuleName-${targetName.lowercase(Locale.getDefault())}"
ZipFile(groupDir.resolve("$moduleName/1.0/$moduleName-1.0-metadata.jar")).use { metadataJar ->
assertTrue { metadataJar.entries().asSequence().any { it.name.startsWith("iosMain") } }
assertTrue { metadataJar.entries().asSequence().none { it.name.startsWith("commonMain") } }
}
ZipFile(groupDir.resolve("$moduleName/1.0/$moduleName-1.0-metadata.jar").toFile())
.use { metadataJar ->
assertTrue(metadataJar.entries().asSequence().any { it.name.startsWith("iosMain") })
assertTrue(metadataJar.entries().asSequence().none { it.name.startsWith("commonMain") })
}
}
// Also check that the targets that don't include any host-specific sources don't even have the metadata artifact:
groupDir.resolve("$dependencyModuleName-linuxx64/1.0/$dependencyModuleName-linuxx64-1.0-metadata.jar").let { metadataJar ->
assertTrue { !metadataJar.exists() }
}
assertFileNotExists(
groupDir.resolve("$dependencyModuleName-linuxx64/1.0/$dependencyModuleName-linuxx64-1.0-metadata.jar")
)
}
private val transitiveDepModuleName = "transitive-dep"
@Test
fun testKotlinNativeImplPublishedDeps() =
testKotlinNativeImplementationDependencies {
build(":$transitiveDepModuleName:publish", ":$dependencyModuleName:publish") {
assertSuccessful()
}
gradleBuildScript().appendText(
"\n" + """
repositories {
maven("${'$'}rootDir/repo")
}
dependencies {
commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0")
}
""".trimIndent()
)
listOf(transitiveDepModuleName, dependencyModuleName).forEach {
// prevent Gradle from linking the above dependency to the project:
gradleBuildScript(it).appendText("\ngroup = \"com.some.other.group\"")
}
}
@Test
fun testKotlinNativeImplProjectDeps() =
testKotlinNativeImplementationDependencies {
gradleBuildScript().appendText("\ndependencies { \"commonMainImplementation\"(project(\":$dependencyModuleName\")) }")
}
private fun testKotlinNativeImplementationDependencies(
setupDependencies: Project.() -> Unit,
) = with(Project("common-klib-lib-and-app")) {
embedProject(Project("common-klib-lib-and-app"), renameTo = transitiveDepModuleName)
embedProject(Project("common-klib-lib-and-app"), renameTo = dependencyModuleName).apply {
projectDir.resolve(dependencyModuleName).walkTopDown().filter { it.extension == "kt" }.forEach { file ->
// Avoid duplicate FQNs as in the compatibility mode, the K2Metadata compiler reports duplicate symbols on them:
file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.demo1") }
}
}
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
gradleBuildScript(dependencyModuleName).appendText("\ndependencies { \"commonMainImplementation\"(project(\":$transitiveDepModuleName\")) }")
gradleVersion: GradleVersion,
localRepo: Path,
setupProject: TestProject.() -> Unit,
) {
project("common-klib-lib-and-app", gradleVersion, localRepoDir = localRepo) {
includeOtherProjectAsSubmodule(
otherProjectName = "common-klib-lib-and-app",
newSubmoduleName = transitiveDepModuleName,
isKts = true,
localRepoDir = localRepo,
)
includeOtherProjectAsSubmodule(
otherProjectName = "common-klib-lib-and-app",
newSubmoduleName = dependencyModuleName,
isKts = true,
localRepoDir = localRepo
)
setupDependencies(this@with)
subProject(dependencyModuleName)
.projectPath
.allKotlinFiles
.forEach { file ->
// Avoid duplicate FQNs as in the compatibility mode, the K2Metadata compiler reports duplicate symbols on them:
file.replaceText("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.demo1")
}
val compileNativeMetadataTaskName = "compileLinuxMainKotlinMetadata"
build(":$compileNativeMetadataTaskName") {
assertSuccessful()
subProject(dependencyModuleName)
.buildGradleKts
.appendText(
"""
|
|dependencies {
| "commonMainImplementation"(project(":$transitiveDepModuleName"))
|}
""".trimMargin()
)
setupProject(this)
build(":compileLinuxMainKotlinMetadata")
}
}
@Test
fun testAvoidSkippingSharedNativeSourceSetKt38746() = with(Project("hierarchical-all-native")) {
val targetNames = listOf(
// Try different alphabetical ordering of the targets to ensure that the behavior doesn't depend on it, as with 'first target'
listOf("a1", "a2", "a3"),
listOf("a3", "a1", "a2"),
listOf("a2", "a3", "a1"),
)
val targetParamNames = listOf("mingwTargetName", "linuxTargetName", "macosTargetName", "currentHostTargetName")
for (names in targetNames) {
val currentHostTargetName = when {
HostManager.hostIsMingw -> names[0]
HostManager.hostIsLinux -> names[1]
HostManager.hostIsMac -> names[2]
else -> error("unexpected host")
}
val params = targetParamNames.zip(names + currentHostTargetName) { k, v -> "-P$k=$v" }
build(":clean", ":compileCurrentHostAndLinuxKotlinMetadata", *params.toTypedArray()) {
assertSuccessful()
assertTasksExecuted(":compileCurrentHostAndLinuxKotlinMetadata", ":compileAllNativeKotlinMetadata")
}
}
}
private var testBuildRunId = 0
private fun BaseGradleIT.Project.checkTaskCompileClasspath(
private fun TestProject.checkTaskCompileClasspath(
taskPath: String,
checkModulesInClasspath: List<Regex> = emptyList(),
checkModulesNotInClasspath: List<Regex> = emptyList(),
@@ -350,28 +476,27 @@ open class KlibBasedMppIT : BaseGradleIT() {
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
}
private fun BaseGradleIT.Project.checkPrintedItems(
private fun TestProject.checkPrintedItems(
subproject: String?,
itemsExpression: String,
checkAnyItemsContains: List<Regex>,
checkNoItemContains: List<Regex>,
) = with(testCase) {
setupWorkingDir()
val printingTaskName = "printItems${testBuildRunId++}"
gradleBuildScript(subproject).appendText(
) {
val printingTaskName = "printItems"
val testProject = if (subproject != null) subProject(subproject) else this
testProject.buildGradleKts.appendText(
"""
${'\n'}
tasks.register("$printingTaskName") {
dependsOn("transformDependenciesMetadata")
doLast {
println("###$printingTaskName" + $itemsExpression)
}
}
""".trimIndent()
|tasks.register("$printingTaskName") {
| dependsOn("transformDependenciesMetadata")
| doLast {
| println("###$printingTaskName" + $itemsExpression)
| }
|}
""".trimMargin()
)
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
assertSuccessful()
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
// NOTE: This does not work for commonized libraries, they may contain the ',' naturally
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
@@ -383,4 +508,8 @@ open class KlibBasedMppIT : BaseGradleIT() {
}
}
}
companion object {
private const val MODULE_GROUP = "com.example"
}
}
@@ -397,11 +397,16 @@ class TestProject(
*/
fun includeOtherProjectAsSubmodule(
otherProjectName: String,
pathPrefix: String,
pathPrefix: String = "",
newSubmoduleName: String = otherProjectName,
isKts: Boolean = false,
localRepoDir: Path? = null,
) {
val otherProjectPath = "$pathPrefix/$otherProjectName".testProjectPath
val otherProjectPath = if (pathPrefix.isEmpty()) {
otherProjectName.testProjectPath
} else {
"$pathPrefix/$otherProjectName".testProjectPath
}
otherProjectPath.copyRecursively(projectPath.resolve(newSubmoduleName))
val gradleSettingToUpdate = if (isKts) settingsGradleKts else settingsGradle
@@ -412,6 +417,8 @@ class TestProject(
include(":$newSubmoduleName")
""".trimIndent()
)
localRepoDir?.let { subProject(newSubmoduleName).configureLocalRepository(localRepoDir) }
}
fun includeOtherProjectAsIncludedBuild(
@@ -814,7 +821,7 @@ private fun TestProject.configureSingleNativeTargetInSubFolders(preset: String =
}
}
private fun TestProject.configureLocalRepository(localRepoDir: Path) {
private fun GradleProject.configureLocalRepository(localRepoDir: Path) {
projectPath.toFile().walkTopDown()
.filter { it.isFile && it.name in buildFileNames }
.forEach { file ->
@@ -1,16 +1,11 @@
plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>")
kotlin("multiplatform")
id("maven-publish")
}
group = "com.example"
version = "1.0"
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
jvm()
js()
@@ -65,7 +60,7 @@ kotlin {
publishing {
repositories {
maven("$rootDir/repo")
maven("<localRepo>")
}
}