[Gradle, Cocoapods] refactor I/O, improve Up-To-Date

all synthetic tasks are per pod
tasks receive parts of extension as inputs
This commit is contained in:
Andrey.Lozhkin
2020-08-28 19:33:36 +03:00
parent 1888462dc0
commit ef57c62576
4 changed files with 637 additions and 229 deletions
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.gradle
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_BUILD_DEPENDENCIES_TASK_NAME
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_BUILD_TASK_NAME
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_DOWNLOAD_TASK_NAME
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_GEN_TASK_NAME
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_IMPORT_TASK_NAME
@@ -16,10 +16,8 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Compan
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_SPEC_TASK_NAME
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import java.io.File
import java.util.concurrent.TimeUnit
@@ -46,7 +44,7 @@ class CocoaPodsIT : BaseGradleIT() {
private val podspecTaskName = ":$POD_SPEC_TASK_NAME"
private val podDownloadTaskName = ":$POD_DOWNLOAD_TASK_NAME"
private val podGenTaskName = ":$POD_GEN_TASK_NAME"
private val podBuildTaskName = ":$POD_BUILD_DEPENDENCIES_TASK_NAME"
private val podBuildTaskName = ":$POD_BUILD_TASK_NAME"
private val podSetupBuildTaskName = ":$POD_SETUP_BUILD_TASK_NAME"
private val podImportTaskName = ":$POD_IMPORT_TASK_NAME"
private val podInstallTaskName = ":$POD_INSTALL_TASK_NAME"
@@ -54,14 +52,30 @@ class CocoaPodsIT : BaseGradleIT() {
private val defaultPodRepo = "https://github.com/AFNetworking/AFNetworking"
private val defaultPodName = "AFNetworking"
private val downloadUrlPodName = "podspecWithFilesExample"
private val downloadUrlRepoName = "https://github.com/alozhkin/podspecWithFilesExample/raw/master"
private val defaultTarget = "IOS"
private val defaultFamily = "IOS"
private val defaultSDK = "iphonesimulator"
private val defaultPodDownloadTaskName = podDownloadTaskName + defaultPodName
private val defaultPodGenTaskName = podGenTaskName + defaultFamily
private val defaultBuildTaskName = podBuildTaskName + defaultSDK.capitalize()
private val defaultSetupBuildTaskName = podSetupBuildTaskName + defaultSDK.capitalize()
private val defaultPodDownloadTaskName = podDownloadFullTaskName()
private val defaultPodGenTaskName = podGenFullTaskName()
private val defaultBuildTaskName = podBuildFullTaskName()
private val defaultSetupBuildTaskName = podSetupBuildFullTaskName()
private val defaultCinteropTaskName = cinteropTaskName + defaultPodName + defaultTarget
private val downloadUrlTaskName = podDownloadTaskName + downloadUrlPodName.capitalize()
private fun podDownloadFullTaskName(podName: String = defaultPodName) = podDownloadTaskName + podName.capitalize()
private fun podGenFullTaskName(familyName: String = defaultFamily) = podGenTaskName + familyName.capitalize()
private fun podSetupBuildFullTaskName(podName: String = defaultPodName, sdkName: String = defaultSDK) =
podSetupBuildTaskName + podName.capitalize() + sdkName.capitalize()
private fun podBuildFullTaskName(podName: String = defaultPodName, sdkName: String = defaultSDK) =
podBuildTaskName + podName.capitalize() + sdkName.capitalize()
private fun cinteropFullTaskName(podName: String = defaultPodName, targetName: String = defaultTarget) =
cinteropTaskName + podName.capitalize() + targetName.capitalize()
private lateinit var hooks: CustomHooks
private lateinit var project: BaseGradleIT.Project
@@ -118,10 +132,10 @@ class CocoaPodsIT : BaseGradleIT() {
)
@Test
fun testPodImportUseFrameworksSingle() = doTestImportSingle(ImportMode.FRAMEWORKS)
fun testPodImportSingle() = doTestImportSingle()
@Test
fun testPodImportUseModularHeadersSingle() = doTestImportSingle(ImportMode.MODULAR_HEADERS)
fun testPodImportMultiple() = doTestImportMultiple()
@Test
fun testPodspecMultiple() = doTestPodspec(
@@ -173,20 +187,14 @@ class CocoaPodsIT : BaseGradleIT() {
)
@Test
fun testPodImportUseFrameworksMultiple() = doTestImportMultiple(ImportMode.FRAMEWORKS)
@Test
fun testPodImportUseModularHeadersMultiple() = doTestImportMultiple(ImportMode.MODULAR_HEADERS)
@Test
fun testSpecReposDownloads() {
fun testSpecReposImport() {
val podName = "example"
val podRepo = "https://github.com/alozhkin/spec_repo_example"
val podRepo = "https://github.com/alozhkin/spec_repo"
with(project.gradleBuildScript()) {
addPod(podName)
addSpecRepo(podRepo)
}
project.testDownload(listOf(podRepo))
project.testImportWithAsserts(listOf(podRepo))
}
@Test
@@ -275,19 +283,6 @@ class CocoaPodsIT : BaseGradleIT() {
project.test(":kotlin-library:podDownload")
}
@Ignore
@Test
fun testSubspecWithModuleName() {
with(project.gradleBuildScript()) {
addPod("AFNetworking/Reachability", "moduleName = \"CustomName\"")
addPod("AFNetworking/Security")
}
hooks.addHook {
assertTasksExecuted(defaultCinteropTaskName, cinteropTaskName + "AFNetworking/Security" + defaultTarget)
}
project.testImportWithAsserts()
}
// up-to-date tests
@@ -355,6 +350,282 @@ class CocoaPodsIT : BaseGradleIT() {
project.testSynthetic(defaultPodGenTaskName)
}
@Test
fun testUTDPodAdded() {
with(project.gradleBuildScript()) {
addPod(defaultPodName, produceGitBlock(defaultPodRepo))
}
project.testImport(listOf(defaultPodRepo))
val anotherPodName = "Alamofire"
val anotherPodRepo = "https://github.com/Alamofire/Alamofire"
with(project.gradleBuildScript()) {
addPod(anotherPodName, produceGitBlock(anotherPodRepo))
}
hooks.rewriteHooks {
assertTasksExecuted(
podspecTaskName,
podDownloadFullTaskName(anotherPodName),
defaultPodGenTaskName,
podSetupBuildFullTaskName(anotherPodName),
podBuildFullTaskName(anotherPodName),
cinteropFullTaskName(anotherPodName)
)
assertTasksUpToDate(
defaultPodDownloadTaskName,
defaultSetupBuildTaskName,
defaultBuildTaskName,
defaultCinteropTaskName
)
}
project.testImport(listOf(defaultPodRepo, anotherPodRepo))
with(project.gradleBuildScript()) {
removePod(anotherPodName)
}
hooks.rewriteHooks {
assertTasksNotRegisteredByPrefix(
listOf(
podDownloadFullTaskName(anotherPodName),
podBuildFullTaskName(anotherPodName),
cinteropFullTaskName(anotherPodName)
)
)
assertTasksUpToDate(
defaultBuildTaskName,
defaultPodDownloadTaskName,
defaultSetupBuildTaskName,
defaultCinteropTaskName
)
}
project.testImport(listOf(defaultPodRepo))
}
@Test
fun testImportSubspecs() {
with(project.gradleBuildScript()) {
addPod("SDWebImage/Core")
addPod("SDWebImage/MapKit")
}
project.testImport(listOf(defaultPodRepo))
}
@Test
fun testUTDTargetAdded() {
with(project.gradleBuildScript()) {
addPod(defaultPodName, produceGitBlock(defaultPodRepo))
appendToCocoapodsBlock("osx.deploymentTarget = \"13.5\"")
}
project.testImport(listOf(defaultPodRepo))
val anotherTarget = "MacosX64"
val anotherSdk = "macosx"
val anotherFamily = "OSX"
with(project.gradleBuildScript()) {
appendToKotlinBlock(anotherTarget.decapitalize() + "()")
}
hooks.rewriteHooks {
assertTasksExecuted(
podGenFullTaskName(anotherFamily),
podSetupBuildFullTaskName(sdkName = anotherSdk),
podBuildFullTaskName(sdkName = anotherSdk),
cinteropFullTaskName(targetName = anotherTarget)
)
assertTasksUpToDate(
podspecTaskName,
defaultPodDownloadTaskName,
defaultPodGenTaskName,
defaultSetupBuildTaskName,
defaultBuildTaskName,
defaultCinteropTaskName
)
}
project.testImport(listOf(defaultPodRepo))
with(project.gradleBuildScript()) {
var text = readText()
text = text.replace(anotherTarget.decapitalize() + "()", "")
writeText(text)
}
hooks.rewriteHooks {
assertTasksNotRegisteredByPrefix(
listOf(
podGenFullTaskName(anotherFamily),
podSetupBuildFullTaskName(sdkName = anotherSdk),
podBuildFullTaskName(sdkName = anotherSdk),
cinteropFullTaskName(targetName = anotherTarget)
)
)
assertTasksUpToDate(
podspecTaskName,
defaultPodDownloadTaskName,
defaultPodGenTaskName,
defaultSetupBuildTaskName,
defaultBuildTaskName,
defaultCinteropTaskName
)
}
project.testImport(listOf(defaultPodRepo))
}
@Test
fun testUTDPodspec() {
project.testWithWrapper(podspecTaskName)
hooks.addHook {
assertTasksExecuted(podspecTaskName)
}
with(project.gradleBuildScript()) {
appendToCocoapodsBlock("license = \"new license name\"")
}
project.testWithWrapper(podspecTaskName)
with(project.gradleBuildScript()) {
appendToCocoapodsBlock("license = \"new license name\"")
}
hooks.rewriteHooks {
assertTasksUpToDate(podspecTaskName)
}
project.testWithWrapper(podspecTaskName)
}
@Test
fun testUTDPodspecDeploymentTarget() {
project.testWithWrapper(podspecTaskName)
hooks.addHook {
assertTasksExecuted(podspecTaskName)
}
with(project.gradleBuildScript()) {
appendToCocoapodsBlock("ios.deploymentTarget = \"12.5\"")
}
project.testWithWrapper(podspecTaskName)
hooks.rewriteHooks {
assertTasksUpToDate(podspecTaskName)
}
project.testWithWrapper(podspecTaskName)
}
@Test
fun testUTDDownload() {
val gitRepo = downloadUrlRepoName.substringBeforeLast("/").substringBeforeLast("/")
with(project.gradleBuildScript()) {
addPod(downloadUrlPodName, produceGitBlock(repo = gitRepo))
}
hooks.addHook {
assertTasksExecuted(downloadUrlTaskName)
}
project.testDownload(listOf(gitRepo))
with(project.gradleBuildScript()) {
changePod(downloadUrlPodName, produceGitBlock(repo = gitRepo, commitName = "f52f035018b4f3fe253d50ce85a7e0652a62ee9b"))
}
project.testDownload(listOf(gitRepo))
hooks.rewriteHooks {
assertTasksUpToDate(downloadUrlTaskName)
}
project.testDownload(listOf(gitRepo))
val podArchivePath = "$downloadUrlRepoName/$downloadUrlPodName.tar.gz"
with(project.gradleBuildScript()) {
changePod(downloadUrlPodName, "source = url(\"$podArchivePath\")")
}
hooks.rewriteHooks {
assertTasksExecuted(downloadUrlTaskName)
}
project.testDownload(listOf(podArchivePath))
}
@Test
fun testUTDPodGen() {
with(project.gradleBuildScript()) {
addPod(defaultPodName)
}
val repos = listOf(
"https://github.com/alozhkin/spec_repo_example",
"https://github.com/alozhkin/spec_repo_example_2"
)
for (repo in repos) {
assumeTrue(isRepoAvailable(repo))
}
hooks.addHook {
assertTasksExecuted(defaultPodGenTaskName)
}
project.testSynthetic(defaultPodGenTaskName)
with(project.gradleBuildScript()) {
addSpecRepo("https://github.com/alozhkin/spec_repo_example")
}
project.testSynthetic(defaultPodGenTaskName)
with(project.gradleBuildScript()) {
addSpecRepo("https://github.com/alozhkin/spec_repo_example_2")
}
project.testSynthetic(defaultPodGenTaskName)
hooks.rewriteHooks {
assertTasksUpToDate(defaultPodGenTaskName)
}
project.testSynthetic(defaultPodGenTaskName)
}
@Test
fun testUTDBuild() {
with(project.gradleBuildScript()) {
addPod(defaultPodName, produceGitBlock())
}
hooks.addHook {
assertTasksExecuted(defaultBuildTaskName)
}
project.testImport()
val repo = "$downloadUrlRepoName/$downloadUrlPodName.tar.gz"
with(project.gradleBuildScript()) {
addPod(downloadUrlPodName, "source = url(\"$repo\")")
}
val urlTaskName = podBuildFullTaskName(downloadUrlPodName)
hooks.rewriteHooks {
assertTasksUpToDate(defaultBuildTaskName)
assertTasksExecuted(urlTaskName)
}
project.testImport()
val anotherTarget = "MacosX64"
val anotherSdk = "macosx"
with(project.gradleBuildScript()) {
appendToCocoapodsBlock("osx.deploymentTarget = \"13.5\"")
appendToKotlinBlock(anotherTarget.decapitalize() + "()")
}
val anotherSdkDefaultPodTaskName = podBuildFullTaskName(sdkName = anotherSdk)
val anotherTargetUrlTaskName = podBuildFullTaskName(downloadUrlPodName, anotherSdk)
hooks.rewriteHooks {
assertTasksUpToDate(defaultBuildTaskName, urlTaskName)
assertTasksExecuted(anotherSdkDefaultPodTaskName, anotherTargetUrlTaskName)
}
project.testImport()
hooks.rewriteHooks {
assertTasksUpToDate(defaultBuildTaskName, urlTaskName, anotherSdkDefaultPodTaskName, anotherTargetUrlTaskName)
}
project.testImport()
}
@Test
fun testPodBuildUTDClean() {
with(project.gradleBuildScript()) {
addPod(defaultPodName, produceGitBlock())
}
hooks.addHook {
assertTasksExecuted(defaultBuildTaskName)
}
project.testImport()
hooks.rewriteHooks {}
project.test(":clean")
hooks.addHook {
assertTasksExecuted(defaultBuildTaskName)
}
project.testImport()
}
@Test
fun testPodInstallWithoutPodFile() {
project.testSynthetic(podInstallTaskName)
}
// groovy tests
@@ -395,6 +666,13 @@ class CocoaPodsIT : BaseGradleIT() {
project.testImportWithAsserts(listOf(repo))
}
@Test
fun supportPodsWithDependencies() {
with(project.gradleBuildScript()) {
addPod("AlamofireImage")
}
project.testImportWithAsserts()
}
// paths
@@ -408,7 +686,7 @@ class CocoaPodsIT : BaseGradleIT() {
// test configuration phase
private class CustomHooks() {
private class CustomHooks {
private val hooks = mutableSetOf<CompiledProject.() -> Unit>()
fun addHook(hook: CompiledProject.() -> Unit) {
@@ -427,21 +705,21 @@ class CocoaPodsIT : BaseGradleIT() {
}
}
private fun doTestImportSingle(importMode: ImportMode) {
private fun doTestImportSingle() {
val project = getProjectByName(cocoapodsSingleKtPod)
val subprojects = listOf("kotlin-library")
doTestPodImport(project, subprojects, importMode)
doTestPodImport(project, subprojects)
}
private fun doTestImportMultiple(importMode: ImportMode) {
private fun doTestImportMultiple() {
val project = getProjectByName(cocoapodsMultipleKtPods)
val subprojects = listOf("kotlin-library", "second-library")
doTestPodImport(project, subprojects, importMode)
doTestPodImport(project, subprojects)
}
private fun doTestPodImport(project: BaseGradleIT.Project, subprojects: List<String>, importMode: ImportMode) {
private fun doTestPodImport(project: BaseGradleIT.Project, subprojects: List<String>) {
with(project) {
preparePodfile("ios-app", importMode)
preparePodfile("ios-app", ImportMode.FRAMEWORKS)
}
project.testImportWithAsserts()
subprojects.forEach {
@@ -468,10 +746,10 @@ class CocoaPodsIT : BaseGradleIT() {
project.testDownload(listOf(repo))
}
fun doTestPodDownloadUrl(
private fun doTestPodDownloadUrl(
fileExtension: String,
podName: String = "podspecWithFilesExample",
repoPath: String = "https://github.com/alozhkin/podspecWithFilesExample/raw/master",
podName: String = downloadUrlPodName,
repoPath: String = downloadUrlRepoName,
archiveName: String = podName,
flatten: Boolean = false
) {
@@ -554,6 +832,30 @@ class CocoaPodsIT : BaseGradleIT() {
appendToCocoapodsBlock(podBlock)
}
private fun File.removePod(podName: String) {
val text = readText()
val begin = text.indexOf("""pod("$podName")""")
require(begin != -1) { "Pod doesn't exist in file" }
var index = begin + """pod("$podName")""".length - 1
if (text.indexOf("""pod("$podName") {""", startIndex = begin) != -1) {
index += 2
var bracket = 1
while (bracket != 0) {
if (text[++index] == '{') {
bracket++
} else if (text[index] == '}') {
bracket--
}
}
}
writeText(text.removeRange(begin..index))
}
private fun File.changePod(podName: String, newConfiguration: String? = null) {
removePod(podName)
addPod(podName, newConfiguration)
}
private fun File.addSpecRepo(specRepo: String) = appendToCocoapodsBlock("url(\"$specRepo\")".wrap("specRepos"))
private fun File.appendToKotlinBlock(str: String) = appendLine(str.wrap("kotlin"))
@@ -691,7 +993,7 @@ class CocoaPodsIT : BaseGradleIT() {
assertTasksExecutedByPrefix(listOf("$taskPrefix:$POD_GEN_TASK_NAME"))
}
with(listOf(POD_SETUP_BUILD_TASK_NAME, POD_BUILD_DEPENDENCIES_TASK_NAME).map { "$taskPrefix:$it" }) {
with(listOf(POD_SETUP_BUILD_TASK_NAME, POD_BUILD_TASK_NAME).map { "$taskPrefix:$it" }) {
if (buildScriptText.matches("pod\\(.*\\)".toRegex())) {
assertTasksRegisteredByPrefix(this)
assertTasksExecutedByPrefix(this)
@@ -11,6 +11,7 @@ import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.daemon.common.trimQuotes
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.KonanTarget.*
import java.io.File
import java.util.*
internal val Project.cocoapodsBuildDirs: CocoapodsBuildDirs
get() = CocoapodsBuildDirs(this)
@@ -68,17 +70,17 @@ private val Family.toPodGenTaskName: String
name
)
private val String.toSetupBuildTaskName: String
get() = lowerCamelCaseName(
KotlinCocoapodsPlugin.POD_SETUP_BUILD_TASK_NAME,
this
)
private fun String.toSetupBuildTaskName(pod: CocoapodsDependency): String = lowerCamelCaseName(
KotlinCocoapodsPlugin.POD_SETUP_BUILD_TASK_NAME,
pod.schemeName,
this
)
private val String.toBuildDependenciesTaskName: String
get() = lowerCamelCaseName(
KotlinCocoapodsPlugin.POD_BUILD_DEPENDENCIES_TASK_NAME,
this
)
private fun String.toBuildDependenciesTaskName(pod: CocoapodsDependency): String = lowerCamelCaseName(
KotlinCocoapodsPlugin.POD_BUILD_TASK_NAME,
pod.schemeName,
this
)
private val CocoapodsDependency.toPodDownloadTaskName: String
get() = lowerCamelCaseName(
@@ -225,7 +227,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
val defTask = project.registerTask<DefFileTask>(
lowerCamelCaseName("generateDef", pod.moduleName).asValidTaskName()
) {
it.pod = pod
it.pod = project.provider { pod }
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.
@@ -248,8 +250,8 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
&& project.findProperty(CONFIGURATION_PROPERTY) == null
) {
val podBuildTaskProvider =
project.tasks.named(target.toValidSDK.toBuildDependenciesTaskName, PodBuildTask::class.java)
interopTask.inputs.file(podBuildTaskProvider.get().buildSettingsFileProvider)
project.tasks.named(target.toValidSDK.toBuildDependenciesTaskName(pod), PodBuildTask::class.java)
interopTask.inputs.file(podBuildTaskProvider.get().buildSettingsFile)
interopTask.dependsOn(podBuildTaskProvider)
}
@@ -275,9 +277,9 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
&& project.findProperty(CONFIGURATION_PROPERTY) == null
) {
val podBuildTaskProvider =
project.tasks.named(target.toValidSDK.toBuildDependenciesTaskName, PodBuildTask::class.java)
project.tasks.named(target.toValidSDK.toBuildDependenciesTaskName(pod), PodBuildTask::class.java)
val buildSettings =
podBuildTaskProvider.get().buildSettingsFileProvider.get()
podBuildTaskProvider.get().buildSettingsFile.get()
.inputStream()
.use {
PodBuildSettingsProperties.readSettingsFromStream(it)
@@ -291,9 +293,14 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
buildSettings.headerPaths?.let { args ->
interop.compilerOpts.addAll(args.splitQuotedArgs().map { "-I$it" })
}
buildSettings.frameworkPaths?.let { args ->
interop.compilerOpts.addAll(args.splitQuotedArgs().map { "-F$it" })
}
val frameworkPaths = buildSettings.frameworkPaths
val configurationBuildDir = buildSettings.configurationBuildDir
val frameworkPathsSelfIncluding = mutableListOf<String>()
frameworkPathsSelfIncluding += configurationBuildDir.trimQuotes()
frameworkPaths?.let { frameworkPathsSelfIncluding.addAll(it.splitQuotedArgs()) }
interop.compilerOpts.addAll(frameworkPathsSelfIncluding.map { "-F$it" })
}
}
}
@@ -306,7 +313,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
cocoapodsExtension: CocoapodsExtension
) {
project.tasks.register(DUMMY_FRAMEWORK_TASK_NAME, DummyFrameworkTask::class.java) {
it.cocoapodsExtension = cocoapodsExtension
it.frameworkName = project.provider { cocoapodsExtension.frameworkName }
}
}
@@ -319,7 +326,18 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
project.tasks.register(POD_SPEC_TASK_NAME, PodspecTask::class.java) {
it.group = TASK_GROUP
it.description = "Generates a podspec file for CocoaPods import"
it.cocoapodsExtension = cocoapodsExtension
it.needPodspec = project.provider { cocoapodsExtension.needPodspec }
it.pods.set(cocoapodsExtension.pods)
it.version = project.provider { cocoapodsExtension.version }
it.homepage.set(cocoapodsExtension.homepage)
it.license.set(cocoapodsExtension.license)
it.authors.set(cocoapodsExtension.authors)
it.summary.set(cocoapodsExtension.summary)
it.frameworkName = project.provider { cocoapodsExtension.frameworkName }
it.ios = project.provider { cocoapodsExtension.ios }
it.osx = project.provider { cocoapodsExtension.osx }
it.tvos = project.provider { cocoapodsExtension.tvos }
it.watchos = project.provider { cocoapodsExtension.watchos }
it.dependsOn(dummyFrameworkTaskProvider)
val generateWrapper = project.findProperty(GENERATE_WRAPPER_PROPERTY)?.toString()?.toBoolean() ?: false
if (generateWrapper) {
@@ -335,13 +353,13 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
project.tasks.register(POD_INSTALL_TASK_NAME, PodInstallTask::class.java) {
it.group = TASK_GROUP
it.description = "Invokes `pod install` call within Podfile location directory"
it.cocoapodsExtension = cocoapodsExtension
it.podfile.set(cocoapodsExtension.podfile)
it.onlyIf { isAvailableToProduceSynthetic }
//TODO avoid subproject task management here
project.allprojects.map { it.tasks.named(POD_SPEC_TASK_NAME, PodspecTask::class.java) }
.forEach { podspecTaskProvider ->
podspecTaskProvider.get().takeIf { task -> task.cocoapodsExtension.needPodspec }
podspecTaskProvider.get().takeIf { task -> task.needPodspec.get() }
?.also { task -> it.inputs.file(task.outputFileProvider) }
it.dependsOn(podspecTaskProvider)
}
@@ -361,12 +379,10 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
val podSource = pod.source
val downloadPodTask = when (podSource) {
is Git -> project.tasks.register(pod.toPodDownloadTaskName, PodDownloadGitTask::class.java) {
it.cocoapodsExtension = cocoapodsExtension
it.podName = project.provider { pod.name }
it.podSource = project.provider { podSource as Git }
}
is Url -> project.tasks.register(pod.toPodDownloadTaskName, PodDownloadUrlTask::class.java) {
it.cocoapodsExtension = cocoapodsExtension
it.podName = project.provider { pod.name }
it.podSource = project.provider { podSource as Url }
}
@@ -394,10 +410,10 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
project.tasks.register(family.toPodGenTaskName, PodGenTask::class.java) {
it.description = "Сreates a synthetic Xcode project to retrieve CocoaPods dependencies"
it.podspecProvider = podspecTaskProvider.get().outputFileProvider
it.podspec = podspecTaskProvider.map { task -> task.outputFileProvider.get() }
it.specRepos = project.provider { cocoapodsExtension.specRepos }
it.family = family
it.cocoapodsExtension = cocoapodsExtension
it.dependsOn(podspecTaskProvider)
it.pods.set(cocoapodsExtension.pods)
it.dependsOn(downloadPods)
it.onlyIf { isAvailableToProduceSynthetic }
}
@@ -409,30 +425,36 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
kotlinExtension: KotlinMultiplatformExtension,
cocoapodsExtension: CocoapodsExtension
) {
val sdks = mutableSetOf<String>()
val schemeNames = mutableSetOf<String>()
kotlinExtension.supportedTargets().all { target ->
cocoapodsExtension.pods.all { pod ->
val sdk = target.toValidSDK
if (sdk in sdks) {
if (schemeNames.contains(pod.schemeName)) {
return@all
}
sdks += sdk
schemeNames.add(pod.schemeName)
val podGenTaskProvider = project.tasks.named(target.konanTarget.family.toPodGenTaskName, PodGenTask::class.java)
val sdks = mutableSetOf<String>()
project.tasks.register(sdk.toSetupBuildTaskName, PodSetupBuildTask::class.java) {
it.group = TASK_GROUP
it.description = "Collect environment variables from .xcworkspace file"
it.cocoapodsExtension = cocoapodsExtension
it.sdk = project.provider { sdk }
kotlinExtension.supportedTargets().all loop@{ target ->
val podspecTaskProvider = project.tasks.named(POD_SPEC_TASK_NAME, PodspecTask::class.java)
it.schemeName = podspecTaskProvider.map { it.specName }
val sdk = target.toValidSDK
if (sdk in sdks) {
return@loop
}
sdks += sdk
it.podsXcodeProjDirProvider = podGenTaskProvider.get().podsXcodeProjDirProvider
it.dependsOn(podGenTaskProvider)
it.onlyIf { isAvailableToProduceSynthetic }
val podGenTaskProvider = project.tasks.named(target.konanTarget.family.toPodGenTaskName, PodGenTask::class.java)
project.tasks.register(sdk.toSetupBuildTaskName(pod), PodSetupBuildTask::class.java) {
it.group = TASK_GROUP
it.description = "Collect environment variables from .xcworkspace file"
it.pod = project.provider { pod }
it.sdk = project.provider { sdk }
it.podsXcodeProjDir = podGenTaskProvider.map { podGen -> podGen.podsXcodeProjDir.get() }
it.frameworkName = project.provider { cocoapodsExtension.frameworkName }
it.dependsOn(podGenTaskProvider)
it.onlyIf { isAvailableToProduceSynthetic }
}
}
}
}
@@ -442,27 +464,38 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
kotlinExtension: KotlinMultiplatformExtension,
cocoapodsExtension: CocoapodsExtension
) {
val sdks = mutableSetOf<String>()
val schemeNames = mutableSetOf<String>()
kotlinExtension.supportedTargets().all { target ->
cocoapodsExtension.pods.all { pod ->
val sdk = target.toValidSDK
if (sdk in sdks) {
if (schemeNames.contains(pod.schemeName)) {
return@all
}
sdks += sdk
schemeNames.add(pod.schemeName)
val podSetupBuildTaskProvider = project.tasks.named(sdk.toSetupBuildTaskName, PodSetupBuildTask::class.java)
val sdks = mutableSetOf<String>()
project.tasks.register(sdk.toBuildDependenciesTaskName, PodBuildTask::class.java) {
it.group = TASK_GROUP
it.description = "Calls `xcodebuild` on xcworkspace for the pod scheme"
it.sdk = project.provider { sdk }
it.cocoapodsExtension = cocoapodsExtension
it.podsXcodeProjDirProvider = podSetupBuildTaskProvider.get().podsXcodeProjDirProvider
it.buildSettingsFileProvider = podSetupBuildTaskProvider.get().buildSettingsFileProvider
it.dependsOn(podSetupBuildTaskProvider)
it.onlyIf { isAvailableToProduceSynthetic }
kotlinExtension.supportedTargets().all loop@{ target ->
val sdk = target.toValidSDK
if (sdk in sdks) {
return@loop
}
sdks += sdk
val podSetupBuildTaskProvider =
project.tasks.named(sdk.toSetupBuildTaskName(pod), PodSetupBuildTask::class.java)
project.tasks.register(sdk.toBuildDependenciesTaskName(pod), PodBuildTask::class.java) {
it.group = TASK_GROUP
it.description = "Calls `xcodebuild` on xcworkspace for the pod scheme"
it.sdk = project.provider { sdk }
it.pod = project.provider { pod }
it.podsXcodeProjDir = podSetupBuildTaskProvider.map { task -> task.podsXcodeProjDir.get() }
it.buildSettingsFile = podSetupBuildTaskProvider.map { task -> task.buildSettingsFile.get() }
it.onlyIf { isAvailableToProduceSynthetic }
}
}
}
}
@@ -471,7 +504,6 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
project: Project,
kotlinExtension: KotlinMultiplatformExtension
) {
val podInstallTaskProvider = project.tasks.named(POD_INSTALL_TASK_NAME, PodInstallTask::class.java)
project.tasks.register(POD_IMPORT_TASK_NAME) {
it.group = TASK_GROUP
@@ -531,7 +563,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
const val POD_DOWNLOAD_TASK_NAME = "podDownload"
const val POD_GEN_TASK_NAME = "podGen"
const val POD_SETUP_BUILD_TASK_NAME = "podSetupBuild"
const val POD_BUILD_DEPENDENCIES_TASK_NAME = "podBuildDependencies"
const val POD_BUILD_TASK_NAME = "podBuild"
const val POD_IMPORT_TASK_NAME = "podImport"
// We don't move these properties in PropertiesProvider because
@@ -7,15 +7,14 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.file.RelativePath
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.api.tasks.Optional
import org.jetbrains.kotlin.gradle.plugin.cocoapods.*
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.*
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.*
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension
import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName
import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs
import org.jetbrains.kotlin.konan.target.Family
import java.io.File
@@ -34,6 +33,9 @@ private val Family.platformLiteral: String
else -> throw IllegalArgumentException("Bad family ${this.name}")
}
val CocoapodsDependency.schemeName: String
get() = name.split("/")[0]
/**
* The task takes the path to the Podfile and calls `pod install`
* to obtain sources or artifacts for the declared dependencies.
@@ -41,24 +43,24 @@ private val Family.platformLiteral: String
*/
open class PodInstallTask : DefaultTask() {
init {
onlyIf { cocoapodsExtension?.podfile != null }
onlyIf { podfile.isPresent }
}
@get:Optional
@get:Nested
internal var cocoapodsExtension: CocoapodsExtension? = null
@get:Input
internal val podfile = project.objects.property(File::class.java)
@get:Optional
@get:OutputDirectory
internal val podsXcodeProjDirProvider: Provider<File>?
get() = cocoapodsExtension?.podfile?.let {
get() = podfile.orNull?.let {
project.provider { it.parentFile.resolve("Pods").resolve("Pods.xcodeproj") }
}
@TaskAction
fun doPodInstall() {
cocoapodsExtension?.podfile?.parentFile?.also { podfileDir ->
podfile.orNull?.parentFile?.also { podfileDir ->
val podInstallProcess = ProcessBuilder("pod", "install").apply {
directory(podfileDir)
}.start()
@@ -81,25 +83,12 @@ open class PodInstallTask : DefaultTask() {
}
}
abstract class CocoapodsWithSyntheticTask : DefaultTask() {
init {
onlyIf {
cocoapodsExtension.pods.isNotEmpty()
}
}
@get:Nested
internal lateinit var cocoapodsExtension: CocoapodsExtension
}
abstract class PodDownloadTask : CocoapodsWithSyntheticTask() {
abstract class DownloadCocoapodsTask : DefaultTask() {
@get:Input
internal lateinit var podName: Provider<String>
}
open class PodDownloadUrlTask : PodDownloadTask() {
open class PodDownloadUrlTask : DownloadCocoapodsTask() {
@get:Nested
internal lateinit var podSource: Provider<Url>
@@ -181,8 +170,7 @@ open class PodDownloadUrlTask : PodDownloadTask() {
}
}
open class PodDownloadGitTask : PodDownloadTask() {
open class PodDownloadGitTask : DownloadCocoapodsTask() {
@get:Nested
internal lateinit var podSource: Provider<Git>
@@ -328,19 +316,31 @@ private fun runCommand(
* The task takes the path to the .podspec file and calls `pod gen`
* to create synthetic xcode project and workspace.
*/
open class PodGenTask : CocoapodsWithSyntheticTask() {
open class PodGenTask : DefaultTask() {
init {
onlyIf {
pods.get().isNotEmpty()
}
}
@get:InputFile
internal lateinit var podspecProvider: Provider<File>
internal lateinit var podspec: Provider<File>
@get:Internal
lateinit var family: Family
@get:Nested
internal lateinit var specRepos: Provider<SpecRepos>
@get:Nested
val pods = project.objects.listProperty(CocoapodsDependency::class.java)
@get:OutputDirectory
internal val podsXcodeProjDirProvider: Provider<File>
internal val podsXcodeProjDir: Provider<File>
get() = project.provider {
project.cocoapodsBuildDirs.synthetic(family)
.resolve(podspecProvider.get().nameWithoutExtension)
.resolve(podspec.get().nameWithoutExtension)
.resolve("Pods")
.resolve("Pods.xcodeproj")
}
@@ -348,18 +348,18 @@ open class PodGenTask : CocoapodsWithSyntheticTask() {
@TaskAction
fun generate() {
val syntheticDir = project.cocoapodsBuildDirs.synthetic(family).apply { mkdirs() }
val localPodspecPaths = cocoapodsExtension.pods.mapNotNull { it.source?.getLocalPath(project, it.name) }
val localPodspecPaths = pods.get().mapNotNull { it.source?.getLocalPath(project, it.name) }
val sources = cocoapodsExtension.specRepos.getAll().toMutableList()
sources += URI("https://cdn.cocoapods.org")
val specRepos = specRepos.get().getAll().toMutableList()
specRepos += URI("https://cdn.cocoapods.org")
val podGenProcessArgs = listOfNotNull(
"pod", "gen",
"--platforms=${family.platformLiteral}",
"--gen-directory=${syntheticDir.absolutePath}",
localPodspecPaths.takeIf { it.isNotEmpty() }?.joinToString(separator = ",")?.let { "--local-sources=$it" },
sources.takeIf { it.isNotEmpty() }?.joinToString(separator = ",")?.let { "--sources=$it" },
podspecProvider.get().absolutePath
specRepos.takeIf { it.isNotEmpty() }?.joinToString(separator = ",")?.let { "--sources=$it" },
podspec.get().absolutePath
)
val podGenProcess = ProcessBuilder(podGenProcessArgs).apply {
@@ -388,7 +388,7 @@ open class PodGenTask : CocoapodsWithSyntheticTask() {
).joinToString("\n")
}
val podsXcprojFile = podsXcodeProjDirProvider.get()
val podsXcprojFile = podsXcodeProjDir.get()
check(podsXcprojFile.exists() && podsXcprojFile.isDirectory) {
"The directory '${podsXcprojFile.path}' was not created as a result of the `pod gen` call."
}
@@ -396,35 +396,35 @@ open class PodGenTask : CocoapodsWithSyntheticTask() {
}
open class PodSetupBuildTask : CocoapodsWithSyntheticTask() {
open class PodSetupBuildTask : DefaultTask() {
@get:InputDirectory
internal lateinit var podsXcodeProjDirProvider: Provider<File>
@get:Input
lateinit var frameworkName: Provider<String>
@get:Input
internal lateinit var sdk: Provider<String>
@Input
lateinit var schemeName: Provider<String>
@get:Nested
lateinit var pod: Provider<CocoapodsDependency>
@get:OutputFile
internal val buildSettingsFileProvider: Provider<File> = project.provider {
internal val buildSettingsFile: Provider<File> = project.provider {
project.cocoapodsBuildDirs
.buildSettings
.resolve(sdk.get().toBuildSettingsFileName)
.resolve(getBuildSettingFileName(pod.get(), sdk.get()))
}
private val String.toBuildSettingsFileName: String
get() = "build-settings-$this.properties"
@get:Internal
internal lateinit var podsXcodeProjDir: Provider<File>
@TaskAction
fun setupBuild() {
val podsXcodeProjDir = podsXcodeProjDirProvider.get()
val podsXcodeProjDir = podsXcodeProjDir.get()
val buildSettingsReceivingCommand = listOf(
"xcodebuild", "-showBuildSettings",
"-project", podsXcodeProjDir.name,
"-scheme", schemeName.get(),
"-scheme", pod.get().schemeName,
"-sdk", sdk.get()
)
@@ -442,113 +442,151 @@ open class PodSetupBuildTask : CocoapodsWithSyntheticTask() {
}
val stdOut = buildSettingsProcess.inputStream
val buildSettingsProperties = PodBuildSettingsProperties.readSettingsFromStream(stdOut)
buildSettingsFileProvider.get().let { buildSettingsProperties.writeSettings(it) }
buildSettingsFile.get().let {
buildSettingsProperties.writeSettings(it)
}
}
}
private fun getBuildSettingFileName(pod: CocoapodsDependency, sdk: String): String =
"build-settings-$sdk-${pod.schemeName}.properties"
/**
* The task compiles external cocoa pods sources.
*/
open class PodBuildTask : CocoapodsWithSyntheticTask() {
@get:InputDirectory
internal lateinit var podsXcodeProjDirProvider: Provider<File>
open class PodBuildTask : DefaultTask() {
@get:InputFile
internal lateinit var buildSettingsFileProvider: Provider<File>
internal lateinit var buildSettingsFile: Provider<File>
@get:Nested
internal lateinit var pod: Provider<CocoapodsDependency>
@get:InputFiles
internal val srcDir: FileTree
get() = project.fileTree(
buildSettingsFile.map {
PodBuildSettingsProperties.readSettingsFromStream(
FileInputStream(it)
).podsTargetSrcRoot
}
)
@get:Internal
internal var buildDir: Provider<File> = project.provider {
project.file(
PodBuildSettingsProperties.readSettingsFromStream(
FileInputStream(buildSettingsFile.get())
).buildDir
)
}
@get:Input
internal lateinit var sdk: Provider<String>
@get:Optional
@get:OutputDirectory
internal var buildDirProvider: Provider<File>? = null
@get:OutputFiles
internal val buildResult: Provider<FileCollection>? = project.provider {
project.fileTree(buildDir.get()) {
it.include("**/${pod.get().schemeName}.*/")
it.include("**/${pod.get().schemeName}/")
}
}
private val CocoapodsExtension.CocoapodsDependency.schemeName: String
get() = name.split("/")[0]
@get:Internal
internal lateinit var podsXcodeProjDir: Provider<File>
@TaskAction
fun buildDependencies() {
val podBuildSettings = PodBuildSettingsProperties.readSettingsFromStream(
FileInputStream(buildSettingsFileProvider.get())
FileInputStream(buildSettingsFile.get())
)
val podsXcodeProjDir = podsXcodeProjDirProvider.get()
val podsXcodeProjDir = podsXcodeProjDir.get()
cocoapodsExtension.pods.all {
val podXcodeBuildCommand = listOf(
"xcodebuild",
"-project", podsXcodeProjDir.name,
"-scheme", pod.get().schemeName,
"-sdk", sdk.get(),
"-configuration", podBuildSettings.configuration
)
val podXcodeBuildCommand = listOf(
"xcodebuild",
"-project", podsXcodeProjDir.name,
"-scheme", it.schemeName,
"-sdk", sdk.get(),
"-configuration", podBuildSettings.configuration
)
val podBuildProcess = ProcessBuilder(podXcodeBuildCommand)
.apply {
directory(podsXcodeProjDir.parentFile)
inheritIO()
}.start()
val podBuildProcess = ProcessBuilder(podXcodeBuildCommand)
.apply {
directory(podsXcodeProjDir.parentFile)
inheritIO()
}.start()
val podBuildRetCode = podBuildProcess.waitFor()
check(podBuildRetCode == 0) {
listOf(
"Executing of '${podXcodeBuildCommand.joinToString(" ")}' failed with code $podBuildRetCode and message:",
podBuildProcess.errorStream.use { it.reader().readText() }
).joinToString("\n")
}
val podBuildRetCode = podBuildProcess.waitFor()
check(podBuildRetCode == 0) {
listOf(
"Executing of '${podXcodeBuildCommand.joinToString(" ")}' failed with code $podBuildRetCode and message:",
podBuildProcess.errorStream.use { it.reader().readText() }
).joinToString("\n")
}
buildDirProvider = project.provider { project.file(podBuildSettings.buildDir) }
}
}
internal data class PodBuildSettingsProperties(
internal val buildDir: String,
internal val configuration: String,
internal val configurationBuildDir: String,
internal val podsTargetSrcRoot: String,
internal val cflags: String? = null,
internal val headerPaths: String? = null,
internal val frameworkPaths: String? = null
) {
fun writeSettings(buildSettingsFile: File) {
fun writeSettings(
buildSettingsFile: File
) {
buildSettingsFile.parentFile.mkdirs()
buildSettingsFile.delete()
buildSettingsFile.createNewFile()
check(buildSettingsFile.exists()) { "Unable to create file ${buildSettingsFile.path}!" }
with(Properties()) {
setProperty(BUILD_DIR, buildDir)
setProperty(CONFIGURATION, configuration)
cflags?.let { setProperty(OTHER_CFLAGS, it) }
headerPaths?.let { setProperty(HEADER_SEARCH_PATHS, it) }
frameworkPaths?.let { setProperty(FRAMEWORK_SEARCH_PATHS, it) }
buildSettingsFile.outputStream().use {
store(it, null)
}
with(buildSettingsFile) {
appendText("$BUILD_DIR=$buildDir\n")
appendText("$CONFIGURATION=$configuration\n")
appendText("$CONFIGURATION_BUILD_DIR=$configurationBuildDir\n")
appendText("$PODS_TARGET_SRCROOT=$podsTargetSrcRoot\n")
cflags?.let { appendText("$OTHER_CFLAGS=$it\n") }
headerPaths?.let { appendText("$HEADER_SEARCH_PATHS=$it\n") }
frameworkPaths?.let { appendText("$FRAMEWORK_SEARCH_PATHS=$it") }
}
}
companion object {
const val BUILD_DIR: String = "BUILD_DIR"
const val CONFIGURATION: String = "CONFIGURATION"
const val OTHER_CFLAGS: String = "OTHER_CFLAGS"
const val HEADER_SEARCH_PATHS: String = "HEADER_SEARCH_PATHS"
const val FRAMEWORK_SEARCH_PATHS: String = "FRAMEWORK_SEARCH_PATHS"
const val BUILD_DIR = "BUILD_DIR"
const val CONFIGURATION = "CONFIGURATION"
const val CONFIGURATION_BUILD_DIR = "CONFIGURATION_BUILD_DIR"
const val PODS_TARGET_SRCROOT = "PODS_TARGET_SRCROOT"
const val OTHER_CFLAGS = "OTHER_CFLAGS"
const val HEADER_SEARCH_PATHS = "HEADER_SEARCH_PATHS"
const val FRAMEWORK_SEARCH_PATHS = "FRAMEWORK_SEARCH_PATHS"
fun readSettingsFromStream(inputStream: InputStream): PodBuildSettingsProperties {
with(Properties()) {
load(inputStream)
return PodBuildSettingsProperties(
getProperty(BUILD_DIR),
getProperty(CONFIGURATION),
getProperty(OTHER_CFLAGS),
getProperty(HEADER_SEARCH_PATHS),
getProperty(FRAMEWORK_SEARCH_PATHS)
readProperty(BUILD_DIR),
readProperty(CONFIGURATION),
readProperty(CONFIGURATION_BUILD_DIR),
readProperty(PODS_TARGET_SRCROOT),
readNullableProperty(OTHER_CFLAGS),
readNullableProperty(HEADER_SEARCH_PATHS),
readNullableProperty(FRAMEWORK_SEARCH_PATHS)
)
}
}
private fun Properties.readProperty(propertyName: String) =
readNullableProperty(propertyName) ?: error("$propertyName property is absent")
private fun Properties.readNullableProperty(propertyName: String) =
getProperty(propertyName)
}
}
@@ -15,6 +15,7 @@ import org.gradle.api.tasks.wrapper.Wrapper
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.*
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.COCOAPODS_EXTENSION_NAME
import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.GENERATE_WRAPPER_PROPERTY
@@ -38,18 +39,55 @@ open class PodspecTask : DefaultTask() {
internal val outputFileProvider: Provider<File>
get() = project.provider { project.file("$specName.podspec") }
@get:Input
internal lateinit var needPodspec: Provider<Boolean>
@get:Nested
internal lateinit var cocoapodsExtension: CocoapodsExtension
val pods = project.objects.listProperty(CocoapodsDependency::class.java)
@get:Input
internal lateinit var version: Provider<String>
@get:Input
@get:Optional
internal val homepage = project.objects.property(String::class.java)
@get:Input
@get:Optional
internal val license = project.objects.property(String::class.java)
@get:Input
@get:Optional
internal val authors = project.objects.property(String::class.java)
@get:Input
@get:Optional
internal val summary = project.objects.property(String::class.java)
@get:Input
internal lateinit var frameworkName: Provider<String>
@get:Nested
internal lateinit var ios: Provider<PodspecPlatformSettings>
@get:Nested
internal lateinit var osx: Provider<PodspecPlatformSettings>
@get:Nested
internal lateinit var tvos: Provider<PodspecPlatformSettings>
@get:Nested
internal lateinit var watchos: Provider<PodspecPlatformSettings>
init {
onlyIf { cocoapodsExtension.needPodspec }
onlyIf { needPodspec.get() }
}
@TaskAction
fun generate() {
val frameworkDir = project.cocoapodsBuildDirs.framework.relativeTo(outputFileProvider.get().parentFile).path
val dependencies = cocoapodsExtension.pods.map { pod ->
val dependencies = pods.get().map { pod ->
val versionSuffix = if (pod.version != null) ", '${pod.version}'" else ""
"| spec.dependency '${pod.name}'$versionSuffix"
}.joinToString(separator = "\n")
@@ -69,10 +107,8 @@ open class PodspecTask : DefaultTask() {
val syncTask = "${project.path}:$SYNC_TASK_NAME"
val deploymentTargets = run {
with(cocoapodsExtension) {
listOf(ios, osx, tvos, watchos).filter { it.deploymentTarget != null }.joinToString("\n") {
"| spec.${it.name}.deployment_target = '${it.deploymentTarget}'"
}
listOf(ios, osx, tvos, watchos).map { it.get() }.filter { it.deploymentTarget != null }.joinToString("\n") {
"| spec.${it.name}.deployment_target = '${it.deploymentTarget}'"
}
}
@@ -81,15 +117,15 @@ open class PodspecTask : DefaultTask() {
"""
|Pod::Spec.new do |spec|
| spec.name = '$specName'
| spec.version = '${cocoapodsExtension.version}'
| spec.homepage = '${cocoapodsExtension.homepage.orEmpty()}'
| spec.version = '${version.get()}'
| spec.homepage = '${homepage.getOrEmpty()}'
| spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
| spec.authors = '${cocoapodsExtension.authors.orEmpty()}'
| spec.license = '${cocoapodsExtension.license.orEmpty()}'
| spec.summary = '${cocoapodsExtension.summary.orEmpty()}'
| spec.authors = '${authors.getOrEmpty()}'
| spec.license = '${license.getOrEmpty()}'
| spec.summary = '${summary.getOrEmpty()}'
|
| spec.static_framework = true
| spec.vendored_frameworks = "$frameworkDir/${cocoapodsExtension.frameworkName}.framework"
| spec.vendored_frameworks = "$frameworkDir/${frameworkName.get()}.framework"
| spec.libraries = "c++"
| spec.module_name = "#{spec.name}_umbrella"
|
@@ -143,6 +179,8 @@ open class PodspecTask : DefaultTask() {
}
}
fun Provider<String>.getOrEmpty() = getOrElse("")
companion object {
private val KotlinMultiplatformExtension?.cocoapodsExtensionOrNull: CocoapodsExtension?
get() = (this as? ExtensionAware)?.extensions?.findByName(COCOAPODS_EXTENSION_NAME) as? CocoapodsExtension
@@ -167,17 +205,15 @@ open class PodspecTask : DefaultTask() {
* and then replace it with the real one during a real build process.
*/
open class DummyFrameworkTask : DefaultTask() {
@OutputDirectory
val destinationDir = project.cocoapodsBuildDirs.framework
@Input
val frameworkNameProvider: Provider<String> = project.provider { cocoapodsExtension.frameworkName }
@get:Nested
internal lateinit var cocoapodsExtension: CocoapodsExtension
lateinit var frameworkName: Provider<String>
private val frameworkDir: File
get() = destinationDir.resolve("${frameworkNameProvider.get()}.framework")
get() = destinationDir.resolve("${frameworkName.get()}.framework")
private fun copyResource(from: String, to: File) {
to.parentFile.mkdirs()
@@ -225,11 +261,11 @@ open class DummyFrameworkTask : DefaultTask() {
// Copy files for the dummy framework.
copyFrameworkFile("Info.plist")
copyFrameworkFile("dummy", frameworkNameProvider.get())
copyFrameworkFile("dummy", frameworkName.get())
copyFrameworkFile("Headers/placeholder.h")
copyFrameworkTextFile("Modules/module.modulemap") {
if (it == "framework module dummy {") {
it.replace("dummy", frameworkNameProvider.get())
it.replace("dummy", frameworkName.get())
} else {
it
}
@@ -242,12 +278,12 @@ open class DummyFrameworkTask : DefaultTask() {
*/
open class DefFileTask : DefaultTask() {
@Nested
lateinit var pod: CocoapodsExtension.CocoapodsDependency
@get:Nested
lateinit var pod: Provider<CocoapodsDependency>
@get:OutputFile
val outputFile: File
get() = project.cocoapodsBuildDirs.defs.resolve("${pod.moduleName}.def")
get() = project.cocoapodsBuildDirs.defs.resolve("${pod.get().moduleName}.def")
@TaskAction
fun generate() {
@@ -255,7 +291,7 @@ open class DefFileTask : DefaultTask() {
outputFile.writeText(
"""
language = Objective-C
modules = ${pod.moduleName}
modules = ${pod.get().moduleName}
""".trimIndent()
)
}