diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 6c9b2edbdc2..1e8701606f3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.Project +import org.gradle.api.file.RegularFile import org.gradle.api.provider.Provider import org.gradle.util.GradleVersion import org.jetbrains.kotlin.compilerRunner.KotlinCompilerArgumentsLogLevel @@ -492,6 +493,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val suppressExperimentalArtifactsDslWarning: Boolean get() = booleanProperty(KOTLIN_NATIVE_SUPPRESS_EXPERIMENTAL_ARTIFACTS_DSL_WARNING) ?: false + val cocoapodsExecutablePath: RegularFile? + get() = property(PropertyNames.KOTLIN_APPLE_COCOAPODS_EXECUTABLE).orNull?.let { RegularFile { File(it) } } + /** * Allows the user to specify a custom location for the Kotlin/Native distribution. * This property takes precedence over the 'KONAN_DATA_DIR' environment variable. @@ -652,6 +656,7 @@ internal class PropertiesProvider private constructor(private val project: Proje val KOTLIN_PROJECT_PERSISTENT_DIR_GRADLE_DISABLE_WRITE = property("kotlin.project.persistent.dir.gradle.disableWrite") val KOTLIN_NATIVE_TOOLCHAIN_ENABLED = property("kotlin.native.toolchain.enabled") val KOTLIN_APPLE_COPY_FRAMEWORK_TO_BUILT_PRODUCTS_DIR = property("kotlin.apple.copyFrameworkToBuiltProductsDir") + val KOTLIN_APPLE_COCOAPODS_EXECUTABLE = property("kotlin.apple.cocoapods.bin") /** * Internal properties: builds get big non-suppressible warning when such properties are used 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 fbd340cfffe..c869f6260a1 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 @@ -490,6 +490,8 @@ open class KotlinCocoapodsPlugin : Plugin { project.registerTask(POD_INSTALL_TASK_NAME) { task -> task.group = TASK_GROUP task.description = "Invokes `pod install` call within Podfile location directory" + task.podExecutablePath + task.podExecutablePath.set(project.provider { project.kotlinPropertiesProvider.cocoapodsExecutablePath }) task.podfile.set(project.provider { cocoapodsExtension.podfile }) @Suppress("DEPRECATION") // is set for compatibility reasons task.podspec.set(podspecTaskProvider.map { it.outputFile }) @@ -536,6 +538,7 @@ open class KotlinCocoapodsPlugin : Plugin { project.registerTask(family.toPodInstallSyntheticTaskName) { task -> task.description = "Invokes `pod install` for synthetic project" + task.podExecutablePath.set(project.provider { project.kotlinPropertiesProvider.cocoapodsExecutablePath }) task.podfile.set(podGenTask.map { it.podfile.get() }) task.family.set(family) task.podName.set(cocoapodsExtension.name) 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 c8f1d09f8ff..03803d251d1 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 @@ -7,13 +7,14 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks +import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property 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.* import org.jetbrains.kotlin.gradle.utils.onlyIfCompat +import org.jetbrains.kotlin.gradle.utils.runCommand import org.jetbrains.kotlin.gradle.utils.runCommandWithFallback import java.io.File @@ -33,6 +34,11 @@ abstract class AbstractPodInstallTask : CocoapodsTask() { @get:InputFile abstract val podfile: Property + @get:Optional + @get:PathSensitive(PathSensitivity.ABSOLUTE) + @get:InputFile + abstract val podExecutablePath: RegularFileProperty + @get:Internal protected val workingDir: Provider = podfile.map { file: File? -> requireNotNull(file) { "Task outputs shouldn't be queried if it's skipped" }.parentFile @@ -55,11 +61,30 @@ abstract class AbstractPodInstallTask : CocoapodsTask() { } } + private fun podExecutable(): String { + return when (val podPath = podExecutablePath.orNull?.asFile) { + is File -> podPath.absolutePath.ifBlank { runWhichPod() } + else -> runWhichPod() + } + } + + private fun runWhichPod(): String { + val checkPodCommand = listOf("which", "pod") + val output = runCommand(checkPodCommand, logger, { result -> + if (result.retCode == 1) { + missingPodsError() + } else { + sharedHandleError(checkPodCommand, result) + } + }) + + return output.removingTrailingNewline().ifBlank { + throw IllegalStateException(missingPodsError()) + } + } + private fun runPodInstall(updateRepo: Boolean): String { - // env is used here to work around the JVM PATH caching when spawning a child process with custom environment, i.e. LC_ALL - // The caching causes the ProcessBuilder to ignore changes in the PATH that may occur on incremental runs of the Gradle daemon - // KT-60394 - val podInstallCommand = listOfNotNull("env", "pod", "install", if (updateRepo) "--repo-update" else null) + val podInstallCommand = listOfNotNull(podExecutable(), "install", if (updateRepo) "--repo-update" else null) return runCommandWithFallback(podInstallCommand, logger, @@ -98,7 +123,6 @@ abstract class AbstractPodInstallTask : CocoapodsTask() { | 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 | @@ -118,5 +142,22 @@ abstract class AbstractPodInstallTask : CocoapodsTask() { } } + private fun missingPodsError(): String { + return """ + | ERROR: CocoaPods executable not found in your PATH. + | Please make sure CocoaPods is installed on your system. + | + | You can install CocoaPods using the following command: + | ${'$'} sudo gem install cocoapods + | + | If CocoaPods is already installed and not in your PATH, you can define the + | CocoaPods executable path in the local.properties file by executing the following: + | ${'$'} echo -e "kotlin.apple.cocoapods.bin=${'$'}(which pod)" >> local.properties + | + | For more information, refer to the documentation: https://kotl.in/hxxwtk + | + """.trimMargin() + } + 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/utils/stringUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt index 42c89853a6c..4164f03bb19 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt @@ -60,3 +60,5 @@ internal fun Appendable.appendLine(value: Any?): Appendable = internal fun Appendable.appendLine(): Appendable = append('\n') + +internal fun String.removingTrailingNewline(): String = this.dropLastWhile { it == '\n' } \ No newline at end of file