From 7df04f074c53a4768452f5a93ba8265ccf00860b Mon Sep 17 00:00:00 2001 From: Mark Turner Date: Wed, 24 Jul 2024 16:24:49 +0930 Subject: [PATCH 01/20] Add example link to `radioplayer-kt`. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f7a42a..33d8ec8 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,8 @@ swiftklib { ### Examples -More samples can be found in the [examples/](https://github.com/ttypic/swift-klib-plugin/tree/main/examples) folder. +- More samples can be found in the [examples/](https://github.com/ttypic/swift-klib-plugin/tree/main/examples) folder. +- Component demonstrating a multipurpose Kotlin Multiplatform and Swift Package audio player: [radioplayer-kt](https://github.com/markst/radioplayer-kt) ## License From 0a6693c5551a5bdf91a3da2ffe44e25d54147bdb Mon Sep 17 00:00:00 2001 From: frankois Date: Sun, 3 Nov 2024 20:20:29 +0100 Subject: [PATCH 02/20] using command line instead of string manipulation For VersionRange, it's not possible to have a specific one We need to try/cacth every swift step and print on the ouput the error, it's important for the user All test are passing except local package The local package need to be done --- .../gradle/fixture/SwiftKlibTestFixture.kt | 7 +- .../gradle/SwiftPackageDependency.kt | 18 ++- .../gradle/api/RemotePackageConfiguration.kt | 6 +- .../RemotePackageConfigurationImpl.kt | 19 ++- .../swiftklib/gradle/task/CompileSwiftTask.kt | 126 +++++++++++++++++- .../gradle/templates/CreatePackageSwift.kt | 63 ++------- 6 files changed, 166 insertions(+), 73 deletions(-) diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt index 0349427..71d190a 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt @@ -232,14 +232,17 @@ private class TestSwiftPackageConfigurationImpl : SwiftPackageConfiguration { private class TestRemotePackageConfigurationImpl(private val name: String) : RemotePackageConfiguration { private var url: String? = null + private var packageName: String? = null private var versionConfig: TestVersionConfig? = null - override fun github(owner: String, repo: String) { + override fun github(owner: String, repo: String, packageName: String?) { url = "https://github.com/$owner/$repo.git" + this.packageName = packageName } - override fun url(url: String) { + override fun url(url: String, packageName: String?) { this.url = url + this.packageName = name } override fun exactVersion(version: String) { diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt index 96015a4..a71a62d 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt @@ -2,16 +2,20 @@ package io.github.ttypic.swiftklib.gradle import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional import java.io.File import java.io.Serializable internal sealed interface SwiftPackageDependency : Serializable { @get:Input val name: String + @get:Input @get:Optional + val packageName: String? data class Local( @Input override val name: String, - @InputDirectory val path: File + @InputDirectory val path: File, + @Input @get:Optional override val packageName: String? = null, ) : SwiftPackageDependency { init { require(name.isNotBlank()) { "Package name cannot be blank" } @@ -26,7 +30,8 @@ internal sealed interface SwiftPackageDependency : Serializable { data class ExactVersion( @Input override val name: String, @Input override val url: String, - @Input val version: String + @Input val version: String, + @Input @get:Optional override val packageName: String? = null ) : Remote { init { require(name.isNotBlank()) { "Package name cannot be blank" } @@ -40,7 +45,8 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input override val url: String, @Input val from: String, @Input val to: String, - @Input val inclusive: Boolean = true + @Input val inclusive: Boolean = true, + @Input @get:Optional override val packageName: String? = null ) : Remote { init { require(name.isNotBlank()) { "Package name cannot be blank" } @@ -53,7 +59,8 @@ internal sealed interface SwiftPackageDependency : Serializable { data class Branch( @Input override val name: String, @Input override val url: String, - @Input val branchName: String + @Input val branchName: String, + @Input @get:Optional override val packageName: String? = null ) : Remote { init { require(name.isNotBlank()) { "Package name cannot be blank" } @@ -65,7 +72,8 @@ internal sealed interface SwiftPackageDependency : Serializable { data class FromVersion( @Input override val name: String, @Input override val url: String, - @Input val version: String + @Input val version: String, + @Input @get:Optional override val packageName: String? = null ) : Remote { init { require(name.isNotBlank()) { "Package name cannot be blank" } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/RemotePackageConfiguration.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/RemotePackageConfiguration.kt index c659cb6..3d07cb4 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/RemotePackageConfiguration.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/RemotePackageConfiguration.kt @@ -4,13 +4,15 @@ package io.github.ttypic.swiftklib.gradle.api interface RemotePackageConfiguration { /** * Sets GitHub repository as the package source. + * Specifies the main package name in case of multi target package (ex: Firebase) */ - fun github(owner: String, repo: String) + fun github(owner: String, repo: String, packageName: String? = null) /** * Sets custom URL as the package source. + * Specifies the main package name in case of multi target package (ex: Firebase) */ - fun url(url: String) + fun url(url: String, packageName: String? = null) /** * Specifies exact version of the package. diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt index d87d587..f2405ca 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt @@ -10,24 +10,28 @@ internal class RemotePackageConfigurationImpl @Inject constructor( private val name: String ) : RemotePackageConfiguration { private val urlProperty = objects.property(String::class.java) + private val packageName = objects.property(String::class.java) private var dependency: SwiftPackageDependency.Remote? = null - override fun github(owner: String, repo: String) { + override fun github(owner: String, repo: String, packageName: String?) { require(owner.isNotBlank()) { "Owner cannot be blank" } require(repo.isNotBlank()) { "Repo cannot be blank" } urlProperty.set("https://github.com/$owner/$repo.git") + this.packageName.set(packageName) } - override fun url(url: String) { + override fun url(url: String, packageName: String?) { require(url.isNotBlank()) { "URL cannot be blank" } urlProperty.set(url) + this.packageName.set(packageName) } override fun exactVersion(version: String) { dependency = SwiftPackageDependency.Remote.ExactVersion( name = name, url = requireUrl(), - version = version + version = version, + packageName = packageName.orNull ) } @@ -37,7 +41,8 @@ internal class RemotePackageConfigurationImpl @Inject constructor( url = requireUrl(), from = from, to = to, - inclusive = inclusive + inclusive = inclusive, + packageName = packageName.orNull ) } @@ -45,7 +50,8 @@ internal class RemotePackageConfigurationImpl @Inject constructor( dependency = SwiftPackageDependency.Remote.Branch( name = name, url = requireUrl(), - branchName = branchName + branchName = branchName, + packageName = packageName.orNull ) } @@ -53,7 +59,8 @@ internal class RemotePackageConfigurationImpl @Inject constructor( dependency = SwiftPackageDependency.Remote.FromVersion( name = name, url = requireUrl(), - version = version + version = version, + packageName = packageName.orNull ) } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index aa75cc2..248c7df 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -3,7 +3,7 @@ package io.github.ttypic.swiftklib.gradle.task import io.github.ttypic.swiftklib.gradle.CompileTarget import io.github.ttypic.swiftklib.gradle.EXTENSION_NAME import io.github.ttypic.swiftklib.gradle.SwiftPackageDependency -import io.github.ttypic.swiftklib.gradle.templates.createPackageSwiftContents +import io.github.ttypic.swiftklib.gradle.templates.toSwiftArgs import io.github.ttypic.swiftklib.gradle.util.StringReplacingOutputStream import org.gradle.api.DefaultTask import org.gradle.api.provider.ListProperty @@ -20,6 +20,7 @@ import org.gradle.process.ExecOperations import java.io.ByteArrayOutputStream import java.io.File import java.math.BigInteger +import java.nio.file.Path import java.security.MessageDigest import javax.inject.Inject @@ -118,14 +119,119 @@ abstract class CompileSwiftTask @Inject constructor( } private fun createPackageSwift(dependencies: List) { - val packageSwiftContents = createPackageSwiftContents(cinteropName, dependencies) + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "init", + "--name", + cinteropName, + "--type", + "empty", + "--disable-xctest", + "--disable-swift-testing" + ) + }.run { + if (exitValue != 0) { + throw RuntimeException("Failed to init Swift Package") + } + } + + dependencies.forEach { dependency -> + if (dependency is SwiftPackageDependency.Local) { + addLocalPackage(dependency.path.absolutePath) + } else { + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.args = listOf("package", "add-dependency") + dependency.toSwiftArgs() + it.isIgnoreExitValue = true + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package dependency $dependency", + ) + } + } + } + } + + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.args = listOf( + "package", + "add-target", + "--path", + cinteropName, + cinteropName + ) + it.isIgnoreExitValue = true + }.run { + if (exitValue != 0) { + throw RuntimeException("Failed to add Swift Package target $cinteropName") + } + } + + dependencies.forEach { dependency -> + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "add-target-dependency", + dependency.name, + "--package", + dependency.packageName ?: dependency.name, + cinteropName, + ) + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package target dependency $cinteropName - package = ${dependency.name}", + ) + } + } + } + + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "add-product", + "--targets", + cinteropName, + "--type", + "static-library", + cinteropName + ) + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package library $cinteropName", + ) + } + } if (printDebug) { logger.warn("======== Package.swift contents ========") - logger.warn(packageSwiftContents) + logger.warn(File(swiftBuildDir, "Package.swift").readText()) logger.warn("======== | Package.swift contents | ========") } - File(swiftBuildDir, "Package.swift") - .writeText(packageSwiftContents) + } + + private fun addLocalPackage(path: String) { + File(swiftBuildDir, "Package.swift").readText().let { + val content = if (!it.contains("dependencies:")) { + it.replace("name: \"cinteropName\",", "name: \"cinteropName\", dependencies:[],") + } else { + + } + } } private fun buildSwift(xcodeVersion: Int): SwiftBuildResult { @@ -157,7 +263,8 @@ abstract class CompileSwiftTask @Inject constructor( ) } - val releaseBuildPath = File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-macosx/release") + val releaseBuildPath = + File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-macosx/release") return SwiftBuildResult( libPath = File(releaseBuildPath, "lib${cinteropName}.a"), @@ -245,7 +352,12 @@ abstract class CompileSwiftTask @Inject constructor( * Note: adds lib-file md5 hash to library in order to automatically * invalidate connected cinterop task */ - private fun createDefFile(libPath: File, headerPath: File, packageName: String, xcodeVersion: Int) { + private fun createDefFile( + libPath: File, + headerPath: File, + packageName: String, + xcodeVersion: Int + ) { val xcodePath = readXcodePath() val linkerPlatformVersion = diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt index a5d8dba..f47ae9d 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt @@ -2,61 +2,22 @@ package io.github.ttypic.swiftklib.gradle.templates import io.github.ttypic.swiftklib.gradle.SwiftPackageDependency -internal fun createPackageSwiftContents( - cinteropName: String, - dependencies: Collection -): String = """ - // swift-tools-version:5.6 - import PackageDescription - - let package = Package( - name: "$cinteropName", - products: [ - .library( - name: "$cinteropName", - type: .static, - targets: ["$cinteropName"]) - ], - dependencies: [ - ${dependencies.joinToString(",\n ") { it.toSwiftPackageDeclaration() }} - ], - targets: [ - .target( - name: "$cinteropName", - dependencies: [ - ${dependencies.joinToString(",\n ") { "\"${it.name}\"" }} - ], - path: "$cinteropName") - ] - ) -""".trimIndent() - - -internal fun SwiftPackageDependency.toSwiftPackageDeclaration(): String = when (this) { +internal fun SwiftPackageDependency.toSwiftArgs(): List = when (this) { is SwiftPackageDependency.Local -> - """ - .package(path: "${path.absolutePath}") - """.trimIndent() - + emptyList() is SwiftPackageDependency.Remote.ExactVersion -> - """ - .package(url: "$url", exact: "$version") - """.trimIndent() - + listOf(url, "--exact", version) is SwiftPackageDependency.Remote.VersionRange -> { - val operator = if (inclusive) "..." else "..<" - """ - .package(url: "$url", "$from"$operator"$to") - """.trimIndent() + if (inclusive) { + // can't do inclusive range from command line + // but I think it's better to use up-to-next-minor-from + listOf(url, "--up-to-next-minor-from", from) + } else { + listOf(url, "--from", from, "--to", to) + } } - is SwiftPackageDependency.Remote.Branch -> - """ - .package(url: "$url", branch: "$branchName") - """.trimIndent() - + listOf(url, "--branch", branchName) is SwiftPackageDependency.Remote.FromVersion -> - """ - .package(url: "$url", from: "$version") - """.trimIndent() + listOf(url, "--from", version) } From 3f7ae02e2de2e2611d5306fe87097337a62a7e64 Mon Sep 17 00:00:00 2001 From: frankois Date: Sun, 3 Nov 2024 21:57:10 +0100 Subject: [PATCH 03/20] fix local spm we can now use local path, some manual update has been done all tests are passing --- .../swiftklib/gradle/task/CompileSwiftTask.kt | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index 248c7df..7ad6295 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -20,7 +20,6 @@ import org.gradle.process.ExecOperations import java.io.ByteArrayOutputStream import java.io.File import java.math.BigInteger -import java.nio.file.Path import java.security.MessageDigest import javax.inject.Inject @@ -141,19 +140,31 @@ abstract class CompileSwiftTask @Inject constructor( dependencies.forEach { dependency -> if (dependency is SwiftPackageDependency.Local) { - addLocalPackage(dependency.path.absolutePath) + addDependencyBlockIfNeeded(cinteropName) + val escapePath = dependency.path.absolutePath.replace("/", "\\/") + execOperations.exec { + it.executable = "sed" + it.workingDir = swiftBuildDir + it.args = listOf( + "-i", + "''", + "/dependencies: \\[/,/]/ s/]/ \\n\\t.package(path: \"${escapePath}\"),\\n]/", + "Package.swift" + ) + it.isIgnoreExitValue = true + } } else { execOperations.exec { it.executable = "swift" it.workingDir = swiftBuildDir it.args = listOf("package", "add-dependency") + dependency.toSwiftArgs() it.isIgnoreExitValue = true - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package dependency $dependency", - ) - } + } + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package dependency $dependency", + ) } } } @@ -224,12 +235,11 @@ abstract class CompileSwiftTask @Inject constructor( } } - private fun addLocalPackage(path: String) { - File(swiftBuildDir, "Package.swift").readText().let { - val content = if (!it.contains("dependencies:")) { - it.replace("name: \"cinteropName\",", "name: \"cinteropName\", dependencies:[],") - } else { - + private fun addDependencyBlockIfNeeded(name: String) { + File(swiftBuildDir, "Package.swift").readText().run { + if (!contains("dependencies:")) { + val updated = replace("name: \"$name\"", "name: \"$name\",\n\tdependencies: []") + File(swiftBuildDir, "Package.swift").writeText(updated) } } } From b47ec304dd1726a822e930f78ddab181f12373f9 Mon Sep 17 00:00:00 2001 From: frankois Date: Mon, 4 Nov 2024 13:45:32 +0100 Subject: [PATCH 04/20] make the plugin working namespace add new test for building and liking Firebase using triple for specify the build target --- .../gradle/SwiftPackageModulesTest.kt | 36 +++++++++ .../gradle/fixture/SwiftKlibTestFixture.kt | 44 ++++++----- .../swiftklib/gradle/SwiftKlibEntryImpl.kt | 17 ++--- .../swiftklib/gradle/api/SwiftKlibEntry.kt | 8 +- .../swiftklib/gradle/task/CompileSwiftTask.kt | 75 ++++++++++++------- 5 files changed, 122 insertions(+), 58 deletions(-) diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt index 1f5cd5c..8f27149 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt @@ -297,6 +297,42 @@ class SwiftPackageModulesTest { assertThat(result).output().contains("Package path must exist") } + @Test + fun `build with remote SPM dependency using Firebase is successful`() { + // Given + val fixture = SwiftKlibTestFixture.builder() + .withSwiftSources( + SwiftSource.of(content = """ + import FirebaseAuth + import Firebase + + @objc public class FirebaseData: NSObject { + @objc public func printVersion() { + print(FirebaseVersion()) + print(ActionCodeOperation.emailLink) + } + } + """.trimIndent()) + ) + .withConfiguration { + minIos = "14.0" + minMacos = "10.15" + dependencies { + remote("FirebaseAuth") { + url("https://github.com/firebase/firebase-ios-sdk.git", "firebase-ios-sdk") + exactVersion("11.0.0") + } + } + } + .build() + + // When + val result = build(fixture.gradleProject.rootDir, "build") + + // Then + assertThat(result).task(":library:build").succeeded() + assertPackageResolved(fixture, "firebase-ios-sdk") + } private fun assertPackageResolved(fixture: SwiftKlibTestFixture, vararg packageNames: String) { val resolvedFile = File( diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt index 71d190a..01974e2 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt @@ -129,16 +129,16 @@ abstract class SwiftKlibTestFixture private constructor( // Only add minimum version configurations if they differ from defaults if (entry._minIos.hasValue()) { - appendLine(" minIos.set(${entry.minIos})") + appendLine(" minIos = \"${entry.minIos}\"") } if (entry._minMacos.hasValue()) { - appendLine(" minMacos.set(${entry.minMacos})") + appendLine(" minMacos = \"${entry.minMacos}\"") } if (entry._minTvos.hasValue()) { - appendLine(" minTvos.set(${entry.minTvos})") + appendLine(" minTvos = \"${entry.minTvos}\"") } if (entry._minWatchos.hasValue()) { - appendLine(" minWatchos.set(${entry.minWatchos})") + appendLine(" minWatchos = \"${entry.minWatchos}\"") } if (entry.dependencies.isNotEmpty()) { @@ -190,16 +190,16 @@ val Plugin.Companion.kotlinMultiplatform private class TestSwiftKlibEntryImpl : SwiftKlibEntry { val _path = notNull() - val _minIos = notNull() - val _minMacos = notNull() - val _minTvos = notNull() - val _minWatchos = notNull() + val _minIos = notNull() + val _minMacos = notNull() + val _minTvos = notNull() + val _minWatchos = notNull() override var path: File by _path - override var minIos: Int by _minIos - override var minMacos: Int by _minMacos - override var minTvos: Int by _minTvos - override var minWatchos: Int by _minWatchos + override var minIos: String by _minIos + override var minMacos: String by _minMacos + override var minTvos: String by _minTvos + override var minWatchos: String by _minWatchos val dependencies = mutableListOf() @@ -229,7 +229,8 @@ private class TestSwiftPackageConfigurationImpl : SwiftPackageConfiguration { } } -private class TestRemotePackageConfigurationImpl(private val name: String) : RemotePackageConfiguration { +private class TestRemotePackageConfigurationImpl(private val name: String) : + RemotePackageConfiguration { private var url: String? = null private var packageName: String? = null @@ -242,7 +243,7 @@ private class TestRemotePackageConfigurationImpl(private val name: String) : Rem override fun url(url: String, packageName: String?) { this.url = url - this.packageName = name + this.packageName = packageName } override fun exactVersion(version: String) { @@ -265,7 +266,8 @@ private class TestRemotePackageConfigurationImpl(private val name: String) : Rem return TestDependencyConfig.Remote( name = name, url = url, - version = versionConfig + version = versionConfig, + packageName = packageName ) } } @@ -280,12 +282,17 @@ private sealed interface TestDependencyConfig { data class Remote( val name: String, val url: String?, - val version: TestVersionConfig? + val version: TestVersionConfig?, + val packageName: String? ) : TestDependencyConfig { override fun toConfigString() = buildString { append("remote(\"$name\") {\n") if (url != null) { - append(" url(\"$url\")\n") + if (packageName != null) { + append(" url(\"$url\", \"$packageName\")\n") + } else { + append(" url(\"$url\")\n") + } } if (version != null) { append(" ${version.toConfigString()}\n") @@ -321,7 +328,8 @@ private class NotNullVar() : ReadWriteProperty { private var value: T? = null public override fun getValue(thisRef: Any?, property: KProperty<*>): T { - return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") + return value + ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") } public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt index efe4286..63d58de 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt @@ -16,16 +16,15 @@ internal abstract class SwiftKlibEntryImpl @Inject constructor( ) : SwiftKlibEntry { val _path: Property = objects.property(File::class.java) val _packageName: Property = objects.property(String::class.java) - val _minIos: Property = objects.property(Int::class.java).convention(13) - val _minMacos: Property = objects.property(Int::class.java).convention(11) - val _minTvos: Property = objects.property(Int::class.java).convention(13) - val _minWatchos: Property = objects.property(Int::class.java).convention(8) - + val _minIos: Property = objects.property(String::class.java).convention("12.0") + val _minMacos: Property = objects.property(String::class.java).convention("10.13") + val _minTvos: Property = objects.property(String::class.java).convention("12.0") + val _minWatchos: Property = objects.property(String::class.java).convention("4.0") override var path: File by _path.bind() - override var minIos: Int by _minIos.bind() - override var minMacos: Int by _minMacos.bind() - override var minTvos: Int by _minTvos.bind() - override var minWatchos: Int by _minWatchos.bind() + override var minIos: String by _minIos.bind() + override var minMacos: String by _minMacos.bind() + override var minTvos: String by _minTvos.bind() + override var minWatchos: String by _minWatchos.bind() internal val dependencyHandler = SwiftPackageConfigurationImpl(objects) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt index 2eec408..436b2b3 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt @@ -5,10 +5,10 @@ import java.io.File interface SwiftKlibEntry { var path: File - var minIos: Int - var minMacos: Int - var minTvos: Int - var minWatchos: Int + var minIos: String + var minMacos: String + var minTvos: String + var minWatchos: String fun packageName(name: String) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index 7ad6295..860b6a9 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -30,10 +30,10 @@ abstract class CompileSwiftTask @Inject constructor( @Input val buildDirectory: String, @InputDirectory val pathProperty: Property, @Input val packageNameProperty: Property, - @Optional @Input val minIosProperty: Property, - @Optional @Input val minMacosProperty: Property, - @Optional @Input val minTvosProperty: Property, - @Optional @Input val minWatchosProperty: Property, + @Optional @Input val minIosProperty: Property, + @Optional @Input val minMacosProperty: Property, + @Optional @Input val minTvosProperty: Property, + @Optional @Input val minWatchosProperty: Property, ) : DefaultTask() { @get:Optional @@ -81,10 +81,10 @@ abstract class CompileSwiftTask @Inject constructor( ) } - private val minIos get() = minIosProperty.getOrElse(13) - private val minMacos get() = minMacosProperty.getOrElse(11) - private val minTvos get() = minTvosProperty.getOrElse(13) - private val minWatchos get() = minWatchosProperty.getOrElse(8) + private val minIos get() = minIosProperty.getOrElse("12.0") + private val minMacos get() = minMacosProperty.getOrElse("10.13") + private val minTvos get() = minTvosProperty.getOrElse("12.0") + private val minWatchos get() = minWatchosProperty.getOrElse("4.0") /** * Creates build directory or cleans up if it already exists @@ -168,7 +168,7 @@ abstract class CompileSwiftTask @Inject constructor( } } } - + addPlatformBlock(cinteropName) execOperations.exec { it.executable = "swift" it.workingDir = swiftBuildDir @@ -235,6 +235,27 @@ abstract class CompileSwiftTask @Inject constructor( } } + private fun addPlatformBlock(name: String) { + File(swiftBuildDir, "Package.swift").readText().run { + if (!contains("platforms:")) { + val entries = listOfNotNull( + ".iOS(\"$minIos\")".takeIf { !minIos.isNullOrEmpty() }, + ".macOS(\"$minMacos\")".takeIf { !minMacos.isNullOrEmpty() }, + ".tvOS(\"$minTvos\")".takeIf { !minTvos.isNullOrEmpty() }, + ".watchOS(\"$minWatchos\")".takeIf { !minWatchos.isNullOrEmpty() }, + ).joinToString(",") + if (entries.isNotEmpty()) { + val updated = + replace( + "name: \"$name\",\n", + "name: \"$name\",\n\tplatforms: [$entries],\n" + ) + File(swiftBuildDir, "Package.swift").writeText(updated) + } + } + } + } + private fun addDependencyBlockIfNeeded(name: String) { File(swiftBuildDir, "Package.swift").readText().run { if (!contains("dependencies:")) { @@ -274,7 +295,7 @@ abstract class CompileSwiftTask @Inject constructor( } val releaseBuildPath = - File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-macosx/release") + File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/release") return SwiftBuildResult( libPath = File(releaseBuildPath, "lib${cinteropName}.a"), @@ -284,16 +305,16 @@ abstract class CompileSwiftTask @Inject constructor( private fun generateBuildArgs(): List { val sdkPath = readSdkPath() - val baseArgs = "swift build --arch ${compileTarget.arch()} -c release".split(" ") - - val xcrunArgs = listOf( - "-sdk", - sdkPath, - "-target", - compileTarget.asSwiftcTarget(compileTarget.operatingSystem()), - ).asSwiftcArgs() - - return baseArgs + xcrunArgs + return listOf( + "swift", + "build", + "-c", + "release", + "--triple", + "${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${minOs(compileTarget)}${compileTarget.simulatorSuffix()}", + "--sdk", + sdkPath + ) } /** Workaround for bug in toolchain where the sdk path (via `swiftc -sdk` flag) is not propagated to clang. */ @@ -379,8 +400,8 @@ abstract class CompileSwiftTask @Inject constructor( val basicLinkerOpts = listOf( "-L/usr/lib/swift", "-$linkerPlatformVersion", - "${minOs(compileTarget)}.0", - "${minOs(compileTarget)}.0", + minOs(compileTarget), + minOs(compileTarget), "-L${xcodePath}/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/${compileTarget.os()}" ) @@ -409,13 +430,13 @@ abstract class CompileSwiftTask @Inject constructor( private fun CompileTarget.operatingSystem(): String = when (this) { - CompileTarget.iosX64, CompileTarget.iosArm64, CompileTarget.iosSimulatorArm64 -> "ios$minIos" - CompileTarget.watchosX64, CompileTarget.watchosArm64, CompileTarget.watchosSimulatorArm64 -> "watchos$minWatchos" - CompileTarget.tvosX64, CompileTarget.tvosArm64, CompileTarget.tvosSimulatorArm64 -> "tvos$minTvos" - CompileTarget.macosX64, CompileTarget.macosArm64 -> "macosx$minMacos" + CompileTarget.iosX64, CompileTarget.iosArm64, CompileTarget.iosSimulatorArm64 -> "ios" + CompileTarget.watchosX64, CompileTarget.watchosArm64, CompileTarget.watchosSimulatorArm64 -> "watchos" + CompileTarget.tvosX64, CompileTarget.tvosArm64, CompileTarget.tvosSimulatorArm64 -> "tvos" + CompileTarget.macosX64, CompileTarget.macosArm64 -> "macosx" } - private fun minOs(compileTarget: CompileTarget): Int = + private fun minOs(compileTarget: CompileTarget): String? = when (compileTarget) { CompileTarget.iosX64, CompileTarget.iosArm64, CompileTarget.iosSimulatorArm64 -> minIos CompileTarget.watchosX64, CompileTarget.watchosArm64, CompileTarget.watchosSimulatorArm64 -> minWatchos From 8e39b53e294e272805188d55bf5faf7538862f36 Mon Sep 17 00:00:00 2001 From: frankois Date: Mon, 4 Nov 2024 14:33:54 +0100 Subject: [PATCH 05/20] WIP: add multi product usage for a SPM repository you can set multiple product from a dependency like Firebase --- .../gradle/SwiftPackageModulesTest.kt | 39 +++++++++++++++++++ .../gradle/fixture/SwiftKlibTestFixture.kt | 14 +++++-- .../swiftklib/gradle/RemotePackageBuilder.kt | 2 +- .../gradle/SwiftPackageDependency.kt | 22 +++++------ .../gradle/api/SwiftPackageConfiguration.kt | 7 ++++ .../RemotePackageConfigurationImpl.kt | 2 +- .../internal/SwiftPackageConfigurationImpl.kt | 15 ++++--- .../swiftklib/gradle/task/CompileSwiftTask.kt | 35 +++++++++-------- 8 files changed, 99 insertions(+), 37 deletions(-) diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt index 8f27149..ebc03f3 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt @@ -334,6 +334,45 @@ class SwiftPackageModulesTest { assertPackageResolved(fixture, "firebase-ios-sdk") } + @Test + fun `build with remote SPM dependency using multi product Firebase is successful`() { + // Given + val fixture = SwiftKlibTestFixture.builder() + .withSwiftSources( + SwiftSource.of(content = """ + import FirebaseAuth + import Firebase + import FirebaseRemoteConfig + + @objc public class FirebaseData: NSObject { + @objc public func testLinking() { + print(FirebaseVersion()) + print(ActionCodeOperation.emailLink) + print(RemoteConfigSettings()) + } + } + """.trimIndent()) + ) + .withConfiguration { + minIos = "14.0" + minMacos = "10.15" + dependencies { + remote(listOf("FirebaseAuth", "FirebaseRemoteConfig")) { + url("https://github.com/firebase/firebase-ios-sdk.git", "firebase-ios-sdk") + exactVersion("11.0.0") + } + } + } + .build() + + // When + val result = build(fixture.gradleProject.rootDir, "build") + + // Then + assertThat(result).task(":library:build").succeeded() + assertPackageResolved(fixture, "firebase-ios-sdk") + } + private fun assertPackageResolved(fixture: SwiftKlibTestFixture, vararg packageNames: String) { val resolvedFile = File( fixture.gradleProject.rootDir, diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt index 01974e2..ee8934c 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt @@ -223,13 +223,17 @@ private class TestSwiftPackageConfigurationImpl : SwiftPackageConfiguration { } override fun remote(name: String, configuration: RemotePackageConfiguration.() -> Unit) { + remote(listOf(name), configuration) + } + + override fun remote(name: List, configuration: RemotePackageConfiguration.() -> Unit) { val config = TestRemotePackageConfigurationImpl(name) config.configuration() dependencies.add(config.build()) } } -private class TestRemotePackageConfigurationImpl(private val name: String) : +private class TestRemotePackageConfigurationImpl(private val name: List) : RemotePackageConfiguration { private var url: String? = null @@ -280,13 +284,17 @@ private sealed interface TestDependencyConfig { } data class Remote( - val name: String, + val name: List, val url: String?, val version: TestVersionConfig?, val packageName: String? ) : TestDependencyConfig { override fun toConfigString() = buildString { - append("remote(\"$name\") {\n") + if (name.size == 1) { + append("remote(\"${name.first()}\") {\n") + } else { + append("remote(listOf(\"${name.joinToString("\",\"")}\")) {\n") + } if (url != null) { if (packageName != null) { append(" url(\"$url\", \"$packageName\")\n") diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/RemotePackageBuilder.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/RemotePackageBuilder.kt index 01bdc9c..a07ad11 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/RemotePackageBuilder.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/RemotePackageBuilder.kt @@ -8,7 +8,7 @@ import javax.inject.Inject @ExperimentalSwiftklibApi class RemotePackageBuilder @Inject constructor( private val objects: ObjectFactory, - private val name: String + private val name: List ) { private val urlProperty: Property = objects.property(String::class.java) private var dependency: SwiftPackageDependency.Remote? = null diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt index a71a62d..1fdc126 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt @@ -8,17 +8,17 @@ import java.io.Serializable internal sealed interface SwiftPackageDependency : Serializable { @get:Input - val name: String + val name: List @get:Input @get:Optional val packageName: String? data class Local( - @Input override val name: String, + @Input override val name: List, @InputDirectory val path: File, @Input @get:Optional override val packageName: String? = null, ) : SwiftPackageDependency { init { - require(name.isNotBlank()) { "Package name cannot be blank" } + require(name.isNotEmpty()) { "Package name cannot be blank" } require(path.exists()) { "Package path must exist: $path" } } } @@ -28,20 +28,20 @@ internal sealed interface SwiftPackageDependency : Serializable { val url: String data class ExactVersion( - @Input override val name: String, + @Input override val name: List, @Input override val url: String, @Input val version: String, @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotBlank()) { "Package name cannot be blank" } + require(name.isNotEmpty()) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } } data class VersionRange( - @Input override val name: String, + @Input override val name: List, @Input override val url: String, @Input val from: String, @Input val to: String, @@ -49,7 +49,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotBlank()) { "Package name cannot be blank" } + require(name.isNotEmpty()) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(from.isNotBlank()) { "From version cannot be blank" } require(to.isNotBlank()) { "To version cannot be blank" } @@ -57,26 +57,26 @@ internal sealed interface SwiftPackageDependency : Serializable { } data class Branch( - @Input override val name: String, + @Input override val name: List, @Input override val url: String, @Input val branchName: String, @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotBlank()) { "Package name cannot be blank" } + require(name.isNotEmpty()) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(branchName.isNotBlank()) { "Branch name cannot be blank" } } } data class FromVersion( - @Input override val name: String, + @Input override val name: List, @Input override val url: String, @Input val version: String, @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotBlank()) { "Package name cannot be blank" } + require(name.isNotEmpty()) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt index f2ef15f..3ace8d1 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt @@ -15,5 +15,12 @@ interface SwiftPackageConfiguration { * @param configuration Configuration block for the remote package */ fun remote(name: String, configuration: RemotePackageConfiguration.() -> Unit) + + /** + * Configures a remote package dependency. + * @param name a list of product to add + * @param configuration Configuration block for the remote package + */ + fun remote(name: List, configuration: RemotePackageConfiguration.() -> Unit) } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt index f2405ca..44b81fa 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/RemotePackageConfigurationImpl.kt @@ -7,7 +7,7 @@ import javax.inject.Inject internal class RemotePackageConfigurationImpl @Inject constructor( private val objects: ObjectFactory, - private val name: String + private val name: List ) : RemotePackageConfiguration { private val urlProperty = objects.property(String::class.java) private val packageName = objects.property(String::class.java) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt index a3a21f6..2b5ccf0 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt @@ -20,15 +20,12 @@ internal class SwiftPackageConfigurationImpl @Inject constructor( @ExperimentalSwiftklibApi override fun local(name: String, path: java.io.File) { val currentDeps = _dependencies.get().toMutableList() - currentDeps.add(SwiftPackageDependency.Local(name, path)) + currentDeps.add(SwiftPackageDependency.Local(listOf(name), path)) _dependencies.set(currentDeps) } @ExperimentalSwiftklibApi - override fun remote( - name: String, - configuration: RemotePackageConfiguration.() -> Unit - ) { + override fun remote(name: List, configuration: RemotePackageConfiguration.() -> Unit) { val builder = RemotePackageConfigurationImpl(objects, name) builder.apply(configuration) @@ -39,4 +36,12 @@ internal class SwiftPackageConfigurationImpl @Inject constructor( currentDeps.add(dependency) _dependencies.set(currentDeps) } + + @ExperimentalSwiftklibApi + override fun remote( + name: String, + configuration: RemotePackageConfiguration.() -> Unit + ) { + remote(listOf(name), configuration) + } } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index 860b6a9..9e0ea58 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -187,25 +187,28 @@ abstract class CompileSwiftTask @Inject constructor( } dependencies.forEach { dependency -> - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "add-target-dependency", - dependency.name, - "--package", - dependency.packageName ?: dependency.name, - cinteropName, - ) - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package target dependency $cinteropName - package = ${dependency.name}", + dependency.name.forEach { library -> + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "add-target-dependency", + library, + "--package", + dependency.packageName ?: library, + cinteropName, ) + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package target dependency $cinteropName - package = ${dependency.packageName ?: library}:$library", + ) + } } } + } execOperations.exec { From c96f686e5b0ac0b9e8d070d747f3f67cf5c924fa Mon Sep 17 00:00:00 2001 From: frankois Date: Mon, 4 Nov 2024 14:56:59 +0100 Subject: [PATCH 06/20] fix test need to duplicate the code of remote function The exception is not forwarded correctly --- .../internal/SwiftPackageConfigurationImpl.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt index 2b5ccf0..c46ff29 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt @@ -25,7 +25,10 @@ internal class SwiftPackageConfigurationImpl @Inject constructor( } @ExperimentalSwiftklibApi - override fun remote(name: List, configuration: RemotePackageConfiguration.() -> Unit) { + override fun remote( + name: List, + configuration: RemotePackageConfiguration.() -> Unit + ) { val builder = RemotePackageConfigurationImpl(objects, name) builder.apply(configuration) @@ -42,6 +45,14 @@ internal class SwiftPackageConfigurationImpl @Inject constructor( name: String, configuration: RemotePackageConfiguration.() -> Unit ) { - remote(listOf(name), configuration) + val builder = RemotePackageConfigurationImpl(objects, listOf(name)) + builder.apply(configuration) + + val dependency = builder.build() + ?: throw IllegalStateException("No version specification provided for remote package $name") + + val currentDeps = _dependencies.get().toMutableList() + currentDeps.add(dependency) + _dependencies.set(currentDeps) } } From f3dcc6a63cb49993c0be6542ad183e4d548fa7a3 Mon Sep 17 00:00:00 2001 From: frankois Date: Mon, 4 Nov 2024 15:22:05 +0100 Subject: [PATCH 07/20] fix test check case when the list of packages have a empty string add some comment for the versioning of package --- .../ttypic/swiftklib/gradle/SwiftPackageDependency.kt | 10 +++++----- .../swiftklib/gradle/templates/CreatePackageSwift.kt | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt index 1fdc126..1969d9f 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt @@ -18,7 +18,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null, ) : SwiftPackageDependency { init { - require(name.isNotEmpty()) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } require(path.exists()) { "Package path must exist: $path" } } } @@ -34,7 +34,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty()) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } @@ -49,7 +49,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty()) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(from.isNotBlank()) { "From version cannot be blank" } require(to.isNotBlank()) { "To version cannot be blank" } @@ -63,7 +63,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty()) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(branchName.isNotBlank()) { "Branch name cannot be blank" } } @@ -76,7 +76,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty()) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt index f47ae9d..d1873c2 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt @@ -11,6 +11,7 @@ internal fun SwiftPackageDependency.toSwiftArgs(): List = when (this) { if (inclusive) { // can't do inclusive range from command line // but I think it's better to use up-to-next-minor-from + // it needs to be rethink listOf(url, "--up-to-next-minor-from", from) } else { listOf(url, "--from", from, "--to", to) From 53cf47132ac2db3296e3dd32102f1118f6d9d84d Mon Sep 17 00:00:00 2001 From: frankois Date: Tue, 5 Nov 2024 09:04:28 +0100 Subject: [PATCH 08/20] update remote parameter name update comment in interface --- .../swiftklib/gradle/api/SwiftPackageConfiguration.kt | 6 +++--- .../gradle/internal/SwiftPackageConfigurationImpl.kt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt index 3ace8d1..d45c323 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftPackageConfiguration.kt @@ -11,16 +11,16 @@ interface SwiftPackageConfiguration { /** * Configures a remote package dependency. - * @param name Package name + * @param name the product's name to add * @param configuration Configuration block for the remote package */ fun remote(name: String, configuration: RemotePackageConfiguration.() -> Unit) /** * Configures a remote package dependency. - * @param name a list of product to add + * @param names a list of product's name to add * @param configuration Configuration block for the remote package */ - fun remote(name: List, configuration: RemotePackageConfiguration.() -> Unit) + fun remote(names: List, configuration: RemotePackageConfiguration.() -> Unit) } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt index c46ff29..8ad8632 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/internal/SwiftPackageConfigurationImpl.kt @@ -26,14 +26,14 @@ internal class SwiftPackageConfigurationImpl @Inject constructor( @ExperimentalSwiftklibApi override fun remote( - name: List, + names: List, configuration: RemotePackageConfiguration.() -> Unit ) { - val builder = RemotePackageConfigurationImpl(objects, name) + val builder = RemotePackageConfigurationImpl(objects, names) builder.apply(configuration) val dependency = builder.build() - ?: throw IllegalStateException("No version specification provided for remote package $name") + ?: throw IllegalStateException("No version specification provided for remote package $names") val currentDeps = _dependencies.get().toMutableList() currentDeps.add(dependency) From de4969ebd6ff0fa39e0a34e570058f4a8f1a799b Mon Sep 17 00:00:00 2001 From: frankois Date: Tue, 5 Nov 2024 09:20:41 +0100 Subject: [PATCH 09/20] add more complex case --- .../gradle/SwiftPackageModulesTest.kt | 57 +++++++++++++++++++ .../gradle/fixture/SwiftKlibTestFixture.kt | 4 +- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt index ebc03f3..ea9dda7 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt @@ -373,6 +373,63 @@ class SwiftPackageModulesTest { assertPackageResolved(fixture, "firebase-ios-sdk") } + @Test + fun `build with complex and mix spm repo`() { + // Given + val fixture = SwiftKlibTestFixture.builder() + .withSwiftSources( + SwiftSource.of(content = """ + import FirebaseAuth + import Firebase + import FirebaseRemoteConfig + import KeychainAccess + import SwiftyJSON + + @objc public class FirebaseData: NSObject { + @objc public func testLinking() { + print(FirebaseVersion()) + print(ActionCodeOperation.emailLink) + print(RemoteConfigSettings()) + } + } + @objc public class DataManager: NSObject { + private let keychain = Keychain(service: "test-service") + + @objc public func processJson(jsonString: String) throws -> String { + let json = try JSON(parseJSON: jsonString) + return json.description + } + } + """.trimIndent()) + ) + .withConfiguration { + minIos = "14.0" + minMacos = "10.15" + dependencies { + remote(listOf("FirebaseAuth", "FirebaseRemoteConfig")) { + url("https://github.com/firebase/firebase-ios-sdk.git", "firebase-ios-sdk") + exactVersion("11.0.0") + } + remote("KeychainAccess") { + github("kishikawakatsumi", "KeychainAccess") + exactVersion("4.2.2") + } + remote("SwiftyJSON") { + github("SwiftyJSON", "SwiftyJSON") + versionRange("5.0.0", "6.0.0", true) + } + } + } + .build() + + // When + val result = build(fixture.gradleProject.rootDir, "build") + + // Then + assertThat(result).task(":library:build").succeeded() + assertPackageResolved(fixture, "firebase-ios-sdk") + } + private fun assertPackageResolved(fixture: SwiftKlibTestFixture, vararg packageNames: String) { val resolvedFile = File( fixture.gradleProject.rootDir, diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt index ee8934c..7c1c1ce 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt @@ -226,8 +226,8 @@ private class TestSwiftPackageConfigurationImpl : SwiftPackageConfiguration { remote(listOf(name), configuration) } - override fun remote(name: List, configuration: RemotePackageConfiguration.() -> Unit) { - val config = TestRemotePackageConfigurationImpl(name) + override fun remote(names: List, configuration: RemotePackageConfiguration.() -> Unit) { + val config = TestRemotePackageConfigurationImpl(names) config.configuration() dependencies.add(config.build()) } From 23a58470cc0005fba347a65d137f94d88ede866c Mon Sep 17 00:00:00 2001 From: frankois Date: Wed, 6 Nov 2024 09:04:30 +0100 Subject: [PATCH 10/20] Add tools version parameter We can now set the toolsVersion from the plugin By default, the command line uses the latest swift version available. By at some cases, it can't work and a specific version need to be set. --- .../gradle/SwiftPackageModulesTest.kt | 81 +++++++++++++++++-- .../gradle/fixture/SwiftKlibTestFixture.kt | 6 ++ .../swiftklib/gradle/SwiftKlibEntryImpl.kt | 2 + .../swiftklib/gradle/SwiftKlibPlugin.kt | 1 + .../swiftklib/gradle/api/SwiftKlibEntry.kt | 1 + .../swiftklib/gradle/task/CompileSwiftTask.kt | 21 ++++- 6 files changed, 105 insertions(+), 7 deletions(-) diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt index ea9dda7..f506bea 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt @@ -430,6 +430,56 @@ class SwiftPackageModulesTest { assertPackageResolved(fixture, "firebase-ios-sdk") } + @Test + fun `build with valid toolsVersion`() { + val fixture = SwiftKlibTestFixture.builder() + .withSwiftSources( + SwiftSource.of(content = """ + import Foundation + """.trimIndent()) + ) + .withConfiguration { + toolsVersion = "5.5" + dependencies { + } + } + .build() + + // When + val result = build(fixture.gradleProject.rootDir, "build") + + // Then + assertThat(result).task(":library:build").succeeded() + getManifestContent(fixture) { manifest -> + assertTrue(manifest.contains("swift-tools-version: 5.5")) + } + } + + @Test + fun `build with invalid toolsVersion`() { + val fixture = SwiftKlibTestFixture.builder() + .withSwiftSources( + SwiftSource.of(content = """ + import Foundation + """.trimIndent()) + ) + .withConfiguration { + toolsVersion = "5.3" + dependencies { + } + } + .build() + + // When + val result = buildAndFail(fixture.gradleProject.rootDir, "build") + + // Then + assertThat(result).output().contains("package manifest version 5.3.0 is too old") + getManifestContent(fixture) { manifest -> + assertTrue(manifest.contains("swift-tools-version:5.3")) + } + } + private fun assertPackageResolved(fixture: SwiftKlibTestFixture, vararg packageNames: String) { val resolvedFile = File( fixture.gradleProject.rootDir, @@ -437,12 +487,31 @@ class SwiftPackageModulesTest { ) assertTrue(resolvedFile.exists(), "Package.resolved file not found") - val content = resolvedFile.readText() - packageNames.forEach { packageName -> - assertTrue( - content.contains("\"identity\" : \"$packageName\"", ignoreCase = true), - "$packageName dependency not found" - ) + getPackageResolvedContent(fixture) { content -> + packageNames.forEach { packageName -> + assertTrue( + content.contains("\"identity\" : \"$packageName\"", ignoreCase = true), + "$packageName dependency not found" + ) + } } } + + private fun getManifestContent(fixture: SwiftKlibTestFixture, content: (String) -> Unit) { + val resolvedFile = File( + fixture.gradleProject.rootDir, + "library/build/swiftklib/test/iosArm64/swiftBuild/Package.swift" + ) + assertTrue(resolvedFile.exists(), "Package.swift file not found") + content(resolvedFile.readText()) + } + + private fun getPackageResolvedContent(fixture: SwiftKlibTestFixture, content: (String) -> Unit) { + val resolvedFile = File( + fixture.gradleProject.rootDir, + "library/build/swiftklib/test/iosArm64/swiftBuild/Package.resolved" + ) + assertTrue(resolvedFile.exists(), "Package.resolved file not found") + content(resolvedFile.readText()) + } } diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt index 7c1c1ce..12b073c 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt @@ -140,6 +140,9 @@ abstract class SwiftKlibTestFixture private constructor( if (entry._minWatchos.hasValue()) { appendLine(" minWatchos = \"${entry.minWatchos}\"") } + if (!entry._toolsVersions.isNullOrEmpty()) { + appendLine(" toolsVersion = \"${entry.toolsVersion}\"") + } if (entry.dependencies.isNotEmpty()) { appendLine(" dependencies {") @@ -194,12 +197,15 @@ private class TestSwiftKlibEntryImpl : SwiftKlibEntry { val _minMacos = notNull() val _minTvos = notNull() val _minWatchos = notNull() + val _toolsVersions: String? + get() = toolsVersion override var path: File by _path override var minIos: String by _minIos override var minMacos: String by _minMacos override var minTvos: String by _minTvos override var minWatchos: String by _minWatchos + override var toolsVersion: String? = null val dependencies = mutableListOf() diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt index 63d58de..9333a8e 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt @@ -16,6 +16,7 @@ internal abstract class SwiftKlibEntryImpl @Inject constructor( ) : SwiftKlibEntry { val _path: Property = objects.property(File::class.java) val _packageName: Property = objects.property(String::class.java) + val _toolsVersion: Property = objects.property(String::class.java) val _minIos: Property = objects.property(String::class.java).convention("12.0") val _minMacos: Property = objects.property(String::class.java).convention("10.13") val _minTvos: Property = objects.property(String::class.java).convention("12.0") @@ -25,6 +26,7 @@ internal abstract class SwiftKlibEntryImpl @Inject constructor( override var minMacos: String by _minMacos.bind() override var minTvos: String by _minTvos.bind() override var minWatchos: String by _minWatchos.bind() + override var toolsVersion: String? by _toolsVersion.bind() internal val dependencyHandler = SwiftPackageConfigurationImpl(objects) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibPlugin.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibPlugin.kt index 41deded..ea47ef4 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibPlugin.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibPlugin.kt @@ -59,6 +59,7 @@ class SwiftKlibPlugin : Plugin { entry._minMacos, entry._minTvos, entry._minWatchos, + entry._toolsVersion ).configure { it.dependenciesProperty = entry.dependencyHandler.dependencies } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt index 436b2b3..1840f47 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt @@ -9,6 +9,7 @@ interface SwiftKlibEntry { var minMacos: String var minTvos: String var minWatchos: String + var toolsVersion: String? fun packageName(name: String) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index 9e0ea58..f9c75bb 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -34,6 +34,7 @@ abstract class CompileSwiftTask @Inject constructor( @Optional @Input val minMacosProperty: Property, @Optional @Input val minTvosProperty: Property, @Optional @Input val minWatchosProperty: Property, + @Optional @Input val toolsVersionProperty: Property, ) : DefaultTask() { @get:Optional @@ -85,7 +86,7 @@ abstract class CompileSwiftTask @Inject constructor( private val minMacos get() = minMacosProperty.getOrElse("10.13") private val minTvos get() = minTvosProperty.getOrElse("12.0") private val minWatchos get() = minWatchosProperty.getOrElse("4.0") - + private val toolsVersion get() = toolsVersionProperty.get() /** * Creates build directory or cleans up if it already exists * and copies Swift source files to it @@ -138,6 +139,24 @@ abstract class CompileSwiftTask @Inject constructor( } } + if (!toolsVersion.isNullOrEmpty()) { + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "tools-version", + "--set", + toolsVersion + ) + }.run { + if (exitValue != 0) { + throw RuntimeException("Failed to set the tool version $toolsVersion") + } + } + } + dependencies.forEach { dependency -> if (dependency is SwiftPackageDependency.Local) { addDependencyBlockIfNeeded(cinteropName) From 3711eb86d6db729df26727453fa00b609bebb477 Mon Sep 17 00:00:00 2001 From: frankois Date: Wed, 6 Nov 2024 09:13:32 +0100 Subject: [PATCH 11/20] Change ExperimentalSwiftklibApi level It needs to be set at Warning Level or it will be considered as a Error. --- .../ttypic/swiftklib/gradle/api/ExperimentalSwiftklibApi.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/ExperimentalSwiftklibApi.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/ExperimentalSwiftklibApi.kt index 4d132ca..13eda5d 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/ExperimentalSwiftklibApi.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/ExperimentalSwiftklibApi.kt @@ -1,6 +1,9 @@ package io.github.ttypic.swiftklib.gradle.api -@RequiresOptIn(message = "This API is experimental. It may be changed in the future without notice.") +@RequiresOptIn( + message = "This API is experimental. It may be changed in the future without notice.", + level = RequiresOptIn.Level.WARNING +) @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) annotation class ExperimentalSwiftklibApi { From b6427adbb35d33c9a433777f4f3a39528e50ee90 Mon Sep 17 00:00:00 2001 From: frankois Date: Wed, 6 Nov 2024 09:17:30 +0100 Subject: [PATCH 12/20] fix build (my bad) set a non null default value for toolsVersion in CompileTask --- .../github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index f9c75bb..2778710 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -86,7 +86,7 @@ abstract class CompileSwiftTask @Inject constructor( private val minMacos get() = minMacosProperty.getOrElse("10.13") private val minTvos get() = minTvosProperty.getOrElse("12.0") private val minWatchos get() = minWatchosProperty.getOrElse("4.0") - private val toolsVersion get() = toolsVersionProperty.get() + private val toolsVersion get() = toolsVersionProperty.getOrElse("") /** * Creates build directory or cleans up if it already exists * and copies Swift source files to it @@ -317,7 +317,7 @@ abstract class CompileSwiftTask @Inject constructor( } val releaseBuildPath = - File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/release") + File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/debug") return SwiftBuildResult( libPath = File(releaseBuildPath, "lib${cinteropName}.a"), From 08941f5651eab6195752a363e4adeb838db50164 Mon Sep 17 00:00:00 2001 From: frankois Date: Wed, 6 Nov 2024 09:19:50 +0100 Subject: [PATCH 13/20] rollback bad commit rollback unwanted commit change --- .../io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index 2778710..ad2df61 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -317,7 +317,7 @@ abstract class CompileSwiftTask @Inject constructor( } val releaseBuildPath = - File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/debug") + File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/release") return SwiftBuildResult( libPath = File(releaseBuildPath, "lib${cinteropName}.a"), From 166468fd54fca17c9d40c7b2d91250c525c3bc3f Mon Sep 17 00:00:00 2001 From: frankois Date: Wed, 6 Nov 2024 17:27:43 +0100 Subject: [PATCH 14/20] cleaning CompileSwiftTask moving all command for updating the manifest to CreatePackageSwift --- .../swiftklib/gradle/task/CompileSwiftTask.kt | 183 ++--------------- .../gradle/templates/CreatePackageSwift.kt | 191 +++++++++++++++++- 2 files changed, 208 insertions(+), 166 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index ad2df61..c652f14 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -3,7 +3,7 @@ package io.github.ttypic.swiftklib.gradle.task import io.github.ttypic.swiftklib.gradle.CompileTarget import io.github.ttypic.swiftklib.gradle.EXTENSION_NAME import io.github.ttypic.swiftklib.gradle.SwiftPackageDependency -import io.github.ttypic.swiftklib.gradle.templates.toSwiftArgs +import io.github.ttypic.swiftklib.gradle.templates.createPackageSwiftContents import io.github.ttypic.swiftklib.gradle.util.StringReplacingOutputStream import org.gradle.api.DefaultTask import org.gradle.api.provider.ListProperty @@ -87,6 +87,7 @@ abstract class CompileSwiftTask @Inject constructor( private val minTvos get() = minTvosProperty.getOrElse("12.0") private val minWatchos get() = minWatchosProperty.getOrElse("4.0") private val toolsVersion get() = toolsVersionProperty.getOrElse("") + /** * Creates build directory or cleans up if it already exists * and copies Swift source files to it @@ -119,171 +120,21 @@ abstract class CompileSwiftTask @Inject constructor( } private fun createPackageSwift(dependencies: List) { - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "init", - "--name", - cinteropName, - "--type", - "empty", - "--disable-xctest", - "--disable-swift-testing" - ) - }.run { - if (exitValue != 0) { - throw RuntimeException("Failed to init Swift Package") - } - } - - if (!toolsVersion.isNullOrEmpty()) { - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "tools-version", - "--set", - toolsVersion - ) - }.run { - if (exitValue != 0) { - throw RuntimeException("Failed to set the tool version $toolsVersion") - } - } - } - - dependencies.forEach { dependency -> - if (dependency is SwiftPackageDependency.Local) { - addDependencyBlockIfNeeded(cinteropName) - val escapePath = dependency.path.absolutePath.replace("/", "\\/") - execOperations.exec { - it.executable = "sed" - it.workingDir = swiftBuildDir - it.args = listOf( - "-i", - "''", - "/dependencies: \\[/,/]/ s/]/ \\n\\t.package(path: \"${escapePath}\"),\\n]/", - "Package.swift" - ) - it.isIgnoreExitValue = true - } - } else { - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.args = listOf("package", "add-dependency") + dependency.toSwiftArgs() - it.isIgnoreExitValue = true - } - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package dependency $dependency", - ) - } - } - } - addPlatformBlock(cinteropName) - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.args = listOf( - "package", - "add-target", - "--path", - cinteropName, - cinteropName - ) - it.isIgnoreExitValue = true - }.run { - if (exitValue != 0) { - throw RuntimeException("Failed to add Swift Package target $cinteropName") - } - } - - dependencies.forEach { dependency -> - dependency.name.forEach { library -> - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "add-target-dependency", - library, - "--package", - dependency.packageName ?: library, - cinteropName, - ) - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package target dependency $cinteropName - package = ${dependency.packageName ?: library}:$library", - ) - } - } - } - - } - - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "add-product", - "--targets", - cinteropName, - "--type", - "static-library", - cinteropName - ) - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package library $cinteropName", - ) - } - } + createPackageSwiftContents( + cinteropName, + dependencies, + execOperations, + swiftBuildDir, + minIos, + minMacos, + minTvos, + minWatchos, + toolsVersion + ) if (printDebug) { logger.warn("======== Package.swift contents ========") logger.warn(File(swiftBuildDir, "Package.swift").readText()) - logger.warn("======== | Package.swift contents | ========") - } - } - - private fun addPlatformBlock(name: String) { - File(swiftBuildDir, "Package.swift").readText().run { - if (!contains("platforms:")) { - val entries = listOfNotNull( - ".iOS(\"$minIos\")".takeIf { !minIos.isNullOrEmpty() }, - ".macOS(\"$minMacos\")".takeIf { !minMacos.isNullOrEmpty() }, - ".tvOS(\"$minTvos\")".takeIf { !minTvos.isNullOrEmpty() }, - ".watchOS(\"$minWatchos\")".takeIf { !minWatchos.isNullOrEmpty() }, - ).joinToString(",") - if (entries.isNotEmpty()) { - val updated = - replace( - "name: \"$name\",\n", - "name: \"$name\",\n\tplatforms: [$entries],\n" - ) - File(swiftBuildDir, "Package.swift").writeText(updated) - } - } - } - } - - private fun addDependencyBlockIfNeeded(name: String) { - File(swiftBuildDir, "Package.swift").readText().run { - if (!contains("dependencies:")) { - val updated = replace("name: \"$name\"", "name: \"$name\",\n\tdependencies: []") - File(swiftBuildDir, "Package.swift").writeText(updated) - } + logger.warn("=addPlatformBlock======= | Package.swift contents | ========") } } @@ -317,7 +168,10 @@ abstract class CompileSwiftTask @Inject constructor( } val releaseBuildPath = - File(swiftBuildDir, ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/release") + File( + swiftBuildDir, + ".build/${compileTarget.arch()}-apple-${compileTarget.operatingSystem()}${compileTarget.simulatorSuffix()}/release" + ) return SwiftBuildResult( libPath = File(releaseBuildPath, "lib${cinteropName}.a"), @@ -346,7 +200,6 @@ abstract class CompileSwiftTask @Inject constructor( readSdkPath(), ).asCcArgs() - private fun List.asSwiftcArgs() = asBuildToolArgs("swiftc") private fun List.asCcArgs() = asBuildToolArgs("cc") private fun List.asBuildToolArgs(tool: String): List { diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt index d1873c2..37dff98 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt @@ -1,12 +1,162 @@ package io.github.ttypic.swiftklib.gradle.templates import io.github.ttypic.swiftklib.gradle.SwiftPackageDependency +import org.gradle.process.ExecOperations +import java.io.File + +internal fun createPackageSwiftContents( + cinteropName: String, + dependencies: Collection, + execOperations: ExecOperations, + swiftBuildDir: File, + minIos: String, + minMacos: String, + minTvos: String, + minWatchos: String, + toolsVersion: String?, +) { + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "init", + "--name", + cinteropName, + "--type", + "empty", + "--disable-xctest", + "--disable-swift-testing" + ) + }.run { + if (exitValue != 0) { + throw RuntimeException("Failed to init Swift Package") + } + } + + if (!toolsVersion.isNullOrEmpty()) { + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "tools-version", + "--set", + toolsVersion + ) + }.run { + if (exitValue != 0) { + throw RuntimeException("Failed to set the tool version $toolsVersion") + } + } + } + + dependencies.forEach { dependency -> + if (dependency is SwiftPackageDependency.Local) { + addDependencyBlockIfNeeded(cinteropName, swiftBuildDir) + val escapePath = dependency.path.absolutePath.replace("/", "\\/") + execOperations.exec { + it.executable = "sed" + it.workingDir = swiftBuildDir + it.args = listOf( + "-i", + "''", + "/dependencies: \\[/,/]/ s/]/ \\n\\t.package(path: \"${escapePath}\"),\\n]/", + "Package.swift" + ) + it.isIgnoreExitValue = true + } + } else { + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.args = listOf("package", "add-dependency") + dependency.toSwiftArgs() + it.isIgnoreExitValue = true + } + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package dependency $dependency", + ) + } + } + } + addPlatformBlock(cinteropName, swiftBuildDir, minIos, minMacos, minTvos, minWatchos) + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.args = listOf( + "package", + "add-target", + "--path", + cinteropName, + cinteropName + ) + it.isIgnoreExitValue = true + }.run { + if (exitValue != 0) { + throw RuntimeException("Failed to add Swift Package target $cinteropName") + } + } + + dependencies.forEach { dependency -> + dependency.name.forEach { library -> + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "add-target-dependency", + library, + "--package", + dependency.packageName ?: library, + cinteropName, + ) + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package target dependency $cinteropName - package = ${dependency.packageName ?: library}:$library", + ) + } + } + } + + } + + execOperations.exec { + it.executable = "swift" + it.workingDir = swiftBuildDir + it.isIgnoreExitValue = true + it.args = listOf( + "package", + "add-product", + "--targets", + cinteropName, + "--type", + "static-library", + cinteropName + ) + }.run { + if (exitValue != 0) { + throw RuntimeException( + "Failed to add Swift Package library $cinteropName", + ) + } + } + +} + internal fun SwiftPackageDependency.toSwiftArgs(): List = when (this) { is SwiftPackageDependency.Local -> - emptyList() + emptyList() + is SwiftPackageDependency.Remote.ExactVersion -> listOf(url, "--exact", version) + is SwiftPackageDependency.Remote.VersionRange -> { if (inclusive) { // can't do inclusive range from command line @@ -17,8 +167,47 @@ internal fun SwiftPackageDependency.toSwiftArgs(): List = when (this) { listOf(url, "--from", from, "--to", to) } } + is SwiftPackageDependency.Remote.Branch -> listOf(url, "--branch", branchName) + is SwiftPackageDependency.Remote.FromVersion -> listOf(url, "--from", version) } + +private fun addDependencyBlockIfNeeded(name: String, swiftBuildDir: File) { + File(swiftBuildDir, "Package.swift").readText().run { + if (!contains("dependencies:")) { + val updated = replace("name: \"$name\"", "name: \"$name\",\n\tdependencies: []") + File(swiftBuildDir, "Package.swift").writeText(updated) + } + } +} + +private fun addPlatformBlock( + name: String, + swiftBuildDir: File, + minIos: String, + minMacos: String, + minTvos: String, + minWatchos: String +) { + File(swiftBuildDir, "Package.swift").readText().run { + if (!contains("platforms:")) { + val entries = listOfNotNull( + ".iOS(\"$minIos\")".takeIf { minIos.isNotEmpty() }, + ".macOS(\"$minMacos\")".takeIf { minMacos.isNotEmpty() }, + ".tvOS(\"$minTvos\")".takeIf { minTvos.isNotEmpty() }, + ".watchOS(\"$minWatchos\")".takeIf { minWatchos.isNotEmpty() }, + ).joinToString(",") + if (entries.isNotEmpty()) { + val updated = + replace( + "name: \"$name\",\n", + "name: \"$name\",\n\tplatforms: [$entries],\n" + ) + File(swiftBuildDir, "Package.swift").writeText(updated) + } + } + } +} From 497bb7a7f1f21fa721fbbf546de65a7db2474267 Mon Sep 17 00:00:00 2001 From: frankois Date: Sun, 24 Nov 2024 09:15:56 +0100 Subject: [PATCH 15/20] simplify content check replace indexOfFirst with contains --- .../ttypic/swiftklib/gradle/SwiftPackageDependency.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt index 1969d9f..8cfb80e 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt @@ -18,7 +18,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null, ) : SwiftPackageDependency { init { - require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } + require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } require(path.exists()) { "Package path must exist: $path" } } } @@ -34,7 +34,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } + require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } @@ -49,7 +49,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } + require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(from.isNotBlank()) { "From version cannot be blank" } require(to.isNotBlank()) { "To version cannot be blank" } @@ -63,7 +63,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } + require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(branchName.isNotBlank()) { "Branch name cannot be blank" } } @@ -76,7 +76,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && name.indexOfFirst { it.isBlank() } == -1) { "Package name cannot be blank" } + require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } From 4bf80d3acc18471ff1cef8fa1c7ca5240309b6c4 Mon Sep 17 00:00:00 2001 From: frankois Date: Sun, 24 Nov 2024 09:43:12 +0100 Subject: [PATCH 16/20] remove bad copy/paste --- .../io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index c652f14..fc8190f 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -134,7 +134,7 @@ abstract class CompileSwiftTask @Inject constructor( if (printDebug) { logger.warn("======== Package.swift contents ========") logger.warn(File(swiftBuildDir, "Package.swift").readText()) - logger.warn("=addPlatformBlock======= | Package.swift contents | ========") + logger.warn("======== | Package.swift contents | ========") } } From 389eeb4c4f6552d48d3bb91d9ac010c7444ff7ba Mon Sep 17 00:00:00 2001 From: frankois Date: Sun, 24 Nov 2024 15:24:02 +0100 Subject: [PATCH 17/20] use none as check if the List is not blank --- .../ttypic/swiftklib/gradle/SwiftPackageDependency.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt index 8cfb80e..491cea3 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageDependency.kt @@ -18,7 +18,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null, ) : SwiftPackageDependency { init { - require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.none { it.isBlank() }) { "Package name cannot be blank" } require(path.exists()) { "Package path must exist: $path" } } } @@ -34,7 +34,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.none { it.isBlank() }) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } @@ -49,7 +49,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.none { it.isBlank() }) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(from.isNotBlank()) { "From version cannot be blank" } require(to.isNotBlank()) { "To version cannot be blank" } @@ -63,7 +63,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.none { it.isBlank() }) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(branchName.isNotBlank()) { "Branch name cannot be blank" } } @@ -76,7 +76,7 @@ internal sealed interface SwiftPackageDependency : Serializable { @Input @get:Optional override val packageName: String? = null ) : Remote { init { - require(name.isNotEmpty() && !name.contains("")) { "Package name cannot be blank" } + require(name.isNotEmpty() && name.none { it.isBlank() }) { "Package name cannot be blank" } require(url.isNotBlank()) { "URL cannot be blank" } require(version.isNotBlank()) { "Version cannot be blank" } } From a6c6ab9cc106c3b5ec90c8066113f2cc8ef5de40 Mon Sep 17 00:00:00 2001 From: frankois Date: Tue, 26 Nov 2024 10:53:35 +0100 Subject: [PATCH 18/20] build the manifest from a template move from the CLI to String template toolsVersion is by default in version 5.6 and not the current compiler version The current template has been tested ion 5.6 and could not work in the later/earlier version if specified by the user. --- .../gradle/SwiftPackageModulesTest.kt | 6 +- .../gradle/fixture/SwiftKlibTestFixture.kt | 7 +- .../swiftklib/gradle/SwiftKlibEntryImpl.kt | 4 +- .../swiftklib/gradle/api/SwiftKlibEntry.kt | 2 +- .../swiftklib/gradle/task/CompileSwiftTask.kt | 11 +- .../gradle/templates/CreatePackageSwift.kt | 256 ++++++------------ 6 files changed, 90 insertions(+), 196 deletions(-) diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt index f506bea..135ec6f 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/SwiftPackageModulesTest.kt @@ -464,7 +464,7 @@ class SwiftPackageModulesTest { """.trimIndent()) ) .withConfiguration { - toolsVersion = "5.3" + toolsVersion = "100.0" dependencies { } } @@ -474,9 +474,9 @@ class SwiftPackageModulesTest { val result = buildAndFail(fixture.gradleProject.rootDir, "build") // Then - assertThat(result).output().contains("package manifest version 5.3.0 is too old") + assertThat(result).output().contains("is using Swift tools version 100.0.0") getManifestContent(fixture) { manifest -> - assertTrue(manifest.contains("swift-tools-version:5.3")) + assertTrue(manifest.contains("swift-tools-version: 100.0"), "must contains version 100.0") } } diff --git a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt index 12b073c..b1d2264 100644 --- a/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt +++ b/plugin/src/functionalTest/kotlin/io/github/ttypic/swiftklib/gradle/fixture/SwiftKlibTestFixture.kt @@ -140,7 +140,7 @@ abstract class SwiftKlibTestFixture private constructor( if (entry._minWatchos.hasValue()) { appendLine(" minWatchos = \"${entry.minWatchos}\"") } - if (!entry._toolsVersions.isNullOrEmpty()) { + if (entry._toolsVersions.hasValue()) { appendLine(" toolsVersion = \"${entry.toolsVersion}\"") } @@ -197,15 +197,14 @@ private class TestSwiftKlibEntryImpl : SwiftKlibEntry { val _minMacos = notNull() val _minTvos = notNull() val _minWatchos = notNull() - val _toolsVersions: String? - get() = toolsVersion + val _toolsVersions = notNull() override var path: File by _path override var minIos: String by _minIos override var minMacos: String by _minMacos override var minTvos: String by _minTvos override var minWatchos: String by _minWatchos - override var toolsVersion: String? = null + override var toolsVersion: String by _toolsVersions val dependencies = mutableListOf() diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt index 9333a8e..493ce66 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt @@ -16,7 +16,7 @@ internal abstract class SwiftKlibEntryImpl @Inject constructor( ) : SwiftKlibEntry { val _path: Property = objects.property(File::class.java) val _packageName: Property = objects.property(String::class.java) - val _toolsVersion: Property = objects.property(String::class.java) + val _toolsVersion: Property = objects.property(String::class.java).convention("5.6") val _minIos: Property = objects.property(String::class.java).convention("12.0") val _minMacos: Property = objects.property(String::class.java).convention("10.13") val _minTvos: Property = objects.property(String::class.java).convention("12.0") @@ -26,7 +26,7 @@ internal abstract class SwiftKlibEntryImpl @Inject constructor( override var minMacos: String by _minMacos.bind() override var minTvos: String by _minTvos.bind() override var minWatchos: String by _minWatchos.bind() - override var toolsVersion: String? by _toolsVersion.bind() + override var toolsVersion: String by _toolsVersion.bind() internal val dependencyHandler = SwiftPackageConfigurationImpl(objects) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt index 1840f47..d037a66 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/api/SwiftKlibEntry.kt @@ -9,7 +9,7 @@ interface SwiftKlibEntry { var minMacos: String var minTvos: String var minWatchos: String - var toolsVersion: String? + var toolsVersion: String fun packageName(name: String) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index fc8190f..2b27ead 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -34,7 +34,7 @@ abstract class CompileSwiftTask @Inject constructor( @Optional @Input val minMacosProperty: Property, @Optional @Input val minTvosProperty: Property, @Optional @Input val minWatchosProperty: Property, - @Optional @Input val toolsVersionProperty: Property, + @Optional @Input val toolsVersionProperty: Property, ) : DefaultTask() { @get:Optional @@ -86,7 +86,7 @@ abstract class CompileSwiftTask @Inject constructor( private val minMacos get() = minMacosProperty.getOrElse("10.13") private val minTvos get() = minTvosProperty.getOrElse("12.0") private val minWatchos get() = minWatchosProperty.getOrElse("4.0") - private val toolsVersion get() = toolsVersionProperty.getOrElse("") + private val toolsVersion get() = toolsVersionProperty.getOrElse("5.6") /** * Creates build directory or cleans up if it already exists @@ -120,20 +120,19 @@ abstract class CompileSwiftTask @Inject constructor( } private fun createPackageSwift(dependencies: List) { - createPackageSwiftContents( + val manifest = createPackageSwiftContents( cinteropName, dependencies, - execOperations, - swiftBuildDir, minIos, minMacos, minTvos, minWatchos, toolsVersion ) + File(swiftBuildDir, "Package.swift").writeText(manifest) if (printDebug) { logger.warn("======== Package.swift contents ========") - logger.warn(File(swiftBuildDir, "Package.swift").readText()) + logger.warn(manifest) logger.warn("======== | Package.swift contents | ========") } } diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt index 37dff98..55da262 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/templates/CreatePackageSwift.kt @@ -7,207 +7,103 @@ import java.io.File internal fun createPackageSwiftContents( cinteropName: String, dependencies: Collection, - execOperations: ExecOperations, - swiftBuildDir: File, minIos: String, minMacos: String, minTvos: String, minWatchos: String, - toolsVersion: String?, -) { - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "init", - "--name", - cinteropName, - "--type", - "empty", - "--disable-xctest", - "--disable-swift-testing" - ) - }.run { - if (exitValue != 0) { - throw RuntimeException("Failed to init Swift Package") - } - } - - if (!toolsVersion.isNullOrEmpty()) { - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "tools-version", - "--set", - toolsVersion - ) - }.run { - if (exitValue != 0) { - throw RuntimeException("Failed to set the tool version $toolsVersion") - } - } - } - - dependencies.forEach { dependency -> - if (dependency is SwiftPackageDependency.Local) { - addDependencyBlockIfNeeded(cinteropName, swiftBuildDir) - val escapePath = dependency.path.absolutePath.replace("/", "\\/") - execOperations.exec { - it.executable = "sed" - it.workingDir = swiftBuildDir - it.args = listOf( - "-i", - "''", - "/dependencies: \\[/,/]/ s/]/ \\n\\t.package(path: \"${escapePath}\"),\\n]/", - "Package.swift" - ) - it.isIgnoreExitValue = true - } - } else { - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.args = listOf("package", "add-dependency") + dependency.toSwiftArgs() - it.isIgnoreExitValue = true - } - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package dependency $dependency", - ) - } - } - } - addPlatformBlock(cinteropName, swiftBuildDir, minIos, minMacos, minTvos, minWatchos) - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.args = listOf( - "package", - "add-target", - "--path", - cinteropName, - cinteropName - ) - it.isIgnoreExitValue = true - }.run { - if (exitValue != 0) { - throw RuntimeException("Failed to add Swift Package target $cinteropName") - } - } - - dependencies.forEach { dependency -> - dependency.name.forEach { library -> - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "add-target-dependency", - library, - "--package", - dependency.packageName ?: library, - cinteropName, - ) - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package target dependency $cinteropName - package = ${dependency.packageName ?: library}:$library", - ) - } - } - } - - } + toolsVersion: String, +): String = """ + // swift-tools-version: $toolsVersion + import PackageDescription + + let package = Package( + name: "$cinteropName", + ${getPlatformBlock(minIos, minMacos, minTvos, minWatchos)}, + products: [ + .library( + name: "$cinteropName", + type: .static, + targets: ${getProductsTargets(cinteropName)}) + ], + dependencies: [ + ${getDependencies(dependencies)} + ], + targets: [ + .target( + name: "$cinteropName", + dependencies: [ + ${getDependenciesTargets(dependencies)} + ], + path: "$cinteropName") + ] + ) +""".trimIndent() + +private fun getPlatformBlock( + minIos: String, + minMacos: String, + minTvos: String, + minWatchos: String +): String { + val entries = listOfNotNull( + ".iOS(\"$minIos\")".takeIf { minIos.isNotEmpty() }, + ".macOS(\"$minMacos\")".takeIf { minMacos.isNotEmpty() }, + ".tvOS(\"$minTvos\")".takeIf { minTvos.isNotEmpty() }, + ".watchOS(\"$minWatchos\")".takeIf { minWatchos.isNotEmpty() }, + ).joinToString(",") + return "platforms: [$entries]" +} - execOperations.exec { - it.executable = "swift" - it.workingDir = swiftBuildDir - it.isIgnoreExitValue = true - it.args = listOf( - "package", - "add-product", - "--targets", - cinteropName, - "--type", - "static-library", - cinteropName - ) - }.run { - if (exitValue != 0) { - throw RuntimeException( - "Failed to add Swift Package library $cinteropName", - ) +private fun getDependencies(dependencies: Collection): String { + return buildList { + dependencies.forEach { dependency -> + add(dependency.toSwiftPackageDependencyDeclaration()) } - } - + }.joinToString(",") } - -internal fun SwiftPackageDependency.toSwiftArgs(): List = when (this) { +private fun SwiftPackageDependency.toSwiftPackageDependencyDeclaration(): String = when (this) { is SwiftPackageDependency.Local -> - emptyList() + """ + .package(path: "${path.absolutePath}") + """.trimIndent() is SwiftPackageDependency.Remote.ExactVersion -> - listOf(url, "--exact", version) + """ + .package(url: "$url", exact: "$version") + """.trimIndent() is SwiftPackageDependency.Remote.VersionRange -> { - if (inclusive) { - // can't do inclusive range from command line - // but I think it's better to use up-to-next-minor-from - // it needs to be rethink - listOf(url, "--up-to-next-minor-from", from) - } else { - listOf(url, "--from", from, "--to", to) - } + val operator = if (inclusive) "..." else "..<" + """ + .package(url: "$url", "$from"$operator"$to") + """.trimIndent() } is SwiftPackageDependency.Remote.Branch -> - listOf(url, "--branch", branchName) + """ + .package(url: "$url", branch: "$branchName") + """.trimIndent() is SwiftPackageDependency.Remote.FromVersion -> - listOf(url, "--from", version) + """ + .package(url: "$url", from: "$version") + """.trimIndent() } -private fun addDependencyBlockIfNeeded(name: String, swiftBuildDir: File) { - File(swiftBuildDir, "Package.swift").readText().run { - if (!contains("dependencies:")) { - val updated = replace("name: \"$name\"", "name: \"$name\",\n\tdependencies: []") - File(swiftBuildDir, "Package.swift").writeText(updated) - } - } -} -private fun addPlatformBlock( - name: String, - swiftBuildDir: File, - minIos: String, - minMacos: String, - minTvos: String, - minWatchos: String -) { - File(swiftBuildDir, "Package.swift").readText().run { - if (!contains("platforms:")) { - val entries = listOfNotNull( - ".iOS(\"$minIos\")".takeIf { minIos.isNotEmpty() }, - ".macOS(\"$minMacos\")".takeIf { minMacos.isNotEmpty() }, - ".tvOS(\"$minTvos\")".takeIf { minTvos.isNotEmpty() }, - ".watchOS(\"$minWatchos\")".takeIf { minWatchos.isNotEmpty() }, - ).joinToString(",") - if (entries.isNotEmpty()) { - val updated = - replace( - "name: \"$name\",\n", - "name: \"$name\",\n\tplatforms: [$entries],\n" - ) - File(swiftBuildDir, "Package.swift").writeText(updated) +private fun getDependenciesTargets( + dependencies: Collection +): String { + return buildList { + dependencies.forEach { dependency -> + dependency.name.forEach { library -> + add(".product(name: \"${library}\", package: \"${dependency.packageName ?: library}\")") } } - } + }.joinToString(",") +} + +private fun getProductsTargets(cinteropName: String): String { + return "[\"$cinteropName\"]" } From 2d3a724596ba546a9d9f4e4a191aa79c1e3d8c88 Mon Sep 17 00:00:00 2001 From: frankois Date: Tue, 26 Nov 2024 11:00:46 +0100 Subject: [PATCH 19/20] update tools version default version: 5.9 --- .../io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt index 493ce66..636f356 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/SwiftKlibEntryImpl.kt @@ -16,7 +16,7 @@ internal abstract class SwiftKlibEntryImpl @Inject constructor( ) : SwiftKlibEntry { val _path: Property = objects.property(File::class.java) val _packageName: Property = objects.property(String::class.java) - val _toolsVersion: Property = objects.property(String::class.java).convention("5.6") + val _toolsVersion: Property = objects.property(String::class.java).convention("5.9") val _minIos: Property = objects.property(String::class.java).convention("12.0") val _minMacos: Property = objects.property(String::class.java).convention("10.13") val _minTvos: Property = objects.property(String::class.java).convention("12.0") From 569e339ef95799cc6d093e915b5b8d9239e3434d Mon Sep 17 00:00:00 2001 From: frankois Date: Tue, 26 Nov 2024 11:09:40 +0100 Subject: [PATCH 20/20] update tools version default version: 5.9 --- .../io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt index 2b27ead..998478c 100644 --- a/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt +++ b/plugin/src/main/kotlin/io/github/ttypic/swiftklib/gradle/task/CompileSwiftTask.kt @@ -86,7 +86,7 @@ abstract class CompileSwiftTask @Inject constructor( private val minMacos get() = minMacosProperty.getOrElse("10.13") private val minTvos get() = minTvosProperty.getOrElse("12.0") private val minWatchos get() = minWatchosProperty.getOrElse("4.0") - private val toolsVersion get() = toolsVersionProperty.getOrElse("5.6") + private val toolsVersion get() = toolsVersionProperty.getOrElse("5.9") /** * Creates build directory or cleans up if it already exists