[Gradle] Migrate KlibBasedMppIT to new test DSL
^KT-65528 In Progress
This commit is contained in:
committed by
Space Team
parent
74c02a77fe
commit
55bbaec3f9
+3
-2
@@ -19,9 +19,10 @@ class K2HierarchicalMppIT : HierarchicalMppIT() {
|
|||||||
override val defaultBuildOptions: BuildOptions get() = super.defaultBuildOptions.copy(languageVersion = "2.0")
|
override val defaultBuildOptions: BuildOptions get() = super.defaultBuildOptions.copy(languageVersion = "2.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
@MppGradlePluginTests
|
||||||
|
@DisplayName("KLibs in K2")
|
||||||
class K2KlibBasedMppIT : KlibBasedMppIT() {
|
class K2KlibBasedMppIT : KlibBasedMppIT() {
|
||||||
override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(languageVersion = "2.0")
|
override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions.copyEnsuringK2()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
|
|||||||
+416
-287
@@ -5,240 +5,401 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle
|
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.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.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipFile
|
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.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
open class KlibBasedMppIT : BaseGradleIT() {
|
|
||||||
override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT
|
|
||||||
|
|
||||||
companion object {
|
@DisplayName("KLibs in K1")
|
||||||
private const val MODULE_GROUP = "com.example"
|
@MppGradlePluginTests
|
||||||
}
|
open class KlibBasedMppIT : KGPBaseTest() {
|
||||||
|
|
||||||
@Test
|
override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions.copyEnsuringK1()
|
||||||
fun testBuildWithProjectDependency() = testBuildWithDependency {
|
|
||||||
gradleBuildScript().appendText(
|
|
||||||
"\n" + """
|
|
||||||
dependencies {
|
|
||||||
commonMainImplementation(project("$dependencyModuleName"))
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@DisplayName("Could be compiled with project dependency")
|
||||||
fun testPublishingAndConsumptionWithEmptySourceSet() = testBuildWithDependency {
|
@GradleTest
|
||||||
// KT-36674
|
fun testBuildWithProjectDependency(
|
||||||
projectDir.resolve("$dependencyModuleName/src/windowsMain").run {
|
gradleVersion: GradleVersion,
|
||||||
assertTrue { isDirectory }
|
@TempDir localRepo: Path,
|
||||||
deleteRecursively()
|
) {
|
||||||
}
|
testBuildWithDependency(gradleVersion, localRepo) {
|
||||||
publishProjectDepAndAddDependency(validateHostSpecificPublication = false)
|
buildGradleKts.appendText(
|
||||||
}
|
"""
|
||||||
|
|
|
||||||
@Test
|
|dependencies {
|
||||||
fun testCommonSourceSetsInTransitiveDependencies() = with(Project("common-klib-lib-and-app")) {
|
| commonMainImplementation(project("$dependencyModuleName"))
|
||||||
// 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()
|
""".trimMargin()
|
||||||
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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@DisplayName("KT-36674: Could be compiled with empty source set")
|
||||||
fun testHostSpecificBuildWithPublishedDependency() = testBuildWithDependency {
|
@GradleTest
|
||||||
// Host-specific dependencies are only possible on macOS
|
fun testPublishingAndConsumptionWithEmptySourceSet(
|
||||||
Assume.assumeTrue(HostManager.hostIsMac)
|
gradleVersion: GradleVersion,
|
||||||
publishProjectDepAndAddDependency(validateHostSpecificPublication = true)
|
@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") {
|
build(":$dependencyModuleName:publish") {
|
||||||
assertSuccessful()
|
if (validateHostSpecificPublication) checkPublishedHostSpecificMetadata(this@publishProjectDepAndAddDependency)
|
||||||
if (validateHostSpecificPublication)
|
|
||||||
checkPublishedHostSpecificMetadata(this@build)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleBuildScript().appendText(
|
buildGradleKts.appendText(
|
||||||
"\n" + """
|
"""
|
||||||
repositories {
|
|
|
||||||
maven("${'$'}rootDir/repo")
|
|repositories {
|
||||||
}
|
| maven("${'$'}rootDir/repo")
|
||||||
dependencies {
|
|}
|
||||||
commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0")
|
|
|
||||||
}
|
|dependencies {
|
||||||
""".trimIndent()
|
| commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0")
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
)
|
)
|
||||||
|
|
||||||
// prevent Gradle from linking the above dependency to the project:
|
// 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 dependencyModuleName = "project-dep"
|
||||||
|
private val transitiveDepModuleName = "transitive-dep"
|
||||||
|
|
||||||
private fun testBuildWithDependency(configureDependency: Project.() -> Unit) = with(Project("common-klib-lib-and-app")) {
|
private fun testBuildWithDependency(
|
||||||
embedProject(Project("common-klib-lib-and-app"), renameTo = dependencyModuleName)
|
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(
|
subProject(dependencyModuleName)
|
||||||
"package com.example.test.kt37832" + "\n" + "class MyException : RuntimeException()"
|
.kotlinSourcesDir("commonMain")
|
||||||
)
|
.resolve("TestKt37832.kt")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
|package com.example.test.kt37832
|
||||||
|
|
|
||||||
|
|class MyException : RuntimeException()
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
subProject(dependencyModuleName)
|
||||||
|
.projectPath
|
||||||
projectDir.resolve(dependencyModuleName + "/src").walkTopDown().filter { it.extension == "kt" }.forEach { file ->
|
.allKotlinFiles
|
||||||
file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.projectdep") }
|
.forEach { file ->
|
||||||
}
|
file.replaceText("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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(":compileCommonMainKotlinMetadata")))
|
||||||
assertFalse(classpathHasKNStdlib(getClasspath(":compileJvmAndJsMainKotlinMetadata")))
|
assertFalse(classpathHasKNStdlib(getClasspath(":compileJvmAndJsMainKotlinMetadata")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPublishedHostSpecificMetadata(compiledProject: CompiledProject) = with(compiledProject) {
|
private fun classpathHasKNStdlib(classpath: Iterable<String>) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") }
|
||||||
val groupDir = project.projectDir.resolve("repo/com/example")
|
|
||||||
|
|
||||||
assertTasksExecuted(
|
private fun BuildResult.getClasspath(taskPath: String): Iterable<String> {
|
||||||
":$dependencyModuleName:compileIosMainKotlinMetadata")
|
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
|
// 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:
|
// that can be built on every host:
|
||||||
|
ZipFile(groupDir.resolve("$dependencyModuleName/1.0/$dependencyModuleName-1.0-all.jar").toFile())
|
||||||
ZipFile(groupDir.resolve("$dependencyModuleName/1.0/$dependencyModuleName-1.0-all.jar")).use { metadataJar ->
|
.use { metadataJar ->
|
||||||
assertTrue { metadataJar.entries().asSequence().none { it.name.startsWith("iosMain") } }
|
assertTrue(metadataJar.entries().asSequence().none { it.name.startsWith("iosMain") })
|
||||||
assertTrue { metadataJar.entries().asSequence().any { it.name.startsWith("linuxMain") } }
|
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
|
// 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:
|
// common source sets:
|
||||||
|
|
||||||
val hostSpecificTargets = when {
|
val hostSpecificTargets = when {
|
||||||
HostManager.hostIsMac -> listOf("iosArm64", "iosX64")
|
HostManager.hostIsMac -> listOf("iosArm64", "iosX64")
|
||||||
else -> error("Host doesn't support host-specific metadata")
|
else -> error("Host doesn't support host-specific metadata")
|
||||||
@@ -246,99 +407,64 @@ open class KlibBasedMppIT : BaseGradleIT() {
|
|||||||
|
|
||||||
hostSpecificTargets.forEach { targetName ->
|
hostSpecificTargets.forEach { targetName ->
|
||||||
val moduleName = "$dependencyModuleName-${targetName.lowercase(Locale.getDefault())}"
|
val moduleName = "$dependencyModuleName-${targetName.lowercase(Locale.getDefault())}"
|
||||||
ZipFile(groupDir.resolve("$moduleName/1.0/$moduleName-1.0-metadata.jar")).use { metadataJar ->
|
ZipFile(groupDir.resolve("$moduleName/1.0/$moduleName-1.0-metadata.jar").toFile())
|
||||||
assertTrue { metadataJar.entries().asSequence().any { it.name.startsWith("iosMain") } }
|
.use { metadataJar ->
|
||||||
assertTrue { metadataJar.entries().asSequence().none { it.name.startsWith("commonMain") } }
|
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:
|
// Also check that the targets that don't include any host-specific sources don't even have the metadata artifact:
|
||||||
|
assertFileNotExists(
|
||||||
groupDir.resolve("$dependencyModuleName-linuxx64/1.0/$dependencyModuleName-linuxx64-1.0-metadata.jar").let { metadataJar ->
|
groupDir.resolve("$dependencyModuleName-linuxx64/1.0/$dependencyModuleName-linuxx64-1.0-metadata.jar")
|
||||||
assertTrue { !metadataJar.exists() }
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
private fun testKotlinNativeImplementationDependencies(
|
||||||
setupDependencies: Project.() -> Unit,
|
gradleVersion: GradleVersion,
|
||||||
) = with(Project("common-klib-lib-and-app")) {
|
localRepo: Path,
|
||||||
embedProject(Project("common-klib-lib-and-app"), renameTo = transitiveDepModuleName)
|
setupProject: TestProject.() -> Unit,
|
||||||
embedProject(Project("common-klib-lib-and-app"), renameTo = dependencyModuleName).apply {
|
) {
|
||||||
projectDir.resolve(dependencyModuleName).walkTopDown().filter { it.extension == "kt" }.forEach { file ->
|
project("common-klib-lib-and-app", gradleVersion, localRepoDir = localRepo) {
|
||||||
// Avoid duplicate FQNs as in the compatibility mode, the K2Metadata compiler reports duplicate symbols on them:
|
includeOtherProjectAsSubmodule(
|
||||||
file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.demo1") }
|
otherProjectName = "common-klib-lib-and-app",
|
||||||
}
|
newSubmoduleName = transitiveDepModuleName,
|
||||||
}
|
isKts = true,
|
||||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
localRepoDir = localRepo,
|
||||||
gradleBuildScript(dependencyModuleName).appendText("\ndependencies { \"commonMainImplementation\"(project(\":$transitiveDepModuleName\")) }")
|
)
|
||||||
|
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"
|
subProject(dependencyModuleName)
|
||||||
build(":$compileNativeMetadataTaskName") {
|
.buildGradleKts
|
||||||
assertSuccessful()
|
.appendText(
|
||||||
|
"""
|
||||||
|
|
|
||||||
|
|dependencies {
|
||||||
|
| "commonMainImplementation"(project(":$transitiveDepModuleName"))
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
setupProject(this)
|
||||||
|
|
||||||
|
build(":compileLinuxMainKotlinMetadata")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private fun TestProject.checkTaskCompileClasspath(
|
||||||
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(
|
|
||||||
taskPath: String,
|
taskPath: String,
|
||||||
checkModulesInClasspath: List<Regex> = emptyList(),
|
checkModulesInClasspath: List<Regex> = emptyList(),
|
||||||
checkModulesNotInClasspath: List<Regex> = emptyList(),
|
checkModulesNotInClasspath: List<Regex> = emptyList(),
|
||||||
@@ -350,28 +476,27 @@ open class KlibBasedMppIT : BaseGradleIT() {
|
|||||||
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
|
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun BaseGradleIT.Project.checkPrintedItems(
|
private fun TestProject.checkPrintedItems(
|
||||||
subproject: String?,
|
subproject: String?,
|
||||||
itemsExpression: String,
|
itemsExpression: String,
|
||||||
checkAnyItemsContains: List<Regex>,
|
checkAnyItemsContains: List<Regex>,
|
||||||
checkNoItemContains: List<Regex>,
|
checkNoItemContains: List<Regex>,
|
||||||
) = with(testCase) {
|
) {
|
||||||
setupWorkingDir()
|
val printingTaskName = "printItems"
|
||||||
|
val testProject = if (subproject != null) subProject(subproject) else this
|
||||||
val printingTaskName = "printItems${testBuildRunId++}"
|
testProject.buildGradleKts.appendText(
|
||||||
gradleBuildScript(subproject).appendText(
|
|
||||||
"""
|
"""
|
||||||
${'\n'}
|
|
||||||
tasks.register("$printingTaskName") {
|
|tasks.register("$printingTaskName") {
|
||||||
dependsOn("transformDependenciesMetadata")
|
| dependsOn("transformDependenciesMetadata")
|
||||||
doLast {
|
| doLast {
|
||||||
println("###$printingTaskName" + $itemsExpression)
|
| println("###$printingTaskName" + $itemsExpression)
|
||||||
}
|
| }
|
||||||
}
|
|}
|
||||||
""".trimIndent()
|
""".trimMargin()
|
||||||
)
|
)
|
||||||
|
|
||||||
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
|
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
|
||||||
assertSuccessful()
|
|
||||||
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
|
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
|
||||||
// NOTE: This does not work for commonized libraries, they may contain the ',' naturally
|
// NOTE: This does not work for commonized libraries, they may contain the ',' naturally
|
||||||
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
|
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
|
||||||
@@ -383,4 +508,8 @@ open class KlibBasedMppIT : BaseGradleIT() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MODULE_GROUP = "com.example"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
-3
@@ -397,11 +397,16 @@ class TestProject(
|
|||||||
*/
|
*/
|
||||||
fun includeOtherProjectAsSubmodule(
|
fun includeOtherProjectAsSubmodule(
|
||||||
otherProjectName: String,
|
otherProjectName: String,
|
||||||
pathPrefix: String,
|
pathPrefix: String = "",
|
||||||
newSubmoduleName: String = otherProjectName,
|
newSubmoduleName: String = otherProjectName,
|
||||||
isKts: Boolean = false,
|
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))
|
otherProjectPath.copyRecursively(projectPath.resolve(newSubmoduleName))
|
||||||
|
|
||||||
val gradleSettingToUpdate = if (isKts) settingsGradleKts else settingsGradle
|
val gradleSettingToUpdate = if (isKts) settingsGradleKts else settingsGradle
|
||||||
@@ -412,6 +417,8 @@ class TestProject(
|
|||||||
include(":$newSubmoduleName")
|
include(":$newSubmoduleName")
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
localRepoDir?.let { subProject(newSubmoduleName).configureLocalRepository(localRepoDir) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun includeOtherProjectAsIncludedBuild(
|
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()
|
projectPath.toFile().walkTopDown()
|
||||||
.filter { it.isFile && it.name in buildFileNames }
|
.filter { it.isFile && it.name in buildFileNames }
|
||||||
.forEach { file ->
|
.forEach { file ->
|
||||||
|
|||||||
+2
-7
@@ -1,16 +1,11 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform").version("<pluginMarkerVersion>")
|
kotlin("multiplatform")
|
||||||
id("maven-publish")
|
id("maven-publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.example"
|
group = "com.example"
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm()
|
||||||
js()
|
js()
|
||||||
@@ -65,7 +60,7 @@ kotlin {
|
|||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven("$rootDir/repo")
|
maven("<localRepo>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user