From 686159184055a1fb13dccac022bbc2677c2a6bbc Mon Sep 17 00:00:00 2001 From: Artem Daugel-Dauge Date: Tue, 20 Jun 2023 17:22:20 +0200 Subject: [PATCH] [Gradle] Migrate Xcode/CocoaPods warnings to the new diagnostics infra + elevate deprecation of useLibraries ^KT-59313 Verification Pending ^KT-58519 Verification Pending --- .../gradle/native/AppleFrameworkNonMacIT.kt | 3 +- .../kotlin/gradle/native/CocoaPodsGitIT.kt | 20 ++---- .../kotlin/gradle/native/CocoaPodsIT.kt | 38 ++++-------- .../kotlin/gradle/native/XCFrameworkIT.kt | 3 +- .../kotlin-library/build.gradle.kts | 3 +- .../diagnostics/KotlinToolingDiagnostics.kt | 14 +++++ .../plugin/mpp/apple/AppleXcodeTasks.kt | 7 +-- .../plugin/mpp/apple/XCFrameworkTask.kt | 13 ++-- .../native/cocoapods/CocoapodsExtension.kt | 39 ++---------- .../cocoapods/CocoapodsPluginDiagnostics.kt | 55 +++++++++++++++++ .../native/cocoapods/KotlinCocoapodsPlugin.kt | 51 +++++++--------- .../native/cocoapods/tasks/DefFileTask.kt | 10 --- .../native/cocoapods/tasks/PodGenTask.kt | 8 +-- .../gradle/unitTests/CocoapodsUnitTests.kt | 61 +++++++++++++++++++ .../kotlin/gradle/util/buildProject.kt | 15 +++++ 15 files changed, 208 insertions(+), 132 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsPluginDiagnostics.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CocoapodsUnitTests.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt index ad6dbf592d2..a6624c3ca01 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.native import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.condition.OS @@ -27,7 +28,7 @@ class AppleFrameworkNonMacIT : KGPBaseTest() { ) build("podImport", buildOptions = buildOptions) { - assertOutputContains("Kotlin Cocoapods Plugin is fully supported on mac machines only. Gradle tasks that can not run on non-mac hosts will be skipped.") + assertHasDiagnostic(CocoapodsPluginDiagnostics.UnsupportedOs) } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsGitIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsGitIT.kt index 14b07bdbea0..d155a4e2b56 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsGitIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsGitIT.kt @@ -10,7 +10,6 @@ import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.client.request.* import io.ktor.http.* -import io.ktor.util.* import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -21,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Compan import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_IMPORT_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_SETUP_BUILD_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_SPEC_TASK_NAME +import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.assertProcessRunResult import org.jetbrains.kotlin.gradle.util.capitalize @@ -435,21 +435,11 @@ class CocoaPodsGitIT : KGPBaseTest() { @DisplayName("Checking useLibraries mode") @GradleTest fun testUseLibrariesMode(gradleVersion: GradleVersion) { - nativeProjectWithCocoapodsAndIosAppPodFile(gradleVersion = gradleVersion) { - buildGradleKts.addCocoapodsBlock("useLibraries()") - buildGradleKts.addPod("AFNetworking", configuration = "headers = \"AFNetworking/AFNetworking.h\"") - testImport() - } - } - - @DisplayName("Checking that useLibraries mode warns when pod is added without headers specified") - @GradleTest - fun testUseLibrariesModeWarnsWhenPodIsAddedWithoutHeadersSpecified(gradleVersion: GradleVersion) { - nativeProjectWithCocoapodsAndIosAppPodFile(gradleVersion = gradleVersion) { - buildGradleKts.addCocoapodsBlock("useLibraries()") - buildGradleKts.addPod("AFNetworking") + nativeProjectWithCocoapodsAndIosAppPodFile(projectName = "native-cocoapods-template-groovy", gradleVersion = gradleVersion) { + buildGradle.addCocoapodsBlock("useLibraries()".trimIndent()) + buildGradle.addPod("AFNetworking", configuration = "headers = \"AFNetworking/AFNetworking.h\"") testImport { - assertOutputContains("w: Pod 'AFNetworking' should have 'headers' property specified when using 'useLibraries()'") + assertHasDiagnostic(CocoapodsPluginDiagnostics.UseLibrariesUsed) } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt index 2fa85fb20df..4233f96f739 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.DUMMY_FRAMEWORK_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_IMPORT_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_SPEC_TASK_NAME +import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.assertProcessRunResult import org.jetbrains.kotlin.gradle.util.replaceText @@ -85,19 +86,6 @@ class CocoaPodsIT : KGPBaseTest() { } } - @DisplayName("Checking the warning about using deprecated podspec path") - @GradleTest - fun warnIfDeprecatedPodspecPathIsUsed(gradleVersion: GradleVersion) { - nativeProjectWithCocoapodsAndIosAppPodFile(cocoapodsSingleKtPod, gradleVersion) { - build(":kotlin-library:tasks") { - assertOutputContains( - listOf("Deprecated DSL found on ${projectPath.toRealPath().absolutePathString()}", "kotlin-library", "build.gradle.kts") - .joinToString(separator = File.separator) - ) - } - } - } - @DisplayName("Build with error if project version is not specified for cocoapods") @GradleTest fun errorIfVersionIsNotSpecified(gradleVersion: GradleVersion) { @@ -691,7 +679,7 @@ class CocoaPodsIT : KGPBaseTest() { """.trimIndent() ) buildWithCocoapodsWrapper(":linkPodDebugFrameworkIOS") { - assertOutputContains("Dependency on 'AFNetworking' with option 'linkOnly=true' is unused for building static frameworks") + assertHasDiagnostic(CocoapodsPluginDiagnostics.LinkOnlyUsedWithStaticFramework) } } } @@ -704,9 +692,9 @@ class CocoaPodsIT : KGPBaseTest() { } } - @DisplayName("Configuration fails when trying to depend on non-declared pod") + @DisplayName("Error reported when trying to depend on non-declared pod") @GradleTest - fun testConfigurationFailsWhenTryingToDependOnNonDeclaredPod(gradleVersion: GradleVersion) { + fun testErrorReportedWhenTryingToDependOnNonDeclaredPod(gradleVersion: GradleVersion) { nativeProjectWithCocoapodsAndIosAppPodFile(cocoapodsDependantPodsProjectName, gradleVersion) { buildGradleKts.addCocoapodsBlock( """ @@ -720,15 +708,15 @@ class CocoaPodsIT : KGPBaseTest() { ) ) - buildAndFail(":help", buildOptions = buildOptions) { + build(":help", buildOptions = buildOptions) { assertOutputContains("Couldn't find declaration of pod 'JBNonExistent' (interop-binding dependency of pod 'Foo')") } } } - @DisplayName("Configuration fails when dependant pods are in the wrong order") + @DisplayName("Error reported when dependant pods are in the wrong order") @GradleTest - fun testConfigurationFailsWhenDependantPodsAreInTheWrongOrder(gradleVersion: GradleVersion) { + fun testErrorReportedWhenDependantPodsAreInTheWrongOrder(gradleVersion: GradleVersion) { nativeProjectWithCocoapodsAndIosAppPodFile(cocoapodsDependantPodsProjectName, gradleVersion) { buildGradleKts.addCocoapodsBlock( """ @@ -743,15 +731,15 @@ class CocoaPodsIT : KGPBaseTest() { ) ) - buildAndFail(":help", buildOptions = buildOptions) { - assertOutputContains("Couldn't find declaration of pod 'Bar' (interop-binding dependency of pod 'Foo')") + build(":help", buildOptions = buildOptions) { + assertHasDiagnostic(CocoapodsPluginDiagnostics.InteropBindingUnknownDependency) } } } - @DisplayName("Configuration fails when pod depends on itself") + @DisplayName("Error reported when pod depends on itself") @GradleTest - fun testConfigurationFailsWhenPodDependsOnItself(gradleVersion: GradleVersion) { + fun testErrorReportedWhenPodDependsOnItself(gradleVersion: GradleVersion) { nativeProjectWithCocoapodsAndIosAppPodFile(cocoapodsDependantPodsProjectName, gradleVersion) { buildGradleKts.addCocoapodsBlock( """ @@ -765,8 +753,8 @@ class CocoaPodsIT : KGPBaseTest() { ) ) - buildAndFail(":help", buildOptions = buildOptions) { - assertOutputContains("Pod 'Foo' has an interop-binding dependency on itself") + build(":help", buildOptions = buildOptions) { + assertHasDiagnostic(CocoapodsPluginDiagnostics.InteropBindingSelfDependency) } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/XCFrameworkIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/XCFrameworkIT.kt index 6eaecb76afe..c991407e843 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/XCFrameworkIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/XCFrameworkIT.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.native import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.replaceFirst import org.jetbrains.kotlin.gradle.util.replaceText @@ -69,7 +70,7 @@ class XCFrameworkIT : KGPBaseTest() { assertTasksExecuted(":shared:assembleOtherDebugXCFramework") assertDirectoryInProjectExists("shared/build/XCFrameworks/debug/other.xcframework") assertDirectoryInProjectExists("shared/build/XCFrameworks/debug/other.xcframework/ios-arm64/dSYMs/shared.framework.dSYM") - assertOutputContains("Name of XCFramework 'other' differs from inner frameworks name 'shared'! Framework renaming is not supported yet") + assertHasDiagnostic(KotlinToolingDiagnostics.XCFrameworkDifferentInnerFrameworksName) } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts index 6db74ab0c57..8e7e1dba651 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-cocoapods-single/kotlin-library/build.gradle.kts @@ -20,8 +20,7 @@ kotlin { cocoapods { summary = "CocoaPods test library" homepage = "https://github.com/JetBrains/kotlin" - // test warnIfDeprecatedPodspecPathIsUsed depends on deprecated api usage - pod("pod_dependency", "1.0", project.file("../pod_dependency/pod_dependency.podspec")) + pod("pod_dependency", "1.0", project.file("../pod_dependency")) pod("subspec_dependency/Core", "1.0", project.file("../subspec_dependency")) podfile = project.file("../ios-app/Podfile") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index 3c89d5103fc..631f7f9dfae 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt @@ -506,6 +506,20 @@ object KotlinToolingDiagnostics { ) } + object XCFrameworkDifferentInnerFrameworksName : ToolingDiagnosticFactory(WARNING) { + operator fun invoke(xcFramework: String, innerFrameworks: String) = build( + "Name of XCFramework '$xcFramework' differs from inner frameworks name '$innerFrameworks'! Framework renaming is not supported yet" + ) + } + + object UnknownAppleFrameworkBuildType : ToolingDiagnosticFactory(WARNING) { + operator fun invoke(envConfiguration: String) = build( + """ + Unable to detect Kotlin framework build type for CONFIGURATION=$envConfiguration automatically. + Specify 'KOTLIN_FRAMEWORK_BUILD_TYPE' to 'debug' or 'release' + """.trimIndent() + ) + } } private fun String.indentLines(nSpaces: Int = 4, skipFirstLine: Boolean = true): String { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt index ced101e3103..870f0e3c0bf 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt @@ -14,6 +14,8 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.process.ExecOperations import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType import org.jetbrains.kotlin.gradle.plugin.mpp.enabledOnCurrentHost @@ -106,10 +108,7 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): Ta if (envBuildType == null || envTargets.isEmpty() || envFrameworkSearchDir == null) { val envConfiguration = System.getenv("CONFIGURATION") if (envTargets.isNotEmpty() && envConfiguration != null) { - logger.warn( - "Unable to detect Kotlin framework build type for CONFIGURATION=$envConfiguration automatically. " + - "Specify 'KOTLIN_FRAMEWORK_BUILD_TYPE' to 'debug' or 'release'" - ) + project.reportDiagnostic(KotlinToolingDiagnostics.UnknownAppleFrameworkBuildType(envConfiguration)) } else { logger.debug("Not registering $frameworkTaskName, since not called from Xcode") } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XCFrameworkTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XCFrameworkTask.kt index af87aa9d98a..ad22f21f349 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XCFrameworkTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XCFrameworkTask.kt @@ -8,12 +8,13 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.apple import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.process.ExecOperations import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.UsesKotlinToolingDiagnostics import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType import org.jetbrains.kotlin.gradle.tasks.* @@ -23,6 +24,7 @@ import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File import javax.inject.Inject +@Suppress("unused") // used through .values() call internal enum class AppleTarget( val targetName: String, val targets: List @@ -147,7 +149,7 @@ abstract class XCFrameworkTask internal constructor( private val execOperations: ExecOperations, private val projectLayout: ProjectLayout, -) : DefaultTask() { +) : DefaultTask(), UsesKotlinToolingDiagnostics { init { onlyIf { HostManager.hostIsMac } } @@ -238,9 +240,10 @@ internal constructor( frameworks.joinToString("\n") { it.file.path }) } if (name != xcfName) { - logger.warn( - "Name of XCFramework '$rawXcfName' differs from inner frameworks name '$name'! Framework renaming is not supported yet" - ) + toolingDiagnosticsCollector.get().report(this, KotlinToolingDiagnostics.XCFrameworkDifferentInnerFrameworksName( + xcFramework = rawXcfName, + innerFrameworks = name, + )) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt index f800e8671f2..6b5fe2770cd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt @@ -14,8 +14,10 @@ import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_FRAMEWORK_PREFIX +import org.jetbrains.kotlin.gradle.plugin.diagnostics.kotlinToolingDiagnosticsCollector import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics import org.jetbrains.kotlin.gradle.utils.getFile import java.io.File import java.net.URI @@ -49,18 +51,12 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje /** * Setup plugin to generate synthetic xcodeproj compatible with static libraries - * - * This option is not supported and scheduled to be removed. If you are using this please - * file an issue with your case to [https://kotl.in/issue](https://kotl.in/issue) */ - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated("This option is not supported and scheduled to be removed") + @Deprecated("'useLibraries' mode is removed", level = DeprecationLevel.ERROR) fun useLibraries() { - useLibraries = true + project.kotlinToolingDiagnosticsCollector.report(project, CocoapodsPluginDiagnostics.UseLibrariesUsed()) } - internal var useLibraries: Boolean = false - /** * Configure name of the pod built from this project. */ @@ -169,31 +165,6 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje // Empty string will lead to an attempt to create two podDownload tasks. // One is original podDownload and second is podDownload + pod.name require(name.isNotEmpty()) { "Please provide not empty pod name to avoid ambiguity" } - var podSource = path - if (path != null && !path.isDirectory) { - val pattern = "\\W*pod(.*\"${name}\".*)".toRegex() - val buildScript = project.buildFile - val lines = buildScript.readLines() - val lineNumber = lines.indexOfFirst { pattern.matches(it) } - val warnMessage = if (lineNumber != -1) run { - val lineContent = lines[lineNumber].trimIndent() - val newContent = lineContent.replace(path.name, "") - """ - |Deprecated DSL found on ${buildScript.absolutePath}${File.pathSeparator}${lineNumber + 1}: - |Found: "$lineContent" - |Expected: "$newContent" - |Please, change the path to avoid this warning. - | - """.trimMargin() - } else - """ - |Deprecated DSL is used for pod "$name". - |Please, change its path from ${path.path} to ${path.parentFile.path} - | - """.trimMargin() - project.logger.warn(warnMessage) - podSource = path.parentFile - } addToPods( project.objects.newInstance( CocoapodsDependency::class.java, @@ -202,7 +173,7 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje ).apply { this.headers = headers this.version = version - source = podSource?.let { Path(it) } + source = path?.let(::Path) this.linkOnly = linkOnly } ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsPluginDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsPluginDiagnostics.kt new file mode 100644 index 00000000000..8659c8bb7a4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsPluginDiagnostics.kt @@ -0,0 +1,55 @@ +/* + * 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.targets.native.cocoapods + +import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnostic.Severity.ERROR +import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnostic.Severity.WARNING +import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnosticFactory + +@InternalKotlinGradlePluginApi // used in integration tests +object CocoapodsPluginDiagnostics { + + object DeprecatedPropertiesUsed : ToolingDiagnosticFactory(WARNING) { + operator fun invoke(usedDeprecatedProperties: List) = build( + """ + |Properties + | ${usedDeprecatedProperties.joinToString(separator = "\n| ")} + |are not supported and will be ignored since CocoaPods plugin generates all required properties automatically. + """.trimMargin() + ) + } + + object LinkOnlyUsedWithStaticFramework : ToolingDiagnosticFactory(WARNING) { + operator fun invoke(podName: String) = build( + """ + Dependency on '$podName' with option 'linkOnly=true' is unused for building static frameworks. + When using static linkage you will need to provide all dependencies for linking the framework into a final application. + """.trimIndent() + ) + } + + object UnsupportedOs : ToolingDiagnosticFactory(WARNING) { + operator fun invoke() = build( + """ + Kotlin CocoaPods Plugin is fully supported on MacOS machines only. Gradle tasks that can not run on non-mac hosts will be skipped. + """.trimIndent() + ) + } + + object UseLibrariesUsed : ToolingDiagnosticFactory(ERROR) { + operator fun invoke() = build("'useLibraries' mode is removed") + } + + object InteropBindingSelfDependency : ToolingDiagnosticFactory(ERROR) { + operator fun invoke(podName: String) = build("Pod '$podName' has an interop-binding dependency on itself") + } + + object InteropBindingUnknownDependency : ToolingDiagnosticFactory(ERROR) { + operator fun invoke(podName: String, dependencyName: String) = build("Couldn't find declaration of pod '$dependencyName' (interop-binding dependency of pod '${podName}')") + } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt index 3557ed67a3f..65379e97337 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt @@ -23,6 +23,8 @@ import org.jetbrains.kotlin.gradle.dsl.* 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.diagnostics.reportDiagnostic +import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnosticOncePerProject import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn import org.jetbrains.kotlin.gradle.plugin.mpp.* @@ -30,6 +32,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.apple.* import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AppleSdk import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AppleTarget import org.jetbrains.kotlin.gradle.plugin.whenEvaluated +import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics import org.jetbrains.kotlin.gradle.targets.native.cocoapods.KotlinArtifactsPodspecExtension import org.jetbrains.kotlin.gradle.targets.native.cocoapods.kotlinArtifactsPodspecExtension import org.jetbrains.kotlin.gradle.targets.native.tasks.* @@ -243,22 +246,6 @@ open class KotlinCocoapodsPlugin : Plugin { val platforms = project.findProperty(PLATFORM_PROPERTY)?.toString()?.split(",", " ")?.filter { it.isNotBlank() } val archs = project.findProperty(ARCHS_PROPERTY)?.toString()?.split(",", " ")?.filter { it.isNotBlank() } - if ( - project.findProperty(CFLAGS_PROPERTY) != null || - project.findProperty(FRAMEWORK_PATHS_PROPERTY) != null || - project.findProperty(HEADER_PATHS_PROPERTY) != null - ) { - logger.warn( - """ - Properties - kotlin.native.cocoapods.cflags - kotlin.native.cocoapods.paths.frameworks - kotlin.native.cocoapods.paths.headers - are not supported and will be ignored since Cocoapods plugin generates all required properties automatically. - """.trimIndent() - ) - } - if (platforms == null || archs == null) { check(project.findProperty(TARGET_PROPERTY) == null) { """ @@ -298,6 +285,15 @@ open class KotlinCocoapodsPlugin : Plugin { } } + private fun reportDeprecatedPropertiesUsage(project: Project) { + listOf(CFLAGS_PROPERTY, FRAMEWORK_PATHS_PROPERTY, HEADER_PATHS_PROPERTY) + .filter { project.findProperty(it) != null } + .takeIf { it.isNotEmpty() } + ?.let { + project.reportDiagnostic(CocoapodsPluginDiagnostics.DeprecatedPropertiesUsed(it)) + } + } + private fun createInterops( project: Project, kotlinExtension: KotlinMultiplatformExtension, @@ -315,7 +311,6 @@ open class KotlinCocoapodsPlugin : Plugin { lowerCamelCaseName("generateDef", pod.moduleName).asValidTaskName() ) { it.pod.set(pod) - it.useLibraries.set(cocoapodsExtension.useLibraries) it.description = "Generates a def file for CocoaPods dependencies with module ${pod.moduleName}" // This task is an implementation detail so we don't add it in any group // to avoid showing it in the `tasks` output. @@ -374,11 +369,15 @@ open class KotlinCocoapodsPlugin : Plugin { dependencyName: String, ) { if (pod.name == dependencyName) { - error("Pod '${pod.name}' has an interop-binding dependency on itself") + project.reportDiagnosticOncePerProject(CocoapodsPluginDiagnostics.InteropBindingSelfDependency(pod.name)) + return } val dependencyPod = cocoapodsExtension.pods.findByName(dependencyName) - ?: error("Couldn't find declaration of pod '$dependencyName' (interop-binding dependency of pod '${pod.name}')") + ?: run { + project.reportDiagnosticOncePerProject(CocoapodsPluginDiagnostics.InteropBindingUnknownDependency(pod.name, dependencyName)) + return + } val dependencyTaskName = cinterops.getByName(dependencyPod.moduleName).interopProcessingTaskName val dependencyTask = project.tasks.named(dependencyTaskName) @@ -520,7 +519,6 @@ open class KotlinCocoapodsPlugin : Plugin { task.description = "Сreates a synthetic Xcode project to retrieve CocoaPods dependencies" task.podspec.set(podspecTaskProvider.map { it.outputFile }) task.podName.set(project.provider { cocoapodsExtension.name }) - task.useLibraries.set(project.provider { cocoapodsExtension.useLibraries }) task.specRepos.set(project.provider { cocoapodsExtension.specRepos }) task.family.set(family) task.platformSettings.set(platformSettings) @@ -799,10 +797,7 @@ open class KotlinCocoapodsPlugin : Plugin { project.runProjectConfigurationHealthCheckWhenEvaluated { cocoapodsExtension.pods.all { pod -> if (pod.linkOnly && cocoapodsExtension.podFrameworkIsStatic.get()) { - logger.warn(""" - Dependency on '${pod.name}' with option 'linkOnly=true' is unused for building static frameworks. - When using static linkage you will need to provide all dependencies for linking the framework into a final application. - """.trimIndent()) + project.reportDiagnostic(CocoapodsPluginDiagnostics.LinkOnlyUsedWithStaticFramework(pod.name)) } } } @@ -854,12 +849,10 @@ open class KotlinCocoapodsPlugin : Plugin { registerPodPublishTasks(project, cocoapodsExtension) if (!HostManager.hostIsMac) { - logger.warn( - """ - Kotlin Cocoapods Plugin is fully supported on mac machines only. Gradle tasks that can not run on non-mac hosts will be skipped. - """.trimIndent() - ) + reportDiagnostic(CocoapodsPluginDiagnostics.UnsupportedOs()) } + reportDeprecatedPropertiesUsage(project) + createInterops(project, kotlinExtension, cocoapodsExtension) configureLinkingOptions(project, cocoapodsExtension) checkLinkOnlyNotUsedWithStaticFramework(project, cocoapodsExtension) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt index c90e935a329..8e089268e34 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt @@ -11,7 +11,6 @@ import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputFile @@ -31,9 +30,6 @@ abstract class DefFileTask @Inject constructor(projectLayout: ProjectLayout) : D @get:Nested abstract val pod: Property - @get:Input - abstract val useLibraries: Property - @get:OutputFile val defFile: Provider = projectLayout.cocoapodsBuildDirs.defs.map { it.file("${pod.get().moduleName}.def") } @@ -51,12 +47,6 @@ abstract class DefFileTask @Inject constructor(projectLayout: ProjectLayout) : D with(pod.get()) { when { headers != null -> appendLine("headers = $headers") - useLibraries.get() -> logger.warn( - """ - w: Pod '$moduleName' should have 'headers' property specified when using 'useLibraries()'. - Otherwise code from this pod won't be accessible from Kotlin. - """.trimIndent() - ) else -> { appendLine("modules = $moduleName") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt index 6d8f0804435..b08c5c7a853 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt @@ -39,9 +39,6 @@ abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : Co @get:Input internal abstract val podName: Property - @get:Input - internal abstract val useLibraries: Property - @get:Input internal abstract val family: Property @@ -80,9 +77,8 @@ abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : Co } appendLine("target '${family.platformLiteral}' do") - if (useLibraries.get().not()) { - appendLine("\tuse_frameworks!") - } + appendLine("\tuse_frameworks!") + val settings = platformSettings.get() val deploymentTarget = settings.deploymentTarget if (deploymentTarget != null) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CocoapodsUnitTests.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CocoapodsUnitTests.kt new file mode 100644 index 00000000000..2872d072d89 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CocoapodsUnitTests.kt @@ -0,0 +1,61 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests + +import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.CFLAGS_PROPERTY +import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.FRAMEWORK_PATHS_PROPERTY +import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.HEADER_PATHS_PROPERTY +import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics +import org.jetbrains.kotlin.gradle.util.* +import org.jetbrains.kotlin.gradle.util.assertContainsDiagnostic +import org.jetbrains.kotlin.konan.target.HostManager +import org.junit.Assume +import org.junit.Test + +class CocoapodsUnitTests { + @Test + fun `warning is reported on non-mac machines`() { + Assume.assumeTrue(!HostManager.hostIsMac) + + buildProjectWithCocoapods { + assertContainsDiagnostic(CocoapodsPluginDiagnostics.UnsupportedOs) + } + } + + @Test + fun `warning is reported when using deprecated properties`() { + buildProjectWithMPP { + propertiesExtension.set(CFLAGS_PROPERTY, "cflags value") + + applyCocoapodsPlugin() + assertContainsDiagnostic(CocoapodsPluginDiagnostics.DeprecatedPropertiesUsed) + } + + buildProjectWithMPP { + propertiesExtension.set(HEADER_PATHS_PROPERTY, "header paths value") + + applyCocoapodsPlugin() + assertContainsDiagnostic(CocoapodsPluginDiagnostics.DeprecatedPropertiesUsed) + } + + buildProjectWithMPP { + propertiesExtension.set(FRAMEWORK_PATHS_PROPERTY, "framework paths value") + + applyCocoapodsPlugin() + assertContainsDiagnostic(CocoapodsPluginDiagnostics.DeprecatedPropertiesUsed) + } + + buildProjectWithMPP { + propertiesExtension.set(FRAMEWORK_PATHS_PROPERTY, "framework paths value") + propertiesExtension.set(HEADER_PATHS_PROPERTY, "header paths value") + + applyCocoapodsPlugin() + assertContainsDiagnostic(CocoapodsPluginDiagnostics.DeprecatedPropertiesUsed(listOf(FRAMEWORK_PATHS_PROPERTY, HEADER_PATHS_PROPERTY))) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/buildProject.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/buildProject.kt index 3d0b6048ef9..ca44719138a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/buildProject.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/buildProject.kt @@ -19,6 +19,8 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_COMPATIBILITY_METADATA_VARIANT import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_INTRANSITIVE_METADATA_CONFIGURATION +import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension +import org.jetbrains.kotlin.gradle.plugin.getExtension import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension import org.jetbrains.kotlin.gradle.unitTests.kpm.applyKpmPlugin @@ -50,6 +52,11 @@ fun buildProjectWithJvm(projectBuilder: ProjectBuilder.() -> Unit = {}, code: Pr code() } +fun buildProjectWithCocoapods(projectBuilder: ProjectBuilder.() -> Unit = {}, code: Project.() -> Unit = {}) = buildProject(projectBuilder) { + project.applyCocoapodsPlugin() + code() +} + fun Project.applyKotlinJvmPlugin() { project.plugins.apply(KotlinPluginWrapper::class.java) } @@ -87,6 +94,14 @@ fun Project.applyMultiplatformPlugin(): KotlinMultiplatformExtension { return extensions.getByName("kotlin") as KotlinMultiplatformExtension } +fun Project.applyCocoapodsPlugin(): CocoapodsExtension { + val kotlinExtension = applyMultiplatformPlugin() + plugins.apply("org.jetbrains.kotlin.native.cocoapods") + return kotlinExtension.getExtension("cocoapods")!!.also { + it.version = "1.0" + } +} + val Project.propertiesExtension: ExtraPropertiesExtension get() = extensions.getByType(ExtraPropertiesExtension::class.java)