[Gradle] Implement ideaImportDependsOn API for KTIJ-20989
This will rely on a task called `prepareKotlinIdeaImport` which, if present, will be invoked by the IDE before import. Co-authored-by: Sergey Igushkin <Sergey.Igushkin@jetbrains.com> Merge-request: KT-MR-5688 Merged-by: Sebastian Sellmair <Sebastian.sellmair@jetbrains.com>
This commit is contained in:
committed by
Space
parent
003cb156bf
commit
0a85f0dd34
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
|
||||
import kotlin.test.Test
|
||||
|
||||
class CommonizerTaskTests {
|
||||
|
||||
private val rootProject = ProjectBuilder.builder().build() as ProjectInternal
|
||||
private val subproject = ProjectBuilder.builder().withName("subproject").withParent(rootProject).build() as ProjectInternal
|
||||
|
||||
private val kotlin = subproject.applyMultiplatformPlugin().apply {
|
||||
linuxX64()
|
||||
linuxArm64()
|
||||
|
||||
targets.flatMap { it.compilations }
|
||||
.filterIsInstance<KotlinNativeCompilation>()
|
||||
.forEach { compilation -> compilation.cinterops.create("dummy") }
|
||||
}
|
||||
|
||||
init {
|
||||
rootProject.enableCInteropCommonization()
|
||||
subproject.enableCInteropCommonization()
|
||||
|
||||
rootProject.evaluate()
|
||||
subproject.evaluate()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test runCommonizer task`() {
|
||||
subproject.tasks.getByName("runCommonizer")
|
||||
.assertDependsOn(subproject.tasks.getByName("commonize"))
|
||||
|
||||
/*
|
||||
Since commonizing the native distribution is done on the root project,
|
||||
we can also expect that the umbrella tasks are present there as well!
|
||||
*/
|
||||
rootProject.tasks.getByName("runCommonizer")
|
||||
.assertDependsOn(rootProject.tasks.getByName("commonize"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test commonizeNativeDistributionTask`() {
|
||||
val commonizeNativeDistributionTaskName = "commonizeNativeDistribution"
|
||||
subproject.assertContainsNoTaskWithName(commonizeNativeDistributionTaskName)
|
||||
|
||||
/* Native Distribution Commonization is only done on the root project */
|
||||
val rootProjectCommonizeNativeDistributionTask = rootProject.assertContainsTaskWithName(commonizeNativeDistributionTaskName)
|
||||
rootProject.tasks.getByName("commonize").assertDependsOn(rootProjectCommonizeNativeDistributionTask)
|
||||
subproject.tasks.getByName("commonize").assertDependsOn(rootProjectCommonizeNativeDistributionTask)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test commonizeCInteropTask`() {
|
||||
val commonizeCInteropTaskName = "commonizeCInterop"
|
||||
val commonizeCInteropTask = subproject.assertContainsTaskWithName(commonizeCInteropTaskName)
|
||||
subproject.tasks.getByName("commonize").assertDependsOn(commonizeCInteropTask)
|
||||
rootProject.assertContainsNoTaskWithName(commonizeCInteropTaskName)
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.fail
|
||||
|
||||
fun Task.isDependsOn(other: Task): Boolean {
|
||||
return other in this.taskDependencies.getDependencies(null)
|
||||
}
|
||||
|
||||
fun Task.assertDependsOn(other: Task) {
|
||||
if (!isDependsOn(other)) {
|
||||
fail("Expected ${this.path} to depend on ${other.path}")
|
||||
}
|
||||
}
|
||||
|
||||
fun Task.assertNotDependsOn(other: Task) {
|
||||
if (isDependsOn(other)) {
|
||||
fail("Expected ${this.path} to *not* depend on ${other.path}")
|
||||
}
|
||||
}
|
||||
|
||||
fun Task.assertTaskDependenciesEquals(dependencies: Set<Task>) {
|
||||
assertEquals(
|
||||
dependencies, this.taskDependencies.getDependencies(null),
|
||||
"Expected given set of taskDependencies for task ${this.path}"
|
||||
)
|
||||
}
|
||||
|
||||
fun Project.assertContainsTaskWithName(taskName: String): Task {
|
||||
this.getKotlinPluginVersion()
|
||||
return project.tasks.findByName(taskName)
|
||||
?: fail("Expected task with name $taskName in project ${this.path}")
|
||||
}
|
||||
|
||||
fun Project.assertContainsNoTaskWithName(taskName: String) {
|
||||
if (taskName in tasks.names) {
|
||||
fail("Expected *no* task with name $taskName in project ${this.path}")
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
@file:OptIn(Idea222Api::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.ide
|
||||
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.gradle.applyMultiplatformPlugin
|
||||
import org.jetbrains.kotlin.gradle.assertDependsOn
|
||||
import org.jetbrains.kotlin.gradle.assertTaskDependenciesEquals
|
||||
import org.jetbrains.kotlin.gradle.enableCInteropCommonization
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.prepareKotlinIdeaImportTask
|
||||
import org.jetbrains.kotlin.gradle.utils.UnsafeApi
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@OptIn(UnsafeApi::class)
|
||||
class PrepareKotlinIdeaImportTaskTest {
|
||||
|
||||
private companion object {
|
||||
const val prepareKotlinIdeaImportTaskName = "prepareKotlinIdeaImport"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test prepareKotlinIdeaImport task depending on commonizer and cinterop tasks`() {
|
||||
val rootProject = ProjectBuilder.builder().build() as ProjectInternal
|
||||
val subproject = ProjectBuilder.builder().withParent(rootProject).withName("subproject").build() as ProjectInternal
|
||||
rootProject.enableCInteropCommonization()
|
||||
subproject.enableCInteropCommonization()
|
||||
|
||||
val kotlin = subproject.applyMultiplatformPlugin()
|
||||
kotlin.linuxX64()
|
||||
kotlin.linuxArm64()
|
||||
|
||||
rootProject.evaluate()
|
||||
subproject.evaluate()
|
||||
|
||||
assertTrue(
|
||||
prepareKotlinIdeaImportTaskName in subproject.tasks.names,
|
||||
"Expected a task named '$prepareKotlinIdeaImportTaskName' to be registered"
|
||||
)
|
||||
|
||||
subproject.prepareKotlinIdeaImportTask.get().assertDependsOn(subproject.tasks.getByName("commonize"))
|
||||
subproject.prepareKotlinIdeaImportTask.get().assertDependsOn(subproject.tasks.getByName("copyCommonizeCInteropForIde"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test declaring dependsOnIdeaImport`() {
|
||||
val project = ProjectBuilder.builder().build()
|
||||
|
||||
assertNull(
|
||||
project.tasks.findByName(prepareKotlinIdeaImportTaskName),
|
||||
"Expected task $prepareKotlinIdeaImportTaskName to not be registered when no task declares a dependency"
|
||||
)
|
||||
|
||||
val testTaskA = project.tasks.register("testTaskA")
|
||||
project.ideaImportDependsOn(testTaskA)
|
||||
|
||||
val prepareKotlinIdeaImportTask = assertNotNull(
|
||||
project.tasks.findByName(prepareKotlinIdeaImportTaskName),
|
||||
"Expected task $prepareKotlinIdeaImportTaskName to be registered after $testTaskA declared dependency"
|
||||
)
|
||||
|
||||
prepareKotlinIdeaImportTask.assertTaskDependenciesEquals(setOf(testTaskA.get()))
|
||||
|
||||
val testTaskB = project.tasks.register("testTaskB")
|
||||
project.ideaImportDependsOn(testTaskB)
|
||||
|
||||
prepareKotlinIdeaImportTask.assertTaskDependenciesEquals(setOf(testTaskA.get(), testTaskB.get()))
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.plugin.ide
|
||||
|
||||
@RequiresOptIn("This API is only supported by IDEA 2022.2 or higher", level = RequiresOptIn.Level.ERROR)
|
||||
internal annotation class Idea222Api
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.plugin.ide
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.dependsOn
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.utils.UnsafeApi
|
||||
|
||||
@Idea222Api
|
||||
internal fun Project.ideaImportDependsOn(task: TaskProvider<*>) {
|
||||
@OptIn(UnsafeApi::class)
|
||||
prepareKotlinIdeaImportTask.dependsOn(task)
|
||||
}
|
||||
|
||||
@UnsafeApi("Use 'ideaImportDependsOn' instead")
|
||||
internal val Project.prepareKotlinIdeaImportTask: TaskProvider<Task>
|
||||
get() = locateOrRegisterTask(
|
||||
"prepareKotlinIdeaImport",
|
||||
configureTask = {
|
||||
description = "Umbrella for all tasks required to run before IDEA/Gradle import"
|
||||
}
|
||||
)
|
||||
+1
-1
@@ -56,4 +56,4 @@ import org.jetbrains.kotlin.gradle.plugin.getTestedVariantData
|
||||
|
||||
val compileJavaTaskProvider: TaskProvider<out JavaCompile>?
|
||||
get() = androidVariant.getJavaTaskProvider()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
|
||||
createTargetConfigurator().configureTarget(result)
|
||||
|
||||
SingleActionPerProject.run(project, "setUpKotlinNativePlatformDependencies") {
|
||||
project.gradle.projectsEvaluated {
|
||||
project.whenEvaluated {
|
||||
project.setupKotlinNativePlatformDependencies()
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -18,7 +18,10 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.addExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.Git
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.Url
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.Framework
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
|
||||
@@ -541,7 +544,8 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
kotlinExtension: KotlinMultiplatformExtension
|
||||
) {
|
||||
val podInstallTaskProvider = project.tasks.named(POD_INSTALL_TASK_NAME, PodInstallTask::class.java)
|
||||
project.tasks.register(POD_IMPORT_TASK_NAME) {
|
||||
val podImportTaskProvider = project.tasks.register(POD_IMPORT_TASK_NAME) {
|
||||
|
||||
it.group = TASK_GROUP
|
||||
it.description = "Called on Gradle sync, depends on Cinterop tasks for every used pod"
|
||||
it.dependsOn(podInstallTaskProvider)
|
||||
@@ -553,6 +557,10 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Older IDEs will explicitly call 'podImport' instead */
|
||||
@OptIn(Idea222Api::class)
|
||||
project.ideaImportDependsOn(podImportTaskProvider)
|
||||
}
|
||||
|
||||
private fun configureTestBinaries(project: Project, cocoapodsExtension: CocoapodsExtension) {
|
||||
|
||||
+10
-1
@@ -13,6 +13,8 @@ import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.GranularMetadataTransformation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||
@@ -57,7 +59,14 @@ internal fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTas
|
||||
|
||||
return locateOrRegisterTask(
|
||||
lowerCamelCaseName("transform", sourceSet.name, "CInteropDependenciesMetadataForIde"),
|
||||
invokeWhenRegistered = { commonizeTask.dependsOn(this) },
|
||||
invokeWhenRegistered = {
|
||||
@OptIn(Idea222Api::class)
|
||||
ideaImportDependsOn(this)
|
||||
|
||||
/* Older IDEs will still enqueue 'runCommonizer' task before import */
|
||||
@Suppress("deprecation")
|
||||
runCommonizerTask.dependsOn(this)
|
||||
},
|
||||
args = listOf(
|
||||
sourceSet,
|
||||
/* outputDirectory = */
|
||||
|
||||
+24
-4
@@ -11,6 +11,8 @@ import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.internal.isInIdeaSync
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
|
||||
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
|
||||
import org.jetbrains.kotlin.gradle.tasks.dependsOn
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
@@ -26,7 +28,14 @@ internal val Project.isOptimisticNumberCommonizationEnabled: Boolean
|
||||
internal val Project.commonizeTask: TaskProvider<Task>
|
||||
get() = locateOrRegisterTask(
|
||||
"commonize",
|
||||
invokeWhenRegistered = { @Suppress("deprecation") runCommonizerTask.dependsOn(this) },
|
||||
invokeWhenRegistered = {
|
||||
@OptIn(Idea222Api::class)
|
||||
ideaImportDependsOn(this)
|
||||
|
||||
/* 'runCommonizer' is called by older IDEs during import */
|
||||
@Suppress("deprecation")
|
||||
runCommonizerTask.dependsOn(this)
|
||||
},
|
||||
configureTask = {
|
||||
group = "interop"
|
||||
description = "Aggregator task for all c-interop & Native distribution commonizer tasks"
|
||||
@@ -40,7 +49,6 @@ internal val Project.commonizeTask: TaskProvider<Task>
|
||||
internal val Project.runCommonizerTask: TaskProvider<Task>
|
||||
get() = locateOrRegisterTask(
|
||||
"runCommonizer",
|
||||
invokeWhenRegistered = { dependsOn(commonizeTask) },
|
||||
configureTask = {
|
||||
group = "interop"
|
||||
description = "[Deprecated: Use 'commonize' instead]"
|
||||
@@ -74,7 +82,15 @@ internal val Project.copyCommonizeCInteropForIdeTask: TaskProvider<CopyCommonize
|
||||
if (commonizeCInteropTask != null) {
|
||||
return locateOrRegisterTask(
|
||||
"copyCommonizeCInteropForIde",
|
||||
invokeWhenRegistered = { if (isInIdeaSync) commonizeTask.dependsOn(this) },
|
||||
invokeWhenRegistered = {
|
||||
@OptIn(Idea222Api::class)
|
||||
ideaImportDependsOn(this)
|
||||
|
||||
/* Older IDEs will still call 'runCommonizer' -> 'commonize' tasks */
|
||||
if (isInIdeaSync) {
|
||||
commonizeTask.dependsOn(this)
|
||||
}
|
||||
},
|
||||
configureTask = {
|
||||
group = "interop"
|
||||
description = "Copies the output of ${commonizeCInteropTask.get().name} into " +
|
||||
@@ -90,7 +106,11 @@ internal val Project.commonizeNativeDistributionTask: TaskProvider<NativeDistrib
|
||||
if (!isAllowCommonizer()) return null
|
||||
return rootProject.locateOrRegisterTask(
|
||||
"commonizeNativeDistribution",
|
||||
invokeWhenRegistered = { rootProject.commonizeTask.dependsOn(this); cleanNativeDistributionCommonizerTask },
|
||||
invokeWhenRegistered = {
|
||||
commonizeTask.dependsOn(this)
|
||||
rootProject.commonizeTask.dependsOn(this)
|
||||
cleanNativeDistributionCommonizerTask
|
||||
},
|
||||
configureTask = {
|
||||
group = "interop"
|
||||
description = "Invokes the commonizer on platform libraries provided by the Kotlin/Native distribution"
|
||||
|
||||
+9
-10
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.getMetadataCompilationForSourceSet
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
|
||||
@@ -87,20 +88,18 @@ private fun File.listLibraryFiles(): List<File> = listFiles().orEmpty()
|
||||
internal val Project.isNativeDependencyPropagationEnabled: Boolean
|
||||
get() = PropertiesProvider(this).nativeDependencyPropagation ?: true
|
||||
|
||||
//for reflection call from KotlinCommonizerModelBuilder
|
||||
// DO NOT REFACTOR THIS FUNCTION!
|
||||
// TODO SELLMAIR: Resolve fragile reflection call from IDE plugin
|
||||
@JvmOverloads
|
||||
/**
|
||||
* Function signature needs to be kept stable since this is used during import
|
||||
* in IDEs (KotlinCommonizerModelBuilder) < 222
|
||||
*
|
||||
* IDEs >= will use the [ideaImportDependsOn] infrastructure
|
||||
*/
|
||||
@JvmName("isAllowCommonizer")
|
||||
internal fun Project.isAllowCommonizer(
|
||||
kotlinVersion: String = getKotlinPluginVersion()
|
||||
): Boolean {
|
||||
internal fun Project.isAllowCommonizer(): Boolean {
|
||||
assert(state.executed) { "'isAllowCommonizer' can only be called after project evaluation" }
|
||||
multiplatformExtensionOrNull ?: return false
|
||||
|
||||
//register commonizer only for 1.4+, only for HMPP projects
|
||||
return compareVersionNumbers(kotlinVersion, "1.4") >= 0
|
||||
&& multiplatformExtension.targets.any { it.platformType == KotlinPlatformType.native }
|
||||
return multiplatformExtension.targets.any { it.platformType == KotlinPlatformType.native }
|
||||
&& isKotlinGranularMetadataEnabled
|
||||
&& !isNativeDependencyPropagationEnabled // temporary fix: turn on commonizer only when native deps propagation is disabled
|
||||
}
|
||||
|
||||
+2
@@ -58,6 +58,8 @@ internal fun <T : Task> Project.registerTask(
|
||||
|
||||
internal fun TaskProvider<*>.dependsOn(other: TaskProvider<*>) = configure { it.dependsOn(other) }
|
||||
|
||||
internal fun TaskProvider<*>.dependsOn(other: Task) = configure { it.dependsOn(other) }
|
||||
|
||||
internal inline fun <reified S : Task> TaskCollection<in S>.withType(): TaskCollection<S> = withType(S::class.java)
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user