HMPP resolution fixes

While we already had the associateWith relationship
that established additional visibility between such
source sets as fooMain and fooTest, this
relationship was not used properly when requested
dependencies were built for source set visibility
inference in HMPP. Instead, an old workaround was
used that just added dependencies of commonMain to
commonTest.

Fix this by using the associateWith relationship in
the preparation logic of source sets visibiltiy.

ALso, make the artifact view used in source sets visibility inference
lenient, so that a host-specific module that has not yet been published
won't lead to fail in source sets visibility and will instead be
ingored.
This commit is contained in:
Sergey Igushkin
2020-09-26 21:05:04 +03:00
parent 5963b07987
commit 16d493558e
6 changed files with 63 additions and 22 deletions
@@ -58,9 +58,42 @@ class HierarchicalMppIT : BaseGradleIT() {
} }
@Test @Test
fun testDependenciesInTests() { fun testNoSourceSetsVisibleIfNoVariantMatched() {
publishThirdPartyLib(withGranularMetadata = true) publishThirdPartyLib(withGranularMetadata = true)
Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run {
setupWorkingDir()
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
// --- Move the dependency from jvmAndJsMain to commonMain, where there's a linuxX64 target missing in the lib
gradleBuildScript().modify {
it.checkedReplace("api(\"com.example.thirdparty:third-party-lib:1.0\")", "//") + "\n" + """
dependencies {
"commonMainApi"("com.example.thirdparty:third-party-lib:1.0")
}
""".trimIndent()
}
testDependencyTransformations { reports ->
val thirdPartyLibApiVisibility = reports.filter { report ->
report.groupAndModule.startsWith("com.example.thirdparty:third-party-lib") && report.scope == "api"
}
val jvmJsSourceSets = setOf("jvmMain", "jsMain", "jvmTest", "jsTest", "jvmAndJsMain", "jvmAndJsTest")
thirdPartyLibApiVisibility.forEach {
if (it.sourceSetName in jvmJsSourceSets)
assertTrue("$it") { it.allVisibleSourceSets == setOf("commonMain") }
}
}
}
}
@Test
fun testDependenciesInTests() {
publishThirdPartyLib(withGranularMetadata = true) {
projectDir.resolve("src/jvmMain").copyRecursively(projectDir.resolve("src/linuxX64Main"))
gradleBuildScript().appendText("\nkotlin.linuxX64()")
}
Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run { Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run {
setupWorkingDir() setupWorkingDir()
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
@@ -68,12 +101,18 @@ class HierarchicalMppIT : BaseGradleIT() {
testDependencyTransformations { reports -> testDependencyTransformations { reports ->
val testApiTransformationReports = val testApiTransformationReports =
reports.filter { report -> reports.filter { report ->
report.groupAndModule.startsWith("com.example.thirdparty") && report.groupAndModule.startsWith("com.example.thirdparty:third-party-lib") &&
report.sourceSetName.let { it == "commonTest" || it == "jvmAndJsTest" } report.sourceSetName.let { it == "commonTest" || it == "jvmAndJsTest" } &&
report.scope == "api"
} }
testApiTransformationReports.forEach { testApiTransformationReports.forEach {
assertTrue("$it") { it.isExcluded } // should not be visible in test source sets if (it.sourceSetName == "commonTest")
assertTrue("$it") { it.isExcluded } // should not be visible in commonTest
else {
assertTrue("$it") { it.allVisibleSourceSets == setOf("commonMain") }
assertTrue("$it") { it.newVisibleSourceSets == emptySet<String>() }
}
} }
} }
@@ -187,12 +226,15 @@ class HierarchicalMppIT : BaseGradleIT() {
projectName: String = "third-party-lib", projectName: String = "third-party-lib",
directoryPrefix: String = "hierarchical-mpp-published-modules", directoryPrefix: String = "hierarchical-mpp-published-modules",
withGranularMetadata: Boolean, withGranularMetadata: Boolean,
jsCompilerType: KotlinJsCompilerType = KotlinJsCompilerType.LEGACY jsCompilerType: KotlinJsCompilerType = KotlinJsCompilerType.LEGACY,
beforePublishing: Project.() -> Unit = { }
): Project = ): Project =
Project(projectName, gradleVersion, directoryPrefix).apply { Project(projectName, gradleVersion, directoryPrefix).apply {
setupWorkingDir() setupWorkingDir()
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
beforePublishing()
if (withGranularMetadata) { if (withGranularMetadata) {
projectDir.resolve("gradle.properties").appendText("kotlin.mpp.enableGranularSourceSetsMetadata=true") projectDir.resolve("gradle.properties").appendText("kotlin.mpp.enableGranularSourceSetsMetadata=true")
} }
@@ -62,7 +62,7 @@ val KOTLIN_JS_DSL_NAME = "kotlin2js"
val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions" val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions"
abstract class KotlinCompilationProcessor<out T : AbstractCompile>( abstract class KotlinCompilationProcessor<out T : AbstractCompile>(
open val kotlinCompilation: AbstractKotlinCompilation<*> open val kotlinCompilation: KotlinCompilation<*>
) { ) {
abstract val kotlinTask: TaskProvider<out T> abstract val kotlinTask: TaskProvider<out T>
abstract fun run() abstract fun run()
@@ -146,6 +146,7 @@ internal class ResolvedMppVariantsProvider private constructor(private val proje
return artifactsConfiguration.incoming.artifactView { view -> return artifactsConfiguration.incoming.artifactView { view ->
view.componentFilter { it in mppComponentIds } view.componentFilter { it in mppComponentIds }
view.attributes { attrs -> attrs.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA)) } view.attributes { attrs -> attrs.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA)) }
view.lenient(true)
}.artifacts.associateBy { it.id.componentIdentifier } }.artifacts.associateBy { it.id.componentIdentifier }
} }
@@ -71,7 +71,7 @@ internal class SourceSetVisibilityProvider(
val firstConfigurationByVariant = mutableMapOf<String, Configuration>() val firstConfigurationByVariant = mutableMapOf<String, Configuration>()
val visiblePlatformVariantNames: Set<String> = val visiblePlatformVariantNames: Set<String?> =
compilations compilations
.filter { it.target.platformType != KotlinPlatformType.common } .filter { it.target.platformType != KotlinPlatformType.common }
.flatMapTo(mutableSetOf()) { compilation -> .flatMapTo(mutableSetOf()) { compilation ->
@@ -79,10 +79,10 @@ internal class SourceSetVisibilityProvider(
// that we have in the compilations: // that we have in the compilations:
dependencyScopes.mapNotNull { scope -> project.resolvableConfigurationFromCompilationByScope(compilation, scope) } dependencyScopes.mapNotNull { scope -> project.resolvableConfigurationFromCompilationByScope(compilation, scope) }
} }
.mapNotNullTo(mutableSetOf()) { configuration -> .mapTo(mutableSetOf()) { configuration ->
val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration) val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration)
?.let { platformVariantName(it) } ?.let { platformVariantName(it) }
?: return@mapNotNullTo null ?: return@mapTo null
firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration) firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration)
resolvedVariant resolvedVariant
@@ -121,11 +121,11 @@ internal class SourceSetVisibilityProvider(
.keys.first() .keys.first()
} }
someVariantByHostSpecificSourceSet.mapValues { (_, variantName) -> someVariantByHostSpecificSourceSet.entries.mapNotNull { (sourceSetName, variantName) ->
val configuration = firstConfigurationByVariant.getValue(variantName) val configuration = firstConfigurationByVariant.getValue(variantName)
resolvedVariantsProvider.getMetadataArtifactByRootModule(mppModuleIdentifier, configuration) resolvedVariantsProvider.getMetadataArtifactByRootModule(mppModuleIdentifier, configuration)
?: error("Couldn't resolve metadata artifact for $mppModuleIdentifier in $configuration") ?.let { sourceSetName to it }
} }.toMap()
} }
return SourceSetVisibilityResult( return SourceSetVisibilityResult(
@@ -51,12 +51,9 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
internal fun transformGranularMetadataTaskName(sourceSetName: String) = internal fun transformGranularMetadataTaskName(sourceSetName: String) =
lowerCamelCaseName("transform", sourceSetName, "DependenciesMetadata") lowerCamelCaseName("transform", sourceSetName, "DependenciesMetadata")
// TODO generalize once a general production-test and other kinds of inter-compilation visibility are supported
// Currently, this is a temporary ad-hoc mechanism for exposing the commonMain dependencies to the test source sets
internal fun dependsOnWithInterCompilationDependencies(project: Project, sourceSet: KotlinSourceSet): Set<KotlinSourceSet> = internal fun dependsOnWithInterCompilationDependencies(project: Project, sourceSet: KotlinSourceSet): Set<KotlinSourceSet> =
sourceSet.dependsOn.toMutableSet().apply { sourceSet.dependsOn.toMutableSet().apply {
if (sourceSet.name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) addAll(getVisibleSourceSetsFromAssociateCompilations(project, sourceSet))
add(project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME))
} }
} }
@@ -21,6 +21,7 @@ import org.gradle.api.Task
import org.gradle.api.UnknownTaskException import org.gradle.api.UnknownTaskException
import org.gradle.api.tasks.TaskCollection import org.gradle.api.tasks.TaskCollection
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mapKotlinTaskProperties import org.jetbrains.kotlin.gradle.plugin.mapKotlinTaskProperties
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
@@ -83,7 +84,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
open fun registerKotlinJVMTask( open fun registerKotlinJVMTask(
project: Project, project: Project,
name: String, name: String,
compilation: AbstractKotlinCompilation<*>, compilation: KotlinCompilation<*>,
configureAction: (KotlinCompile) -> (Unit) configureAction: (KotlinCompile) -> (Unit)
): TaskProvider<out KotlinCompile> { ): TaskProvider<out KotlinCompile> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
@@ -98,7 +99,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
fun registerKotlinJSTask( fun registerKotlinJSTask(
project: Project, project: Project,
name: String, name: String,
compilation: AbstractKotlinCompilation<*>, compilation: KotlinCompilation<*>,
configureAction: (Kotlin2JsCompile) -> Unit configureAction: (Kotlin2JsCompile) -> Unit
): TaskProvider<out Kotlin2JsCompile> { ): TaskProvider<out Kotlin2JsCompile> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
@@ -113,7 +114,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
fun registerKotlinJsIrTask( fun registerKotlinJsIrTask(
project: Project, project: Project,
name: String, name: String,
compilation: AbstractKotlinCompilation<*>, compilation: KotlinCompilation<*>,
configureAction: (KotlinJsIrLink) -> Unit configureAction: (KotlinJsIrLink) -> Unit
): TaskProvider<out KotlinJsIrLink> { ): TaskProvider<out KotlinJsIrLink> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
@@ -128,7 +129,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
fun registerKotlinCommonTask( fun registerKotlinCommonTask(
project: Project, project: Project,
name: String, name: String,
compilation: AbstractKotlinCompilation<*>, compilation: KotlinCompilation<*>,
configureAction: (KotlinCompileCommon) -> (Unit) configureAction: (KotlinCompileCommon) -> (Unit)
): TaskProvider<out KotlinCompileCommon> { ): TaskProvider<out KotlinCompileCommon> {
val properties = PropertiesProvider(project) val properties = PropertiesProvider(project)
@@ -144,7 +145,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
kotlinTaskHolder: TaskProvider<out AbstractKotlinCompile<*>>, kotlinTaskHolder: TaskProvider<out AbstractKotlinCompile<*>>,
project: Project, project: Project,
propertiesProvider: PropertiesProvider, propertiesProvider: PropertiesProvider,
compilation: AbstractKotlinCompilation<*> compilation: KotlinCompilation<*>
) { ) {
project.runOnceAfterEvaluated("apply properties and language settings to ${kotlinTaskHolder.name}", kotlinTaskHolder) { project.runOnceAfterEvaluated("apply properties and language settings to ${kotlinTaskHolder.name}", kotlinTaskHolder) {
propertiesProvider.mapKotlinTaskProperties(kotlinTaskHolder.get()) propertiesProvider.mapKotlinTaskProperties(kotlinTaskHolder.get())
@@ -165,7 +166,7 @@ internal class AndroidTasksProvider(targetName: String) : KotlinTasksProvider(ta
kotlinTaskHolder: TaskProvider<out AbstractKotlinCompile<*>>, kotlinTaskHolder: TaskProvider<out AbstractKotlinCompile<*>>,
project: Project, project: Project,
propertiesProvider: PropertiesProvider, propertiesProvider: PropertiesProvider,
compilation: AbstractKotlinCompilation<*> compilation: KotlinCompilation<*>
) { ) {
super.configure(kotlinTaskHolder, project, propertiesProvider, compilation) super.configure(kotlinTaskHolder, project, propertiesProvider, compilation)
kotlinTaskHolder.configure { kotlinTaskHolder.configure {