[Gradle, Native] Implement custom name for generated framework for XCode
This commit is contained in:
+61
-16
@@ -22,7 +22,12 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
val PODFILE_IMPORT_DIRECTIVE_PLACEHOLDER = "<import_mode_directive>"
|
||||
|
||||
@Test
|
||||
fun testPodspec() {
|
||||
fun testPodspec() = doTestPodspec()
|
||||
|
||||
@Test
|
||||
fun testPodspecCustomFrameworkName() = doTestPodspec("MultiPlatformLibrary")
|
||||
|
||||
private fun doTestPodspec(frameworkName: String? = null) {
|
||||
assumeTrue(HostManager.hostIsMac)
|
||||
val gradleProject = transformProjectWithPluginsDsl("new-mpp-cocoapods", gradleVersion)
|
||||
|
||||
@@ -30,8 +35,10 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
gradleProject.build(":kotlin-library:podspec") {
|
||||
assertFailed()
|
||||
assertContains("The Gradle wrapper is required to run the build from Xcode.")
|
||||
assertContains("Please run the same command with `-Pkotlin.native.cocoapods.generate.wrapper=true` " +
|
||||
"or run the `:wrapper` task to generate the wrapper manually.")
|
||||
assertContains(
|
||||
"Please run the same command with `-Pkotlin.native.cocoapods.generate.wrapper=true` " +
|
||||
"or run the `:wrapper` task to generate the wrapper manually."
|
||||
)
|
||||
}
|
||||
|
||||
// Check that we can generate the wrapper along with the podspec if the corresponding property specified
|
||||
@@ -42,6 +49,14 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
|
||||
// Check that the podspec file is correctly generated.
|
||||
val podspecFileName = "kotlin-library/kotlin_library.podspec"
|
||||
|
||||
|
||||
frameworkName?.let {
|
||||
fileInWorkingDir(podspecFileName).modify {
|
||||
it.replace("build/cocoapods/framework/kotlin_library.framework", "build/cocoapods/framework/$frameworkName.framework")
|
||||
}
|
||||
}
|
||||
|
||||
val expectedPodspecContent = """
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = 'kotlin_library'
|
||||
@@ -53,7 +68,7 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
spec.summary = 'CocoaPods test library'
|
||||
|
||||
spec.static_framework = true
|
||||
spec.vendored_frameworks = "build/cocoapods/framework/#{spec.name}.framework"
|
||||
spec.vendored_frameworks = "build/cocoapods/framework/${frameworkName ?: "kotlin_library"}.framework"
|
||||
spec.libraries = "c++"
|
||||
spec.module_name = "#{spec.name}_umbrella"
|
||||
|
||||
@@ -159,11 +174,34 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
CommandResult(process.exitValue(), stdOut, stdErr).block()
|
||||
}
|
||||
|
||||
private fun doTestXcode(mode: ImportMode) {
|
||||
private fun doTestXcode(mode: ImportMode, isCustomFrameworkName: Boolean) {
|
||||
assumeTrue(HostManager.hostIsMac)
|
||||
val gradleProject = transformProjectWithPluginsDsl("new-mpp-cocoapods", gradleVersion)
|
||||
|
||||
with(gradleProject) {
|
||||
setupWorkingDir()
|
||||
|
||||
// Add property with custom framework name
|
||||
if (isCustomFrameworkName) {
|
||||
gradleBuildScript("kotlin-library").appendText(
|
||||
"""
|
||||
kotlin {
|
||||
cocoapods {
|
||||
frameworkName = "MultiPlatformLibrary"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// Change swift sources import
|
||||
val iosAppDir = projectDir.resolve("ios-app")
|
||||
iosAppDir.resolve("ios-app/ViewController.swift").modify {
|
||||
it.replace("import kotlin_library", "import MultiPlatformLibrary")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Generate podspec.
|
||||
gradleProject.build(":kotlin-library:podspec", "-Pkotlin.native.cocoapods.generate.wrapper=true") {
|
||||
assertSuccessful()
|
||||
@@ -178,7 +216,8 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
|
||||
// Install pods.
|
||||
runCommand(iosAppDir, "pod", "install") {
|
||||
assertEquals(0, exitCode, """
|
||||
assertEquals(
|
||||
0, exitCode, """
|
||||
|Exit code mismatch for `pod install`.
|
||||
|stdout:
|
||||
|$stdOut
|
||||
@@ -190,15 +229,17 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
}
|
||||
|
||||
// Run Xcode build.
|
||||
runCommand(iosAppDir, "xcodebuild",
|
||||
"-sdk", "iphonesimulator",
|
||||
"-arch", "arm64",
|
||||
"-configuration", "Release",
|
||||
"-workspace", "ios-app.xcworkspace",
|
||||
"-scheme", "ios-app",
|
||||
inheritIO = true // Xcode doesn't finish the process if the PIPE redirect is used.
|
||||
runCommand(
|
||||
iosAppDir, "xcodebuild",
|
||||
"-sdk", "iphonesimulator",
|
||||
"-arch", "arm64",
|
||||
"-configuration", "Release",
|
||||
"-workspace", "ios-app.xcworkspace",
|
||||
"-scheme", "ios-app",
|
||||
inheritIO = true // Xcode doesn't finish the process if the PIPE redirect is used.
|
||||
) {
|
||||
assertEquals(0, exitCode, """
|
||||
assertEquals(
|
||||
0, exitCode, """
|
||||
|Exit code mismatch for `xcodebuild`.
|
||||
|stdout:
|
||||
|$stdOut
|
||||
@@ -212,8 +253,12 @@ class CocoaPodsIT : BaseGradleIT() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testXcodeUseFrameworks() = doTestXcode(ImportMode.FRAMEWORKS)
|
||||
fun testXcodeUseFrameworks() = doTestXcode(ImportMode.FRAMEWORKS, false)
|
||||
|
||||
@Test
|
||||
fun testXcodeUseModularHeaders() = doTestXcode(ImportMode.MODULAR_HEADERS)
|
||||
fun testXcodeUseModularHeaders() = doTestXcode(ImportMode.MODULAR_HEADERS, false)
|
||||
|
||||
@Test
|
||||
fun testXcodeWithCustomFrameworkName() = doTestXcode(ImportMode.FRAMEWORKS, true)
|
||||
|
||||
}
|
||||
+10
-1
@@ -11,6 +11,7 @@ import org.gradle.api.Action
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.AbstractExecTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
@@ -27,10 +28,16 @@ import java.io.File
|
||||
*/
|
||||
sealed class NativeBinary(
|
||||
private val name: String,
|
||||
open var baseName: String,
|
||||
baseNameProvided: String,
|
||||
val buildType: NativeBuildType,
|
||||
var compilation: KotlinNativeCompilation
|
||||
) : Named {
|
||||
open var baseName: String
|
||||
get() = baseNameProvider.get()
|
||||
set(value) {
|
||||
baseNameProvider = project.provider { value }
|
||||
}
|
||||
internal var baseNameProvider: Provider<String> = project.provider { baseNameProvided }
|
||||
|
||||
internal val konanTarget: KonanTarget
|
||||
get() = compilation.konanTarget
|
||||
@@ -247,8 +254,10 @@ class Framework(
|
||||
enum class BitcodeEmbeddingMode {
|
||||
/** Don't embed LLVM IR bitcode. */
|
||||
DISABLE,
|
||||
|
||||
/** Embed LLVM IR bitcode as data. */
|
||||
BITCODE,
|
||||
|
||||
/** Embed placeholder LLVM IR data as a marker. */
|
||||
MARKER,
|
||||
}
|
||||
|
||||
+6
@@ -47,6 +47,12 @@ open class CocoapodsExtension(private val project: Project) {
|
||||
@Input
|
||||
var homepage: String? = null
|
||||
|
||||
/**
|
||||
* Configure framework name of the pod built from this project.
|
||||
*/
|
||||
@Input
|
||||
var frameworkName: String = project.name.asValidFrameworkName()
|
||||
|
||||
private val _pods = project.container(CocoapodsDependency::class.java)
|
||||
|
||||
// For some reason Gradle doesn't consume the @Nested annotation on NamedDomainObjectContainer.
|
||||
|
||||
+7
-3
@@ -61,9 +61,10 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
private fun KotlinMultiplatformExtension.targetsForPlatform(requestedPlatform: KonanTarget) =
|
||||
supportedTargets().matching { it.konanTarget == requestedPlatform }
|
||||
|
||||
private fun createDefaultFrameworks(kotlinExtension: KotlinMultiplatformExtension) {
|
||||
private fun createDefaultFrameworks(kotlinExtension: KotlinMultiplatformExtension, cocoapodsExtension: CocoapodsExtension) {
|
||||
kotlinExtension.supportedTargets().all { target ->
|
||||
target.binaries.framework {
|
||||
baseNameProvider = project.provider { cocoapodsExtension.frameworkName }
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
@@ -156,7 +157,9 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
project: Project,
|
||||
cocoapodsExtension: CocoapodsExtension
|
||||
) {
|
||||
val dummyFrameworkTask = project.tasks.create("generateDummyFramework", DummyFrameworkTask::class.java)
|
||||
val dummyFrameworkTask = project.tasks.create("generateDummyFramework", DummyFrameworkTask::class.java) {
|
||||
it.settings = cocoapodsExtension
|
||||
}
|
||||
|
||||
project.tasks.create("podspec", PodspecTask::class.java) {
|
||||
it.group = TASK_GROUP
|
||||
@@ -237,12 +240,13 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
override fun apply(project: Project): Unit = with(project) {
|
||||
|
||||
pluginManager.withPlugin("kotlin-multiplatform") {
|
||||
val kotlinExtension = project.multiplatformExtension
|
||||
val cocoapodsExtension = CocoapodsExtension(this)
|
||||
|
||||
kotlinExtension.addExtension(EXTENSION_NAME, cocoapodsExtension)
|
||||
createDefaultFrameworks(kotlinExtension)
|
||||
createDefaultFrameworks(kotlinExtension, cocoapodsExtension)
|
||||
createSyncTask(project, kotlinExtension)
|
||||
createPodspecGenerationTask(project, cocoapodsExtension)
|
||||
createInterops(project, kotlinExtension, cocoapodsExtension)
|
||||
|
||||
+15
-9
@@ -7,6 +7,7 @@
|
||||
package org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.wrapper.Wrapper
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension
|
||||
@@ -30,8 +31,11 @@ open class PodspecTask : DefaultTask() {
|
||||
@OutputFile
|
||||
val outputFile: File = project.projectDir.resolve("$specName.podspec")
|
||||
|
||||
@Nested
|
||||
lateinit var settings: CocoapodsExtension
|
||||
@Input
|
||||
val frameworkNameProvider: Provider<String> = project.provider { settings.frameworkName }
|
||||
|
||||
@get:Nested
|
||||
internal lateinit var settings: CocoapodsExtension
|
||||
|
||||
// TODO: Handle Framework name customization - rename the framework during sync process.
|
||||
@TaskAction
|
||||
@@ -69,7 +73,7 @@ open class PodspecTask : DefaultTask() {
|
||||
| spec.summary = '${settings.summary.orEmpty()}'
|
||||
|
|
||||
| spec.static_framework = true
|
||||
| spec.vendored_frameworks = "$frameworkDir/#{spec.name}.framework"
|
||||
| spec.vendored_frameworks = "$frameworkDir/${frameworkNameProvider.get()}.framework"
|
||||
| spec.libraries = "c++"
|
||||
| spec.module_name = "#{spec.name}_umbrella"
|
||||
|
|
||||
@@ -133,12 +137,14 @@ open class DummyFrameworkTask : DefaultTask() {
|
||||
@OutputDirectory
|
||||
val destinationDir = project.cocoapodsBuildDirs.framework
|
||||
|
||||
@get:Input
|
||||
val frameworkName
|
||||
get() = project.name.asValidFrameworkName()
|
||||
@Input
|
||||
val frameworkNameProvider: Provider<String> = project.provider { settings.frameworkName }
|
||||
|
||||
@get:Nested
|
||||
internal lateinit var settings: CocoapodsExtension
|
||||
|
||||
private val frameworkDir: File
|
||||
get() = destinationDir.resolve("$frameworkName.framework")
|
||||
get() = destinationDir.resolve("${frameworkNameProvider.get()}.framework")
|
||||
|
||||
private fun copyResource(from: String, to: File) {
|
||||
to.parentFile.mkdirs()
|
||||
@@ -184,11 +190,11 @@ open class DummyFrameworkTask : DefaultTask() {
|
||||
|
||||
// Copy files for the dummy framework.
|
||||
copyFrameworkFile("Info.plist")
|
||||
copyFrameworkFile("dummy", frameworkName)
|
||||
copyFrameworkFile("dummy", frameworkNameProvider.get())
|
||||
copyFrameworkFile("Headers/dummy.h")
|
||||
copyFrameworkTextFile("Modules/module.modulemap") {
|
||||
if (it == "framework module dummy {") {
|
||||
it.replace("dummy", frameworkName)
|
||||
it.replace("dummy", frameworkNameProvider.get())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user