All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.gradle.plugin.hierarchy.defaultKotlinHierarchySetup.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2023 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.hierarchy

import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.NativeTargetShortcutTrace.Companion.nativeTargetShortcutTrace
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyTemplate
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.FinaliseRefinesEdges
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.KotlinDefaultHierarchyFallbackDependsOnUsageDetected
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.KotlinDefaultHierarchyFallbackIllegalTargetNames
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected
import org.jetbrains.kotlin.gradle.plugin.diagnostics.kotlinToolingDiagnosticsCollector
import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic
import org.jetbrains.kotlin.gradle.plugin.requiredStage
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName

internal suspend fun Project.setupDefaultKotlinHierarchy() = requiredStage(FinaliseRefinesEdges) setup@{
    val extension = project.multiplatformExtensionOrNull ?: return@setup

    /* User configured a target hierarchy explicitly: No need for our defaults here */
    if (extension.hierarchy.appliedTemplates.isNotEmpty()) return@setup

    /* User explicitly disabled the default target hierarchy by Gradle property */
    if (!kotlinPropertiesProvider.mppApplyDefaultHierarchyTemplate) {
        setupPreMultiplatformStableDefaultDependsOnEdges()
        return@setup
    }

    /*
    User used ios(), tvos(), ... shortcuts.
    This will be incompatible with the default template, as it sets dependsOn edges.
    We detect this case manually to give even better diagnostic
     */
    run check@{
        extension.sourceSets
            .mapNotNull { it.nativeTargetShortcutTrace }
            .toSet()
            .ifEmpty { return@check }
            .onEach { trace -> project.reportDiagnostic(KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected(project, trace)) }

        setupPreMultiplatformStableDefaultDependsOnEdges()
        return@setup
    }


    /*
    User manually added a .dependsOn:
    We fall back to the old behaviour and add the commonMain/commonTest default edges
     */
    run check@{
        val sourceSetsWithDependsOnEdges = extension.sourceSets.filter { sourceSet -> sourceSet.dependsOn.isNotEmpty() }
        if (sourceSetsWithDependsOnEdges.isEmpty()) return@check
        val diagnostic = KotlinDefaultHierarchyFallbackDependsOnUsageDetected(project, sourceSetsWithDependsOnEdges)
        kotlinToolingDiagnosticsCollector.report(project, diagnostic)
        setupPreMultiplatformStableDefaultDependsOnEdges()
        return@setup
    }


    /*
    Using a group of the 'defaultTargetHierarchy' as 'target name' will potentially lead to conflicts e.g.:
    linuxX64("linux") or macosX64("native") can lead to confusion of for 'linuxMain' and 'nativeMain' SourceSets
     */
    run check@{
        val illegalTargetNamesUsed = illegalTargetNamesUsed()
        if (illegalTargetNamesUsed.isEmpty()) return@check
        val diagnostic = KotlinDefaultHierarchyFallbackIllegalTargetNames(project, illegalTargetNamesUsed)
        kotlinToolingDiagnosticsCollector.report(project, diagnostic)
        setupPreMultiplatformStableDefaultDependsOnEdges()
        return@setup
    }

    extension.applyDefaultHierarchyTemplate()
}

private suspend fun Project.illegalTargetNamesUsed(): Set {
    val targets = multiplatformExtension.awaitTargets()
    val targetNames = targets.map { it.name }.toSet()
    return targets.flatMap { it.compilations }.mapNotNull { compilation ->
        val hierarchy = KotlinHierarchyTemplate.default.buildHierarchy(compilation) ?: return@mapNotNull null
        val nodeNames = hierarchy.childrenClosure
            .mapNotNull { it.node as? KotlinHierarchy.Node.Group }
            .map { it.name }
            .toSortedSet(String.CASE_INSENSITIVE_ORDER)
        targetNames.intersect(nodeNames)
    }.flatten().toSet()
}

/**
 * Before 1.9.20 (and without any targetHierarchy applied), we just added default dependsOn
 * edges from 'main' compilations defaultSourceSets to 'commonMain' and
 * edges from 'test' compilations defaultSourceSets to 'commonTest
 */
private suspend fun Project.setupPreMultiplatformStableDefaultDependsOnEdges() = multiplatformExtension.targets
    .flatMap { target -> target.compilations }
    .forEach { compilation ->
        val sourceSetTree = KotlinSourceSetTree.orNull(compilation) ?: return@forEach
        val commonSourceSetName = lowerCamelCaseName("common", sourceSetTree.name)
        val commonSourceSet = multiplatformExtension.sourceSets.findByName(commonSourceSetName) ?: return@forEach
        compilation.defaultSourceSet.dependsOn(commonSourceSet)
    }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy