Skip to content
This repository has been archived by the owner on May 27, 2020. It is now read-only.

Commit

Permalink
Lots of stuff
Browse files Browse the repository at this point in the history
 - Adds custom preset
 - Adds test task
 - Fix generate task
  • Loading branch information
AlecKazakova committed Jan 27, 2019
1 parent 47fc340 commit 1fc44ba
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 202 deletions.
18 changes: 1 addition & 17 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
buildscript {
ext.versions = [
kotlin: '1.3.20',
]

ext.deps = [
plugins: [
kotlin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}",
],

kotlin: [
stdlib: [
jdk: "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}",
],
],
junit: 'junit:junit:4.12',
truth: 'com.google.truth:truth:0.42',
]
apply from: "$rootDir/gradle/dependencies.gradle"

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.alecstrong.cocoapods.gradle.plugin

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.konan.target.Architecture
import java.io.File

open class CocoapodsCompileTask : DefaultTask() {
internal lateinit var buildType: NativeBuildType
internal lateinit var compilations: Collection<KotlinNativeLink>

@TaskAction
fun compileFatFramework() {
compileFatBinary(
binaryPath = project.name,
bundleName = "${project.name}.framework"
)
}

@TaskAction
fun compileFatDsym() {
compileFatBinary(
binaryPath = "Contents/Resources/DWARF/${project.name}",
bundleName = "${project.name}.framework.dSYM"
)
}

private fun compileFatBinary(
binaryPath: String,
bundleName: String
) {
val finalContainerPath = "${project.buildDir.path}/$bundleName"
val finalOutputPath = "$finalContainerPath/$binaryPath"

var deviceParentDir: String? = null

project.exec { exec ->
File(finalOutputPath).parentFile.mkdirs()

val args = mutableListOf("-create")

compilations.forEach { compilation ->
val output = compilation.outputFile.get().parentFile.absolutePath
val target = compilation.binary.target.konanTarget
if (target.architecture == Architecture.ARM64) {
deviceParentDir = output
}

args.addAll(listOf(
"-arch", target.architecture(), "$output/$bundleName/$binaryPath"
))
}

args.addAll(listOf(
"-output", finalOutputPath
))

exec.executable = "lipo"
exec.args = args
exec.isIgnoreExitValue = true
}

if (deviceParentDir == null) {
throw IllegalStateException("You need to have a compilation target for X64")
}

val initialContainer = "$deviceParentDir/$bundleName"
project.copy { copy ->
copy.from(initialContainer) { from ->
from.exclude(binaryPath)
}
copy.into(finalContainerPath)
}

// clean plist (only works for frameworks)
val plistPath = "$finalContainerPath/Info.plist"
if (File(plistPath).exists()) {
project.exec { exec ->
exec.executable = "/usr/libexec/PlistBuddy"
exec.args = listOf("-c", "Delete :UIRequiredDeviceCapabilities", plistPath)
exec.isIgnoreExitValue = true
}

project.exec { exec ->
exec.executable = "/usr/libexec/PlistBuddy"
exec.args = listOf("-c", "Add :CFBundleSupportedPlatforms:1 string iPhoneSimulator", plistPath)
exec.isIgnoreExitValue = true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ package com.alecstrong.cocoapods.gradle.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.konan.target.Architecture
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind

open class CocoapodsPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create("cocoapods", CocoapodsExtension::class.java)

project.extensions.add("cocoapodsPreset", CocoapodsTargetPreset(project))

project.afterEvaluate {
project.tasks.register("generatePodspec", GeneratePodspecTask::class.java) { task ->
task.group = GROUP
Expand All @@ -37,154 +31,26 @@ open class CocoapodsPlugin : Plugin<Project> {
task.description = "Create a dummy dynamic framework to be used during Cocoapods installation"
}

createFatFrameworkTasks(project)
}
}

private fun createFatFrameworkTasks(project: Project) {
val mppExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)

mppExtension.targets
.flatMap { target ->
target.compilations
.filterIsInstance<KotlinNativeCompilation>()
.filter { !it.isTestCompilation }
.flatMap { compilation ->
target.components.flatMap { component ->
(component.target as KotlinNativeTarget).binaries.map { binary ->
compilation to binary
}
}
}
}
.filter { (_, binary) ->
binary.target.konanTarget.family == Family.IOS
}
.flatMap { (compilation, binary) ->
compilation.buildTypes.map { buildType ->
Configuration(
buildType = buildType,
linkTask = binary.linkTask
)
}
}
.groupBy { it.buildType }
.forEach { (buildType, configurations) ->
val dsym = project.registerTaskFor(
buildType = buildType,
configurations = configurations,
binaryPath = "Contents/Resources/DWARF/${project.name}",
bundleName = "${project.name}.framework.dSYM",
taskSuffix = "DSYM"
)
val framework = project.registerTaskFor(
buildType = buildType,
configurations = configurations,
binaryPath = project.name,
bundleName = "${project.name}.framework",
taskSuffix = "Framework"
)
project.tasks.register("createIos${buildType.name()}Artifacts") { task ->
task.dependsOn(dsym, framework)
}
}
}

private fun Project.registerTaskFor(
buildType: NativeBuildType,
configurations: Collection<Configuration>,
binaryPath: String,
bundleName: String,
taskSuffix: String
): TaskProvider<Task> {
return tasks.register("createIos${buildType.name()}Fat$taskSuffix") { task ->
task.dependsOn(configurations.map { it.linkTask })

task.doLast {
// put together final path
val finalContainerParentDir = property(POD_FRAMEWORK_DIR_ENV) as String
val finalContainerPath = "$finalContainerParentDir/$bundleName"
val finalOutputPath = "$finalContainerPath/$binaryPath"

var deviceParentDir: String? = null

exec { exec ->
File(finalOutputPath).parentFile.mkdirs()

val args = mutableListOf("-create")

configurations.forEach { (_, linkTask) ->
val output = linkTask.outputFile.get().parentFile.absolutePath
val target = linkTask.compilation.target.konanTarget
if (target.architecture == Architecture.ARM64) {
deviceParentDir = output
val mppExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
mppExtension.targets
.filterIsInstance<KotlinNativeTarget>()
.flatMap { it.binaries }
.filter { it.outputKind == NativeOutputKind.FRAMEWORK }
.map { it.buildType to it.linkTask }
.groupBy({ it.first }, { it.second })
.forEach { (buildType, compilations) ->
project.tasks.register("createIos${buildType.name()}Artifacts",
CocoapodsCompileTask::class.java) { task ->
task.dependsOn(compilations)
task.buildType = buildType
task.compilations = compilations
task.group = GROUP
}

args.addAll(listOf(
"-arch", target.architecture(), "$output/$bundleName/$binaryPath"
))
}

args.addAll(listOf(
"-output", finalOutputPath
))

exec.executable = "lipo"
exec.args = args
exec.isIgnoreExitValue = true
}

if (deviceParentDir == null) {
throw IllegalStateException("You need to have a compilation target for X64")
}

val initialContainer = "$deviceParentDir/$bundleName"
copy { copy ->
copy.from(initialContainer) { from ->
from.exclude(binaryPath)
}
copy.into(finalContainerPath)
}

// clean plist (only works for frameworks)
val plistPath = "$finalContainerPath/Info.plist"
if (File(plistPath).exists()) {
exec { exec ->
exec.executable = "/usr/libexec/PlistBuddy"
exec.args = listOf("-c", "Delete :UIRequiredDeviceCapabilities", plistPath)
exec.isIgnoreExitValue = true
}

exec { exec ->
exec.executable = "/usr/libexec/PlistBuddy"
exec.args = listOf("-c", "Add :CFBundleSupportedPlatforms:1 string iPhoneSimulator", plistPath)
exec.isIgnoreExitValue = true
}
}
}
}
}

private data class Configuration(
val buildType: NativeBuildType,
val linkTask: KotlinNativeLink
)

private fun KonanTarget.architecture() = when (this) {
is KonanTarget.IOS_X64 -> "x86_64"
is KonanTarget.IOS_ARM64 -> "arm64"
is KonanTarget.IOS_ARM32 -> "arm32"
else -> throw IllegalStateException("Cannot collapse non-ios target $this into descriptor.")
}

private fun NativeBuildType.name() = when (this) {
NativeBuildType.RELEASE -> "Release"
NativeBuildType.DEBUG -> "Debug"
}

companion object {
private const val GROUP = "cocoapods"

internal const val POD_FRAMEWORK_DIR_ENV = "POD_FRAMEWORK_DIR"
internal const val GROUP = "cocoapods"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.alecstrong.cocoapods.gradle.plugin

import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind.EXECUTABLE

class CocoapodsTargetPreset(
private val project: Project
) : KotlinTargetPreset<KotlinNativeTarget> {
override fun createTarget(name: String): KotlinNativeTarget {
val extension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
val simulator = (extension.targetFromPreset(
extension.presets.getByName("iosX64"), name
) as KotlinNativeTarget).apply {
binaries {
framework()
}
}

val device = (extension.targetFromPreset(
extension.presets.getByName("iosArm64"), "${name}Device"
) as KotlinNativeTarget).apply {
binaries {
framework {
embedBitcode("disable")
}
}
}

configureSources(name, device.compilations)

project.tasks.register("${name}Test", CocoapodsTestTask::class.java) { task ->
task.dependsOn(simulator.compilations.getByName("test").getLinkTask(EXECUTABLE, DEBUG))
task.group = CocoapodsPlugin.GROUP
task.description = "Run tests for target '$name' on an iOS Simulator"
task.target = simulator
}

return simulator
}

override fun getName() = "Cocoapods"

private fun configureSources(name: String, compilations: NamedDomainObjectContainer<KotlinNativeCompilation>) {
val extension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)

project.afterEvaluate {
compilations.named("main").configure { compilation ->
extension.sourceSets.findByName("${name}Main")?.let(compilation::source)
}
compilations.named("test").configure { compilation ->
extension.sourceSets.findByName("${name}Test")?.let(compilation::source)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.alecstrong.cocoapods.gradle.plugin

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind.EXECUTABLE

open class CocoapodsTestTask : DefaultTask() {
internal lateinit var target: KotlinNativeTarget

@Input var device: String = "iPhone 8"

@TaskAction
fun performTest() {
val binary = target.compilations.getByName("test").getBinary(EXECUTABLE, DEBUG)
project.exec { exec ->
exec.commandLine("xcrun", "simctl", "spawn", device, binary.absolutePath)
}
}
}
Loading

0 comments on commit 1fc44ba

Please sign in to comment.