Cocoapods plugin: error reporting in case CocoaPods is not installed on users machine
#KT-47362
This commit is contained in:
committed by
Space
parent
1b81018b69
commit
d670743a2f
-7
@@ -254,13 +254,6 @@ open class CocoapodsExtension(private val project: Project) {
|
|||||||
pods.all { pod ->
|
pods.all { pod ->
|
||||||
binary.linkerOpts("-framework", pod.moduleName)
|
binary.linkerOpts("-framework", pod.moduleName)
|
||||||
|
|
||||||
check(KotlinCocoapodsPlugin.isAvailableToProduceSynthetic) {
|
|
||||||
"""
|
|
||||||
Dependency on pods requires cocoapods-generate plugin to be installed.
|
|
||||||
Please install it by executing 'gem install cocoapods-generate' in terminal.
|
|
||||||
""".trimMargin()
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.linkTaskProvider.configure { task ->
|
binary.linkTaskProvider.configure { task ->
|
||||||
|
|
||||||
val podBuildTaskProvider = project.getPodBuildTaskProvider(binary.target, pod)
|
val podBuildTaskProvider = project.getPodBuildTaskProvider(binary.target, pod)
|
||||||
|
|||||||
-12
@@ -299,13 +299,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
|||||||
// to avoid showing it in the `tasks` output.
|
// to avoid showing it in the `tasks` output.
|
||||||
}
|
}
|
||||||
|
|
||||||
check(isAvailableToProduceSynthetic) {
|
|
||||||
"""
|
|
||||||
Dependency on pods requires cocoapods-generate plugin to be installed.
|
|
||||||
Please install it by executing 'gem install cocoapods-generate' in terminal.
|
|
||||||
""".trimMargin()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinExtension.supportedTargets().all { target ->
|
kotlinExtension.supportedTargets().all { target ->
|
||||||
target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops.create(pod.moduleName) { interop ->
|
target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops.create(pod.moduleName) { interop ->
|
||||||
|
|
||||||
@@ -395,7 +388,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
|||||||
it.description = "Invokes `pod install` call within Podfile location directory"
|
it.description = "Invokes `pod install` call within Podfile location directory"
|
||||||
it.podfile.set(cocoapodsExtension.podfile)
|
it.podfile.set(cocoapodsExtension.podfile)
|
||||||
it.frameworkName = project.provider { cocoapodsExtension.frameworkNameInternal }
|
it.frameworkName = project.provider { cocoapodsExtension.frameworkNameInternal }
|
||||||
it.onlyIf { isAvailableToProduceSynthetic }
|
|
||||||
it.dependsOn(podspecTaskProvider)
|
it.dependsOn(podspecTaskProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +441,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
|||||||
it.family = family
|
it.family = family
|
||||||
it.pods.set(cocoapodsExtension.pods)
|
it.pods.set(cocoapodsExtension.pods)
|
||||||
it.dependsOn(downloadPods)
|
it.dependsOn(downloadPods)
|
||||||
it.onlyIf { isAvailableToProduceSynthetic }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -487,7 +478,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
|||||||
it.podsXcodeProjDir = podGenTaskProvider.map { podGen -> podGen.podsXcodeProjDir.get() }
|
it.podsXcodeProjDir = podGenTaskProvider.map { podGen -> podGen.podsXcodeProjDir.get() }
|
||||||
it.frameworkName = project.provider { cocoapodsExtension.frameworkNameInternal }
|
it.frameworkName = project.provider { cocoapodsExtension.frameworkNameInternal }
|
||||||
it.dependsOn(podGenTaskProvider)
|
it.dependsOn(podGenTaskProvider)
|
||||||
it.onlyIf { isAvailableToProduceSynthetic }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,7 +518,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
|||||||
it.pod = project.provider { pod }
|
it.pod = project.provider { pod }
|
||||||
it.podsXcodeProjDir = podSetupBuildTaskProvider.map { task -> task.podsXcodeProjDir.get() }
|
it.podsXcodeProjDir = podSetupBuildTaskProvider.map { task -> task.podsXcodeProjDir.get() }
|
||||||
it.buildSettingsFile = podSetupBuildTaskProvider.map { task -> task.buildSettingsFile.get() }
|
it.buildSettingsFile = podSetupBuildTaskProvider.map { task -> task.buildSettingsFile.get() }
|
||||||
it.onlyIf { isAvailableToProduceSynthetic }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,7 +532,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
|||||||
it.group = TASK_GROUP
|
it.group = TASK_GROUP
|
||||||
it.description = "Called on Gradle sync, depends on Cinterop tasks for every used pod"
|
it.description = "Called on Gradle sync, depends on Cinterop tasks for every used pod"
|
||||||
it.dependsOn(podInstallTaskProvider)
|
it.dependsOn(podInstallTaskProvider)
|
||||||
it.onlyIf { isAvailableToProduceSynthetic }
|
|
||||||
|
|
||||||
kotlinExtension.supportedTargets().all { target ->
|
kotlinExtension.supportedTargets().all { target ->
|
||||||
target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops.all { interop ->
|
target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops.all { interop ->
|
||||||
|
|||||||
+131
-60
@@ -11,6 +11,7 @@ import org.gradle.api.file.FileCollection
|
|||||||
import org.gradle.api.file.FileTree
|
import org.gradle.api.file.FileTree
|
||||||
import org.gradle.api.file.RelativePath
|
import org.gradle.api.file.RelativePath
|
||||||
import org.gradle.api.logging.Logger
|
import org.gradle.api.logging.Logger
|
||||||
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
import org.gradle.api.tasks.Optional
|
import org.gradle.api.tasks.Optional
|
||||||
@@ -23,6 +24,7 @@ import org.jetbrains.kotlin.gradle.tasks.PodspecTask.Companion.retrievePods
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.PodspecTask.Companion.retrieveSpecRepos
|
import org.jetbrains.kotlin.gradle.tasks.PodspecTask.Companion.retrieveSpecRepos
|
||||||
import org.jetbrains.kotlin.konan.target.Family
|
import org.jetbrains.kotlin.konan.target.Family
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -64,41 +66,23 @@ open class PodInstallTask : DefaultTask() {
|
|||||||
project.provider { it.parentFile.resolve("Pods").resolve("Pods.xcodeproj") }
|
project.provider { it.parentFile.resolve("Pods").resolve("Pods.xcodeproj") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun doPodInstall() {
|
fun doPodInstall() {
|
||||||
podfile.orNull?.parentFile?.also { podfileDir ->
|
podfile.orNull?.parentFile?.also { podfileDir ->
|
||||||
val podInstallProcess = ProcessBuilder("pod", "install").apply {
|
val podInstallCommand = listOf("pod", "install")
|
||||||
directory(podfileDir)
|
|
||||||
}.start()
|
|
||||||
val podInstallRetCode = podInstallProcess.waitFor()
|
|
||||||
val podInstallOutput = podInstallProcess.inputStream.use { it.reader().readText() }
|
|
||||||
|
|
||||||
check(podInstallRetCode == 0) {
|
runCommand(podInstallCommand,
|
||||||
val specReposMessages = retrieveSpecRepos(project)?.let { MissingSpecReposMessage(it).missingMessage }
|
project.logger,
|
||||||
val cocoapodsMessages = retrievePods(project)?.map { MissingCocoapodsMessage(it, project).missingMessage }
|
errorHandler = { returnCode, output, _ ->
|
||||||
|
CocoapodsErrorHandlingUtil.handlePodInstallError(returnCode, output, project, frameworkName.get())
|
||||||
|
},
|
||||||
|
exceptionHandler = { e: IOException ->
|
||||||
|
CocoapodsErrorHandlingUtil.handle(e, podInstallCommand)
|
||||||
|
},
|
||||||
|
processConfiguration = {
|
||||||
|
directory(podfileDir)
|
||||||
|
})
|
||||||
|
|
||||||
listOfNotNull(
|
|
||||||
"Executing of 'pod install' failed with code $podInstallRetCode.",
|
|
||||||
"Error message:",
|
|
||||||
podInstallOutput,
|
|
||||||
specReposMessages?.let {
|
|
||||||
"""
|
|
||||||
|Please, check that file "${podfile.get().path}" contains following lines in header:
|
|
||||||
|$it
|
|
||||||
|
|
|
||||||
""".trimMargin()
|
|
||||||
},
|
|
||||||
cocoapodsMessages?.let {
|
|
||||||
"""
|
|
||||||
|Please, check that each target depended on ${frameworkName.get()} contains following dependencies:
|
|
||||||
|${it.joinToString("\n")}
|
|
||||||
|
|
|
||||||
""".trimMargin()
|
|
||||||
}
|
|
||||||
|
|
||||||
).joinToString("\n")
|
|
||||||
}
|
|
||||||
with(podsXcodeProjDirProvider) {
|
with(podsXcodeProjDirProvider) {
|
||||||
check(this != null && get().exists() && get().isDirectory) {
|
check(this != null && get().exists() && get().isDirectory) {
|
||||||
"The directory 'Pods/Pods.xcodeproj' was not created as a result of the `pod install` call."
|
"The directory 'Pods/Pods.xcodeproj' was not created as a result of the `pod install` call."
|
||||||
@@ -108,11 +92,6 @@ open class PodInstallTask : DefaultTask() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ExtendedErrorDiagnostic {
|
|
||||||
fun isAppropriateOutput(output: String): Boolean
|
|
||||||
val message: String
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DownloadCocoapodsTask : DefaultTask() {
|
abstract class DownloadCocoapodsTask : DefaultTask() {
|
||||||
@get:Input
|
@get:Input
|
||||||
internal lateinit var podName: Provider<String>
|
internal lateinit var podName: Provider<String>
|
||||||
@@ -303,14 +282,23 @@ open class PodDownloadGitTask : DownloadCocoapodsTask() {
|
|||||||
private fun runCommand(
|
private fun runCommand(
|
||||||
command: List<String>,
|
command: List<String>,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
extendedErrorDiagnostic: ExtendedErrorDiagnostic? = null,
|
errorHandler: ((retCode: Int, output: String, process: Process) -> String?)? = null,
|
||||||
errorHandler: ((retCode: Int, process: Process) -> Unit)? = null,
|
exceptionHandler: ((ex: IOException) -> Unit)? = null,
|
||||||
processConfiguration: ProcessBuilder.() -> Unit = { }
|
processConfiguration: ProcessBuilder.() -> Unit = { }
|
||||||
): String {
|
): String {
|
||||||
val process = ProcessBuilder(command)
|
var process: Process? = null
|
||||||
.apply {
|
try {
|
||||||
this.processConfiguration()
|
process = ProcessBuilder(command)
|
||||||
}.start()
|
.apply {
|
||||||
|
this.processConfiguration()
|
||||||
|
}.start()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
if (exceptionHandler != null) exceptionHandler(e) else throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process == null) {
|
||||||
|
throw IllegalStateException("Failed to run command ${command.joinToString(" ")}")
|
||||||
|
}
|
||||||
|
|
||||||
var inputText = ""
|
var inputText = ""
|
||||||
var errorText = ""
|
var errorText = ""
|
||||||
@@ -340,12 +328,14 @@ private fun runCommand(
|
|||||||
)
|
)
|
||||||
|
|
||||||
check(retCode == 0) {
|
check(retCode == 0) {
|
||||||
errorHandler?.invoke(retCode, process)
|
errorHandler?.invoke(retCode, inputText, process)
|
||||||
?: """
|
?: """
|
||||||
|Executing of '${command.joinToString(" ")}' failed with code $retCode and message:
|
|Executing of '${command.joinToString(" ")}' failed with code $retCode and message:
|
||||||
|
|
|
|
||||||
|
|$inputText
|
||||||
|
|
|
||||||
|$errorText
|
|$errorText
|
||||||
|${extendedErrorDiagnostic?.takeIf { it.isAppropriateOutput(inputText) }?.message ?: ""}
|
|
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,23 +396,18 @@ open class PodGenTask : DefaultTask() {
|
|||||||
podspec.get().absolutePath
|
podspec.get().absolutePath
|
||||||
)
|
)
|
||||||
|
|
||||||
val podGenDiagnostic = object : ExtendedErrorDiagnostic {
|
runCommand(
|
||||||
override fun isAppropriateOutput(output: String): Boolean =
|
podGenProcessArgs,
|
||||||
output.contains("deployment target") || output.contains("requested platforms: [\"${family.platformLiteral}\"]")
|
project.logger,
|
||||||
|
exceptionHandler = { e: IOException ->
|
||||||
override val message: String
|
CocoapodsErrorHandlingUtil.handle(e, podGenProcessArgs)
|
||||||
get() =
|
},
|
||||||
"""
|
errorHandler = { retCode, output, _ ->
|
||||||
|Tip: try to configure deployment_target for ALL targets as follows:
|
CocoapodsErrorHandlingUtil.handlePodGenError(retCode, output, family)
|
||||||
|cocoapods {
|
},
|
||||||
| ...
|
processConfiguration = {
|
||||||
| ${family.name.toLowerCase()}.deploymentTarget = "..."
|
directory(syntheticDir)
|
||||||
| ...
|
})
|
||||||
|}
|
|
||||||
""".trimMargin()
|
|
||||||
}
|
|
||||||
|
|
||||||
runCommand(podGenProcessArgs, project.logger, podGenDiagnostic) { directory(syntheticDir) }
|
|
||||||
|
|
||||||
val podsXcprojFile = podsXcodeProjDir.get()
|
val podsXcprojFile = podsXcodeProjDir.get()
|
||||||
check(podsXcprojFile.exists() && podsXcprojFile.isDirectory) {
|
check(podsXcprojFile.exists() && podsXcprojFile.isDirectory) {
|
||||||
@@ -596,3 +581,89 @@ data class PodBuildSettingsProperties(
|
|||||||
getProperty(propertyName)
|
getProperty(propertyName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object CocoapodsErrorHandlingUtil {
|
||||||
|
fun handle(e: IOException, command: List<String>) {
|
||||||
|
if (e.message?.contains("No such file or directory") == true) {
|
||||||
|
val message = """
|
||||||
|
|'${command.take(2).joinToString(" ")}' command failed with an exception:
|
||||||
|
| ${e.message}
|
||||||
|
|
|
||||||
|
| Possible reason: CocoaPods is not installed
|
||||||
|
| Please check that CocoaPods v1.10 or above and cocoapods-generate plugin are installed.
|
||||||
|
|
|
||||||
|
| To check CocoaPods version type 'pod --version' in the terminal
|
||||||
|
|
|
||||||
|
| To install CocoaPods execute 'sudo gem install cocoapods'
|
||||||
|
| To install cocoapod-generate execute 'sudo gem install cocoapods-generate'
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
throw IllegalStateException(message)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handlePodInstallError(retCode: Int, error: String, project: Project, frameworkName: String): String {
|
||||||
|
val specReposMessages = retrieveSpecRepos(project)?.let { MissingSpecReposMessage(it).missingMessage }
|
||||||
|
val cocoapodsMessages = retrievePods(project)?.map { MissingCocoapodsMessage(it, project).missingMessage }
|
||||||
|
|
||||||
|
return listOfNotNull(
|
||||||
|
"'pod install' command failed with code $retCode.",
|
||||||
|
"Error message:",
|
||||||
|
error,
|
||||||
|
specReposMessages?.let {
|
||||||
|
"""
|
||||||
|
| Please, check that podfile contains following lines in header:
|
||||||
|
| $it
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
},
|
||||||
|
cocoapodsMessages?.let {
|
||||||
|
"""
|
||||||
|
| Please, check that each target depended on $frameworkName contains following dependencies:
|
||||||
|
| ${it.joinToString("\n")}
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
}
|
||||||
|
|
||||||
|
).joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handlePodGenError(retCode: Int, error: String, family: Family): String? {
|
||||||
|
|
||||||
|
var message = """
|
||||||
|
|'pod gen' command failed with return code: $retCode
|
||||||
|
|
|
||||||
|
| Error: ${error.lines().filter { it.contains("[!]") }?.joinToString("\n")}
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
if (error.contains("Unknown command: `gen`")) {
|
||||||
|
message += """
|
||||||
|
|
|
||||||
|
| Possible reason: cocoapod-generate is not installed
|
||||||
|
| To install cocoapod-generate execute 'sudo gem install cocoapods-generate' in the terminal
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
return message
|
||||||
|
} else if (
|
||||||
|
error.contains("deployment target") ||
|
||||||
|
error.contains("requested platforms:") ||
|
||||||
|
error.contains("no platform was specified")
|
||||||
|
) {
|
||||||
|
message += """
|
||||||
|
|
|
||||||
|
| Possible reason: ${family.name.toLowerCase()} deployment target is not configured
|
||||||
|
| Configure deployment_target for ALL targets as follows:
|
||||||
|
| cocoapods {
|
||||||
|
| ...
|
||||||
|
| ${family.name.toLowerCase()}.deploymentTarget = "..."
|
||||||
|
| ...
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user