Fix publishing dependencies of Android libraries (KT-29476)

* Add the extendsFrom relation between the Kotlin MPP compilation's
  configurations (`*Api`, `*Implementation`, `*RuntimeOnly`) and
  the Android variant's `*Elements` configurations, as those
  dependencies currently are not added to the Android source sets and
  are thus missing from the published dependencies.

* Fix a `this is KotlinCompilationToRunnableFiles` check that
  mistakenly referenced the Project receiver of `whenEvaluated { ... }`

Issue #KT-29476 Fixed
This commit is contained in:
Sergey Igushkin
2019-01-29 18:32:40 +03:00
parent 64b2527e31
commit 55cc5e84fc
3 changed files with 66 additions and 28 deletions
@@ -140,6 +140,8 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio
// Convert the 'app' project to a library, publish the two without metadata,
// check that the dependencies in the POMs are correctly rewritten:
val appGroupDir = "app/build/repo/com/example/"
gradleSettingsScript().modify { it.replace("enableFeaturePreview", "//") }
gradleBuildScript("app").modify {
it.replace("com.android.application", "com.android.library")
@@ -152,19 +154,47 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio
}
build("publish") {
assertSuccessful()
val appGroupDir = "app/build/repo/com/example/"
listOf("foobar", "foobaz").forEach { flavor ->
listOf("-debug", "").forEach { buildType ->
assertFileExists(appGroupDir + "app-androidapp-$flavor$buildType/1.0/app-androidapp-$flavor$buildType-1.0.aar")
assertFileExists(appGroupDir + "app-androidapp-$flavor$buildType/1.0/app-androidapp-$flavor$buildType-1.0-sources.jar")
val pomText = projectDir.resolve(
appGroupDir + "app-androidapp-$flavor$buildType/1.0/app-androidapp-$flavor$buildType-1.0.pom"
).readText()
assertTrue { "<artifactId>lib-androidlib-$flavor$buildType</artifactId>" in pomText }
).readText().replace("\\s+".toRegex(), "")
assertTrue {
"<artifactId>lib-androidlib-$flavor$buildType</artifactId><version>1.0</version><scope>runtime</scope>" in pomText
}
}
}
projectDir.resolve(groupDir).deleteRecursively()
}
// Also check that api and runtimeOnly MPP dependencies get correctly published with the appropriate scope, KT-29476:
gradleBuildScript("app").modify {
it.replace("implementation project(':lib')", "api project(':lib')") + "\n" + """
kotlin.sourceSets.commonMain.dependencies {
runtimeOnly(kotlin('reflect'))
}
""".trimIndent()
}
build("publish") {
assertSuccessful()
listOf("foobar", "foobaz").forEach { flavor ->
listOf("-debug", "").forEach { buildType ->
val pomText = projectDir.resolve(
appGroupDir + "app-androidapp-$flavor$buildType/1.0/app-androidapp-$flavor$buildType-1.0.pom"
).readText().replace("\\s+".toRegex(), "")
assertTrue {
"<artifactId>lib-androidlib-$flavor$buildType</artifactId><version>1.0</version><scope>compile</scope>" in pomText
}
assertTrue {
val kotlinVersion = defaultBuildOptions().kotlinVersion
"<artifactId>kotlin-reflect</artifactId><version>$kotlinVersion</version><scope>runtime</scope>" in pomText
}
}
}
}
}
@Test
@@ -146,6 +146,16 @@ class Android25ProjectHandler(
// We should request such API in the Android plugin
val apiElementsConfigurationName = "${variant.name}ApiElements"
val runtimeElementsConfigurationName = "${variant.name}RuntimeElements"
// KT-29476, the Android *Elements configurations need Kotlin MPP dependencies:
if (project.configurations.findByName(apiElementsConfigurationName) != null) {
project.addExtendsFromRelation(apiElementsConfigurationName, compilation.apiConfigurationName)
}
if (project.configurations.findByName(runtimeElementsConfigurationName) != null) {
project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.implementationConfigurationName)
project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.runtimeOnlyConfigurationName)
}
listOf(apiElementsConfigurationName, runtimeElementsConfigurationName).forEach { outputConfigurationName ->
project.configurations.findByName(outputConfigurationName)?.usesPlatformOf(compilation.target)
}
@@ -104,35 +104,33 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
with(target.project) {
//TODO possibly issue with forced instantiation
whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}