diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/AbstractPodInstallTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/AbstractPodInstallTask.kt index c6161daa78c..c8f1d09f8ff 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/AbstractPodInstallTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/AbstractPodInstallTask.kt @@ -12,6 +12,7 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.work.DisableCachingByDefault import org.jetbrains.kotlin.gradle.utils.CommandFallback +import org.jetbrains.kotlin.gradle.utils.RunProcessResult import org.jetbrains.kotlin.gradle.utils.onlyIfCompat import org.jetbrains.kotlin.gradle.utils.runCommandWithFallback import java.io.File @@ -62,11 +63,12 @@ abstract class AbstractPodInstallTask : CocoapodsTask() { return runCommandWithFallback(podInstallCommand, logger, - fallback = { retCode, output, process -> + fallback = { result -> + val output = result.stdErr.ifBlank { result.stdOut } if (output.contains("out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`") && updateRepo.not()) { CommandFallback.Action(runPodInstall(true)) } else { - CommandFallback.Error(sharedHandleError(podInstallCommand, retCode, output, process)) + CommandFallback.Error(sharedHandleError(podInstallCommand, result)) } }, processConfiguration = { @@ -76,27 +78,45 @@ abstract class AbstractPodInstallTask : CocoapodsTask() { }) } - private fun sharedHandleError(podInstallCommand: List, retCode: Int, error: String, process: Process): String? { - return if (error.contains("No such file or directory")) { - val command = podInstallCommand.joinToString(" ") - """ - |'$command' command failed with an exception: - | $error - | + private fun sharedHandleError(podInstallCommand: List, result: RunProcessResult): String? { + val command = podInstallCommand.joinToString(" ") + val output = result.stdErr.ifBlank { result.stdOut } + + var message = """ + |'$command' command failed with an exception: + | stdErr: ${result.stdErr} + | stdOut: ${result.stdOut} + | exitCode: ${result.retCode} + | + """.trimMargin() + + if (output.contains("No such file or directory")) { + message += """ | Full command: $command | | Possible reason: CocoaPods is not installed - | Please check that CocoaPods v1.10 or above is installed. + | Please check that CocoaPods v1.14 or above is installed. | | To check CocoaPods version type 'pod --version' in the terminal | | To install CocoaPods execute 'sudo gem install cocoapods' + | For more information, refer to the documentation: https://kotl.in/fx2sde | """.trimMargin() + return message + } else if (output.contains("[Xcodeproj] Unknown object version")) { + message += """ + | Your CocoaPods installation may be outdated or corrupted + | + | To update CocoaPods execute 'sudo gem install cocoapods' + | For more information, refer to the documentation: https://kotl.in/0xfxux + | + """.trimMargin() + return message } else { - handleError(retCode, error, process) + return handleError(result) } } - abstract fun handleError(retCode: Int, error: String, process: Process): String? + abstract fun handleError(result: RunProcessResult): String? } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallSyntheticTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallSyntheticTask.kt index fef1bad4af3..61a07b20dbc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallSyntheticTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallSyntheticTask.kt @@ -13,6 +13,7 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.work.DisableCachingByDefault import org.jetbrains.kotlin.gradle.plugin.cocoapods.platformLiteral +import org.jetbrains.kotlin.gradle.utils.RunProcessResult import org.jetbrains.kotlin.konan.target.Family import java.io.File @@ -42,18 +43,18 @@ abstract class PodInstallSyntheticTask : AbstractPodInstallTask() { super.doPodInstall() } - override fun handleError(retCode: Int, error: String, process: Process): String? { + override fun handleError(result: RunProcessResult): String? { var message = """ - |'pod install' command on the synthetic project failed with return code: $retCode + |'pod install' command on the synthetic project failed with return code: ${result.retCode} | - | Error: ${error.lines().filter { it.contains("[!]") }.joinToString("\n")} + | Error: ${result.stdErr.lines().filter { it.contains("[!]") }.joinToString("\n")} | """.trimMargin() if ( - error.contains("deployment target") || - error.contains("no platform was specified") || - error.contains(Regex("The platform of the target .+ is not compatible with `${podName.get()}")) + result.stdErr.contains("deployment target") || + result.stdErr.contains("no platform was specified") || + result.stdErr.contains(Regex("The platform of the target .+ is not compatible with `${podName.get()}")) ) { message += """ | @@ -68,9 +69,9 @@ abstract class PodInstallSyntheticTask : AbstractPodInstallTask() { """.trimMargin() return message } else if ( - error.contains("Unable to add a source with url") || - error.contains("Couldn't determine repo name for URL") || - error.contains("Unable to find a specification") + result.stdErr.contains("Unable to add a source with url") || + result.stdErr.contains("Couldn't determine repo name for URL") || + result.stdErr.contains("Unable to find a specification") ) { message += """ | diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt index 3103873cd20..12eba644426 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.Cocoapods import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.SpecRepos import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingCocoapodsMessage import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingSpecReposMessage +import org.jetbrains.kotlin.gradle.utils.RunProcessResult import java.io.File @DisableCachingByDefault @@ -36,14 +37,14 @@ abstract class PodInstallTask : AbstractPodInstallTask() { @get:Nested abstract val pods: ListProperty - override fun handleError(retCode: Int, error: String, process: Process): String? { + override fun handleError(result: RunProcessResult): String? { val specReposMessages = MissingSpecReposMessage(specRepos.get()).missingMessage val cocoapodsMessages = pods.get().map { MissingCocoapodsMessage(it).missingMessage } return listOfNotNull( - "'pod install' command failed with code $retCode.", + "'pod install' command failed with code ${result.retCode}.", "Error message:", - error.lines().filter { it.isNotBlank() }.joinToString("\n"), + result.stdErr.lines().filter { it.isNotBlank() }.joinToString("\n"), """ | Please, check that podfile contains following lines in header: | $specReposMessages diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt index f85dd3925b4..5bdf379bfc5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ProcessUtils.kt @@ -8,6 +8,21 @@ package org.jetbrains.kotlin.gradle.utils import org.gradle.api.logging.Logger import kotlin.concurrent.thread +/** + * Represents the result of running a process. + * + * @property stdOut The standard output of the process. + * @property stdErr The standard error of the process. + * @property retCode The return code of the process. + * @property process The underlying `Process` object. + */ +data class RunProcessResult( + val stdOut: String, + val stdErr: String, + val retCode: Int, + val process: Process, +) + /** * Executes a command and returns the input text. * @@ -20,15 +35,15 @@ import kotlin.concurrent.thread internal fun runCommand( command: List, logger: Logger? = null, - errorHandler: ((retCode: Int, output: String, process: Process) -> String?)? = null, + errorHandler: ((result: RunProcessResult) -> String?)? = null, processConfiguration: ProcessBuilder.() -> Unit = { }, ): String { val runResult = assembleAndRunProcess(command, logger, processConfiguration) check(runResult.retCode == 0) { - errorHandler?.invoke(runResult.retCode, runResult.output, runResult.process) ?: createErrorMessage(command, runResult) + errorHandler?.invoke(runResult) ?: createErrorMessage(command, runResult) } - return runResult.inputText + return runResult.stdOut } /** @@ -51,29 +66,20 @@ sealed class CommandFallback { internal fun runCommandWithFallback( command: List, logger: Logger? = null, - fallback: (retCode: Int, output: String, process: Process) -> CommandFallback, + fallback: (result: RunProcessResult) -> CommandFallback, processConfiguration: ProcessBuilder.() -> Unit = { }, ): String { val runResult = assembleAndRunProcess(command, logger, processConfiguration) return if (runResult.retCode != 0) { - when (val fallbackOption = fallback(runResult.retCode, runResult.output, runResult.process)) { + when (val fallbackOption = fallback(runResult)) { is CommandFallback.Action -> fallbackOption.fallback is CommandFallback.Error -> error(fallbackOption.error ?: createErrorMessage(command, runResult)) } } else { - runResult.inputText + runResult.stdOut } } -private data class RunProcessResult( - val inputText: String, - val errorText: String, - val retCode: Int, - val process: Process, -) { - val output: String get() = inputText.ifBlank { errorText } -} - private fun assembleAndRunProcess( command: List, logger: Logger? = null, @@ -118,9 +124,9 @@ private fun createErrorMessage(command: List, runResult: RunProcessResul return """ |Executing of '${command.joinToString(" ")}' failed with code ${runResult.retCode} and message: | - |${runResult.inputText} + |${runResult.stdOut} | - |${runResult.errorText} + |${runResult.stdErr} | """.trimMargin() } \ No newline at end of file