From cf57c26619a74c02d7237772fa4360fba46abdd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 10 Jan 2025 17:21:59 +0100 Subject: [PATCH] Exporting and beyond (#24) Better support for importing your own code and third dependencies to your Kotlin Better performance and Caching A full working example for using the plugin with iOS --- .../workflows/gradle-wrapper-validation.yml | 2 +- .github/workflows/pre-merge.yaml | 9 +- .github/workflows/publish-plugin.yaml | 10 +- .gitignore | 5 + README.md | 154 +++---- example/build.gradle.kts | 33 +- example/iosApp/GoogleService-Info.plist | 30 ++ .../iosApp/iosApp.xcodeproj/project.pbxproj | 417 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/swiftpm/Package.resolved | 123 ++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 ++++ .../iosApp/Assets.xcassets/Contents.json | 6 + example/iosApp/iosApp/ContentView.swift | 16 + example/iosApp/iosApp/Info.plist | 48 ++ .../Preview Assets.xcassets/Contents.json | 6 + example/iosApp/iosApp/iOSApp.swift | 26 ++ .../kotlin/com/example/Platform.ios.kt | 21 + .../src/iosMain/kotlin/com/example/Test.kt | 14 - .../src/iosTest/kotlin/com/example/Test.kt | 12 + .../{Test.swift => SpmForSwiftSource.swift} | 7 + plugin-build/build.gradle.kts | 1 - plugin-build/gradle.properties | 2 +- .../frankois944/spmForKmp/BasicPackageTest.kt | 5 +- .../spmForKmp/ComplexPackageTest.kt | 8 +- .../frankois944/spmForKmp/LocalPackageTest.kt | 14 +- .../Package.swift | 8 +- .../LocalSourceDummyFramework.swift} | 4 +- .../frankois944/spmForKmp/SpmForKmpPlugin.kt | 45 +- .../PackageImplicitDependencies.kt | 6 +- .../spmForKmp/manifest/Template.kt | 9 +- .../spmForKmp/operations/XcodeOperations.kt | 266 ++++++----- .../tasks/CompileSwiftPackageTask.kt | 17 +- .../tasks/GenerateCInteropDefinitionTask.kt | 117 +++-- .../tasks/GenerateExportableManifestTask.kt | 90 ++++ .../spmForKmp/tasks/GenerateManifestTask.kt | 13 +- .../spmForKmp/utils/InjectedExecOps.kt | 9 + .../spmForKmp/xcodeconfig/ModuleConfig.kt | 10 + 38 files changed, 1326 insertions(+), 353 deletions(-) create mode 100644 example/iosApp/GoogleService-Info.plist create mode 100644 example/iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 example/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 example/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 example/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 example/iosApp/iosApp/Assets.xcassets/Contents.json create mode 100644 example/iosApp/iosApp/ContentView.swift create mode 100644 example/iosApp/iosApp/Info.plist create mode 100644 example/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 example/iosApp/iosApp/iOSApp.swift delete mode 100644 example/src/iosMain/kotlin/com/example/Test.kt rename example/src/swift/{Test.swift => SpmForSwiftSource.swift} (62%) rename plugin-build/plugin/src/functionalTest/resources/{LocalDummyFramework => LocalSourceDummyFramework}/Package.swift (75%) rename plugin-build/plugin/src/functionalTest/resources/{LocalDummyFramework/Sources/LocalDummyFramework/LocalDummyFramework.swift => LocalSourceDummyFramework/Sources/LocalSourceDummyFramework/LocalSourceDummyFramework.swift} (60%) rename plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/{dependency => }/PackageImplicitDependencies.kt (94%) create mode 100644 plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateExportableManifestTask.kt create mode 100644 plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/utils/InjectedExecOps.kt create mode 100644 plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/xcodeconfig/ModuleConfig.kt diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index a5317c4..1b6012b 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout latest code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@v4 # v4 - name: Validate Gradle Wrapper uses: gradle/actions/wrapper-validation@v4 diff --git a/.github/workflows/pre-merge.yaml b/.github/workflows/pre-merge.yaml index 731e0f6..048b05e 100644 --- a/.github/workflows/pre-merge.yaml +++ b/.github/workflows/pre-merge.yaml @@ -46,8 +46,10 @@ jobs: uses: actions/cache@v4 with: path: | - example/build/spmKmpPlugin/input/scratch - example/build/spmKmpPlugin/input/Package.resolved + example/build/spmKmpPlugin/scratch + example/build/spmKmpPlugin/Package.resolved + example/iosApp/build + example/iosApp/spm key: ${{ runner.os }}-example-scratch - name: Cache Gradle Caches uses: gradle/actions/setup-gradle@v4 @@ -65,6 +67,9 @@ jobs: with: report_paths: '**/build/test-results/**/TEST-*.xml' include_passed: true + - name: build iosExampleApp + run: xcodebuild -project iosApp.xcodeproj -scheme iosApp -configuration Debug -destination 'generic/platform=iOS Simulator' ARCHS=arm64 -derivedDataPath "./build" -clonedSourcePackagesDirPath "./spm" build + working-directory: "example/iosApp" - name: Upload coverage to Codecov if: ${{ !cancelled() }} uses: codecov/codecov-action@v5 diff --git a/.github/workflows/publish-plugin.yaml b/.github/workflows/publish-plugin.yaml index 5d1f4ce..987ed30 100644 --- a/.github/workflows/publish-plugin.yaml +++ b/.github/workflows/publish-plugin.yaml @@ -17,7 +17,7 @@ jobs: if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} steps: - name: Checkout Repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@v4 # v4 - name: Cache Gradle Caches uses: gradle/actions/setup-gradle@v4 - name: Cache Konan @@ -32,14 +32,6 @@ jobs: with: path: /Users/runner/work/spm4Kmp/spm4Kmp/example/build/spmKmpPlugin/input/scratch key: ${{ runner.os }}-example-scratch - - name: Run Gradle tasks - run: ./gradlew preMerge --continue - - name: Archive test results - uses: actions/upload-artifact@v4 - if: success() || failure() - with: - name: test-pre-merge-report - path: plugin-build/plugin/build/reports/ - name: Publish on Plugin Portal run: ./gradlew :plugin-build:plugin:setupPluginUploadFromEnvironment :plugin-build:plugin:publishPlugins if: success() diff --git a/.gitignore b/.gitignore index ee7cf11..9f26dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,8 @@ build plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/.build .kotlin +example/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata +example/iosApp/iosApp.xcodeproj/xcuserdata +example/exportedNativeShared +example/iosApp/spm +plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/.build diff --git a/README.md b/README.md index 3578767..bc7e161 100644 --- a/README.md +++ b/README.md @@ -3,38 +3,27 @@ [![GitHub Release](https://img.shields.io/github/v/release/frankois944/spm4Kmp)](https://github.com/frankois944/spm4Kmp/releases/) [![build&tests](https://github.com/frankois944/spm4Kmp/actions/workflows/pre-merge.yaml/badge.svg)](https://github.com/frankois944/spm4Kmp/actions/workflows/pre-merge.yaml) [![codecov](https://codecov.io/gh/frankois944/spm4Kmp/graph/badge.svg?token=OXEHFLQG1I)](https://codecov.io/gh/frankois944/spm4Kmp) -![GitHub License](https://img.shields.io/github/license/frankois944/spm4kmp) +[![GitHub License](https://img.shields.io/github/license/frankois944/spm4kmp)](https://github.com/frankois944/spm4Kmp/blob/main/LICENSE) -The Swift Package Manager for Kotlin multiplatform Plugin aka `spmForKmp` gradle plugin is a Gradle plugin designed to simplify integrating Swift Package Manager (SPM) dependencies into Kotlin Multiplatform (KMP) projects. It allows you to effortlessly configure and use Swift packages in your Kotlin projects targeting Apple platforms, such as iOS. +The Swift Package Manager for Kotlin multiplatform Plugin aka `spmForKmp` gradle plugin is a Gradle plugin designed to simplify integrating Swift Package Manager (SPM) dependencies into Kotlin Multiplatform (KMP) projects. It allows you to (almost) effortlessly configure and use Swift packages in your Kotlin projects targeting Apple platforms, such as iOS. --- ## Feedback -This project greatly needs feedback and get information about the edge case for progressing; the discussion tab is welcomed. +This project greatly needs feedback and information about the edge case for progressing; the discussion tab is welcomed. ## Features -- **Support for SPM Dependencies**: Seamlessly add remote SPM dependencies to your KMP modules. -- **KMP Compatibility**: Configure Swift packages for iOS and other Apple targets. -- **Export Dependencies to Kotlin**: Enable specific SPM dependencies to be exposed directly in your Kotlin code (if compatible). +- **Create a bridge easily**: Import your own Swift code for functionality can't be done in Kotlin for example. +- **Import Swift compatible code to Kotlin**: Enable **SPM dependencies** and your **own Swift code** to be exposed directly in your Kotlin code (if compatible). - **Automatic CInterop Configuration**: Simplify the process of creating native CInterop definitions for your Swift packages with dependencies. --- -## Prerequisites - -Before using the `spmForKmp` plugin, ensure the following: - -1. Your project is set up as a Kotlin Multiplatform (KMP) project. -2. You are using the Gradle build system. -3. You have added Apple targets (e.g., `iosArm64`, `iosSimulatorArm64`, etc.) to your KMP project. - ---- - ## Getting Started -Follow these steps to add and configure the `spmForKmp` plugin in your project: +A fully working sample is [available](https://github.com/frankois944/spm4Kmp/tree/main/example) as a playground. ### 1. Apply the Plugin @@ -44,7 +33,7 @@ Add the plugin to your `build.gradle.kts` or the appropriate Gradle module’s ` ```kotlin plugins { id("org.jetbrains.kotlin.multiplatform") - id("io.github.frankois944.spmForKmp").version("0.0.3") // Apply the spmForKmp plugin + id("io.github.frankois944.spmForKmp").version("0.0.5") // Apply the spmForKmp plugin } ``` @@ -58,8 +47,6 @@ kotlin.mpp.enableCInteropCommonization=true ### 2. Configure Kotlin Multiplatform Targets -Make sure you define your Kotlin Multiplatform targets for Apple. Here is an example configuration: - ```kotlin kotlin { listOf( @@ -77,107 +64,59 @@ kotlin { swiftPackageConfig { create("nativeExample") { // same name as the one in `cinterops.create("...")` - } -} -``` - ---- - -### 3. Configure Swift Package Dependencies Plugin - -To use Swift Package Manager (SPM) dependencies in your project, define them in your `swiftPackageConfig` block. - -You have many options to configure the plugin, some examples hereafter. - -### 3.1 With no external dependencies - -The following example creates a `nativeExample` Kotlin module with the content of the `src/swift` folder, it's the default behavior. - -The content of `src/swift` is optional and will be replaced with a dummy Swift class, so you can only declare the dependencies. - -```kotlin -swiftPackageConfig { - create("nativeExample") { // same name as the one in `cinterops.create("...")` - // optional content - } -} -``` - -### 3.2 With external dependencies - -The following example creates a `nativeExample` Kotlin module with the content of the `src/swift` folder and the declared dependencies. - -- `CryptoSwift` is a Swift package that will be used in the Swift code. -- `firebase-ios-sdk` is a ObjC compatible package that can be used in the Swift code and in the Kotlin code. - -The `exportToKotlin` parameter is used to export the compatible code of the package to Kotlin for use in shared Kotlin code. - -By default, the package is not exported to Kotlin. - -```kotlin -swiftPackageConfig { - create("nativeExample") { // same name as the one in `cinterops.create("...")` - customPackageSourcePath = "src/nativeExample" // (Optional) Custom path for your own Swift source files + // add your own swift code and/or your external dependencies, it's optional dependency( - // available only in the Swift code + // available only in your own Swift code SwiftDependency.Package.Remote.Version( - url = "https://github.com/krzyzanowskim/CryptoSwift.git", // Repository URL - names = listOf("CryptoSwift"), // Library names - version = "1.8.4", // Package version + url = "https://github.com/krzyzanowskim/CryptoSwift.git", // Repository URL + names = listOf("CryptoSwift"), // Library names + version = "1.8.4", // Package version ) ) dependency( - // available in the Swift and kolin code + // available in the Swift and Kolin code SwiftDependency.Package.Remote.Version( url = "https://github.com/firebase/firebase-ios-sdk.git", // Repository URL names = listOf("FirebaseAnalytics", "FirebaseCore"), // Libraries from the package packageName = "firebase-ios-sdk", // (Optional) Package name, can be required in some cases version = "11.6.0", // Package version - exportToKotlin = true // Export to Kotlin for use in shared Kotlin code + exportToKotlin = true // Export to Kotlin for usage in shared Kotlin code ) ) - // and more Swift packages... } } ``` -### 3.3 Supported Swift Package Types +### 2.1. Supported Swift Package Dependency Types The plugin supports the following configurations : - **Remote Package**: A Swift package hosted on a remote repository using version, commit, or branch. - **Local Package**: A Swift package located in a local directory. -- **Local Binary Package**: A xcFramework in a local directory. -- **Remote Binary Package**: A xcFramework hosted on a remote repository. +- **Local Binary xcFramework**: A xcFramework in a local directory. +- **Remote Binary xcFramework**: A xcFramework hosted on a remote repository. For more information, refer to the [SwiftDependency](https://github.com/frankois944/spm4Kmp/blob/main/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/definition/SwiftDependency.kt) file. --- -### 4. Add your own Swift code +### 3. Add your own Swift code You can now add your own Swift code in the `src/swift` folder. -```swift -import Foundation -// inside the folder src/swift -// everything will be automatically accessible from your Kotlin code -@objcMembers public class MySwiftDummyClass: NSObject { - func mySwiftDummyFunction() -> String { - return "Hello from Swift!" - } -} -``` - -```kotlin -package com.example -import nativeExample.MySwiftDummyClass -@kotlinx.cinterop.ExperimentalForeignApi -val dummyClass = MySwiftDummyClass() -``` +> [!IMPORTANT] +> Your swift code need to be mark as [@objc/@objcMembers](https://akdebuging.com/posts/what-is-objc-and-objcmember/) and the visibility set as `public` +> or it won't be exported and available from your Kotlin code +> ```swift +> @objcMembers public class MyOwnSwiftCode: NSObject { +> public func exportedMethod() -> String { +> return "value" +> } +> } +> ``` -### 5. Add your own Swift code and uses the dependencies +### 3.1. With external dependencies -You can also use the Swift packages you have added in the `swiftPackageConfig` block in your Swift code. +You can also use the dependency you have added in the `swiftPackageConfig` block in your Swift code. For example, `CryptoSwift` is not a library that can be used directly in Kotlin code, but you can create a bridge in your Swift code. @@ -207,16 +146,37 @@ import CryptoSwift } ``` +### 3.2. Export your dependency directly to your Kotlin Code + +You can also use the dependency you have added in the `swiftPackageConfig` in your Kotlin and Swift applications. + +> [!WARNING] +> This feature is highly experimental + ```kotlin -package com.example -import dummy.MySwiftClass +swiftPackageConfig { + create("dummy") { + dependency( + SwiftDependency.Binary.Local( + path = "path/to/DummyFramework.xcframework.zip", + packageName = "DummyFramework", + exportToKotlin = true, // by default false + ), + ) + } +} ``` ---- - -Please take a look at the functional tests in the [plugin-test](https://github.com/frankois944/spm4Kmp/tree/main/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp) folder for more examples. +> [!IMPORTANT] +> When exporting dependency, some configuration need to be added to your xcode project. +> +> A local swift package is being generated during the build and this message diplayed +> ``` +> Spm4Kmp: A local Swift package has been generated in /path/to/the/local/package +> Please add it to your project as a local package dependency. +> ``` +> Add the folder to your project as a Local package, that's all. -The `example` module of this repository is a playground where you can try to use the plugin --- diff --git a/example/build.gradle.kts b/example/build.gradle.kts index 3b15671..7a11a6c 100644 --- a/example/build.gradle.kts +++ b/example/build.gradle.kts @@ -42,6 +42,21 @@ kotlin { } } +val copyTestResources = + tasks.register("copyTestResources") { + from( + "${layout.projectDirectory.asFile.path}/../plugin-build/plugin/src/functionalTest/resources" + + "/DummyFramework.xcframework/ios-arm64_x86_64-simulator/", + ) { + include("*.framework/**") + } + into("${layout.projectDirectory.asFile.path}/build/bin/iosSimulatorArm64/debugTest/Frameworks/") + } + +tasks.named("iosSimulatorArm64Test") { + dependsOn(copyTestResources) +} + android { namespace = "com.example" compileSdk = 34 @@ -53,7 +68,7 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } } - +val testRessources = "${layout.projectDirectory.asFile.path}/../plugin-build/plugin/src/functionalTest/resources" swiftPackageConfig { create("nativeShared") { // optional parameters @@ -74,7 +89,11 @@ swiftPackageConfig { // Repository URL url = "https://github.com/firebase/firebase-ios-sdk.git", // Libraries from the package - names = listOf("FirebaseAnalytics", "FirebaseCore"), + names = + listOf( + "FirebaseCore", + "FirebaseAnalytics", + ), // (Optional) Package name, can be required in some cases packageName = "firebase-ios-sdk", // Package version @@ -82,6 +101,16 @@ swiftPackageConfig { // Export to Kotlin for use in shared Kotlin code, false by default exportToKotlin = true, ), + SwiftDependency.Binary.Local( + path = "$testRessources/DummyFramework.xcframework.zip", + packageName = "DummyFramework", + exportToKotlin = true, + ), + SwiftDependency.Package.Local( + path = "$testRessources/LocalSourceDummyFramework", + packageName = "LocalSourceDummyFramework", + exportToKotlin = true, + ), SwiftDependency.Package.Remote.Version( url = "https://github.com/krzyzanowskim/CryptoSwift.git", names = listOf("CryptoSwift"), diff --git a/example/iosApp/GoogleService-Info.plist b/example/iosApp/GoogleService-Info.plist new file mode 100644 index 0000000..a1fd9eb --- /dev/null +++ b/example/iosApp/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyCfrkiVxg1CHk8FwdMIOP8dWcWa4BMR7YQ + GCM_SENDER_ID + 986130107097 + PLIST_VERSION + 1 + BUNDLE_ID + fr.frankois944.kmp.smp4kmp.example + PROJECT_ID + fir-kmpdemo-6cde4 + STORAGE_BUCKET + fir-kmpdemo-6cde4.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:986130107097:ios:bc74692aa1cda1762fccc8 + + diff --git a/example/iosApp/iosApp.xcodeproj/project.pbxproj b/example/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0155246 --- /dev/null +++ b/example/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,417 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 071933DE2D3008430094EBEA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 071933DD2D3008420094EBEA /* GoogleService-Info.plist */; }; + 077FEBFD2D312EB9000D5E8C /* exportedNativeShared in Frameworks */ = {isa = PBXBuildFile; productRef = 077FEBFC2D312EB9000D5E8C /* exportedNativeShared */; }; + 077FEC002D3132EA000D5E8C /* exportedNativeShared in Frameworks */ = {isa = PBXBuildFile; productRef = 077FEBFF2D3132EA000D5E8C /* exportedNativeShared */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 071933DD2D3008420094EBEA /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 078E73F42D2FD23C00B49156 /* SpmForSwiftSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpmForSwiftSource.swift; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7555FF78242A565900829871 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 077FEBFD2D312EB9000D5E8C /* exportedNativeShared in Frameworks */, + 077FEC002D3132EA000D5E8C /* exportedNativeShared in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 078E73F52D2FD23C00B49156 /* swift */ = { + isa = PBXGroup; + children = ( + 078E73F42D2FD23C00B49156 /* SpmForSwiftSource.swift */, + ); + name = swift; + path = ../src/swift; + sourceTree = SOURCE_ROOT; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + 078E73F52D2FD23C00B49156 /* swift */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 7555FFB0242A642200829871 /* Frameworks */, + 071933DD2D3008420094EBEA /* GoogleService-Info.plist */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* iosApp.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + 7555FFB0242A642200829871 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + 7555FFB5242A651A00829871 /* ShellScript */, + 7555FF77242A565900829871 /* Sources */, + 7555FF78242A565900829871 /* Frameworks */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* iosApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1130; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + 077FEBFE2D3132EA000D5E8C /* XCLocalSwiftPackageReference "../exportedNativeShared" */, + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + 071933DE2D3008430094EBEA /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 7555FFB5242A651A00829871 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"$SRCROOT/../../\"\n./gradlew :example:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + shared, + ); + PRODUCT_BUNDLE_IDENTIFIER = fr.frankois944.kmp.smp4kmp.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + shared, + ); + PRODUCT_BUNDLE_IDENTIFIER = fr.frankois944.kmp.smp4kmp.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 077FEBFE2D3132EA000D5E8C /* XCLocalSwiftPackageReference "../exportedNativeShared" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../exportedNativeShared; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 077FEBFC2D312EB9000D5E8C /* exportedNativeShared */ = { + isa = XCSwiftPackageProductDependency; + productName = exportedNativeShared; + }; + 077FEBFF2D3132EA000D5E8C /* exportedNativeShared */ = { + isa = XCSwiftPackageProductDependency; + productName = exportedNativeShared; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} diff --git a/example/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/example/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..bf04c54 --- /dev/null +++ b/example/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,123 @@ +{ + "originHash" : "cf50dc3ad683b69feffe6f882120d9f27012a681603c581a6ceb1b0a3f13a23f", + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", + "version" : "1.2024011602.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk.git", + "state" : { + "revision" : "2e02253fd1ce99145bcbf1bb367ccf61bd0ca46b", + "version" : "11.6.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "4f234bcbdae841d7015258fbbf8e7743a39b8200", + "version" : "11.4.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "f56d8fc3162de9a498377c7b6cea43431f4f5083", + "version" : "1.65.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "85b7b231882c3c472b8bda4fb495324d3f19bab6", + "version" : "4.2.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "ebc7251dd5b37f627c93698e4374084d98409633", + "version" : "1.28.2" + } + } + ], + "version" : 3 +} diff --git a/example/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/example/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/example/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/example/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..fb88a39 --- /dev/null +++ b/example/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/example/iosApp/iosApp/Assets.xcassets/Contents.json b/example/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/example/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/example/iosApp/iosApp/ContentView.swift b/example/iosApp/iosApp/ContentView.swift new file mode 100644 index 0000000..6a15a09 --- /dev/null +++ b/example/iosApp/iosApp/ContentView.swift @@ -0,0 +1,16 @@ +import SwiftUI +import shared + +struct ContentView: View { + let greet = Greeting().greet() + + var body: some View { + Text(greet) + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} \ No newline at end of file diff --git a/example/iosApp/iosApp/Info.plist b/example/iosApp/iosApp/Info.plist new file mode 100644 index 0000000..8044709 --- /dev/null +++ b/example/iosApp/iosApp/Info.plist @@ -0,0 +1,48 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UILaunchScreen + + + \ No newline at end of file diff --git a/example/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/example/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/example/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/example/iosApp/iosApp/iOSApp.swift b/example/iosApp/iosApp/iOSApp.swift new file mode 100644 index 0000000..066d8d9 --- /dev/null +++ b/example/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,26 @@ +import SwiftUI +import shared +import FirebaseAnalytics +import FirebaseCore + +@main +struct iOSApp: App { + + init() { + print(shared.Platform_iosKt.myNativeClass) + shared.Platform_iosKt.configureFirebase() + print(shared.Platform_iosKt.consentStatusGranted) + print(shared.Platform_iosKt.localSourceDummyTest) + Analytics.logEvent(AnalyticsEventSelectContent, parameters: [ + AnalyticsParameterItemID: "id-", + AnalyticsParameterItemName: "title", + AnalyticsParameterContentType: "cont", + ]) + } + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/example/src/iosMain/kotlin/com/example/Platform.ios.kt b/example/src/iosMain/kotlin/com/example/Platform.ios.kt index 4fec944..df061d0 100644 --- a/example/src/iosMain/kotlin/com/example/Platform.ios.kt +++ b/example/src/iosMain/kotlin/com/example/Platform.ios.kt @@ -1,5 +1,12 @@ +@file:OptIn(ExperimentalForeignApi::class) + package com.example +import FirebaseAnalytics.FIRConsentStatusGranted +import FirebaseCore.FIRApp +import kotlinx.cinterop.ExperimentalForeignApi +import nativeShared.MySwiftDummyClass +import nativeShared.TestClass import platform.UIKit.UIDevice class IOSPlatform : Platform { @@ -8,3 +15,17 @@ class IOSPlatform : Platform { } actual fun getPlatform(): Platform = IOSPlatform() + +val myNativeClass = MySwiftDummyClass().mySwiftDummyFunction() + +@ExperimentalForeignApi +val getSwiftValue = TestClass().getSomeValue() + +@ExperimentalForeignApi +fun configureFirebase() = FIRApp.configure() + +@ExperimentalForeignApi +val consentStatusGranted = FIRConsentStatusGranted + +@ExperimentalForeignApi +val localSourceDummyTest = LocalSourceDummyFramework.LocalSourceDummy().test() diff --git a/example/src/iosMain/kotlin/com/example/Test.kt b/example/src/iosMain/kotlin/com/example/Test.kt deleted file mode 100644 index 8fac2d8..0000000 --- a/example/src/iosMain/kotlin/com/example/Test.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) - -package com.example -import FirebaseAnalytics.FIRAnalytics -import FirebaseCore.FIRApp -import kotlinx.cinterop.ExperimentalForeignApi -import nativeShared.TestClass -import platform.Foundation.version - -@ExperimentalForeignApi -val getSwiftValue = TestClass().getSomeValue() - -val testFirebaseCoreVerion = FIRApp.version() -val testFirebaseAnalyticsVersion = FIRAnalytics.version() diff --git a/example/src/iosTest/kotlin/com/example/Test.kt b/example/src/iosTest/kotlin/com/example/Test.kt index 13c2723..04900e7 100644 --- a/example/src/iosTest/kotlin/com/example/Test.kt +++ b/example/src/iosTest/kotlin/com/example/Test.kt @@ -2,10 +2,12 @@ package com.example +import FirebaseAnalytics.FIRConsentStatusGranted import kotlinx.cinterop.BetaInteropApi import nativeShared.TestClass import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull @OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) class Test { @@ -18,4 +20,14 @@ class Test { fun exampleTest2() { assertEquals("202cb962ac59075b964b07152d234b70", TestClass().getValueFromCrypt()) } + + @Test + fun exampleTest3() { + assertEquals("TEST DUMMY FRAMEWORK", LocalSourceDummyFramework.LocalSourceDummy().test()) + } + + @Test + fun exampleTest4() { + assertNotNull("TEST DUMMY FRAMEWORK", FIRConsentStatusGranted) + } } diff --git a/example/src/swift/Test.swift b/example/src/swift/SpmForSwiftSource.swift similarity index 62% rename from example/src/swift/Test.swift rename to example/src/swift/SpmForSwiftSource.swift index 89e1dcf..896d310 100644 --- a/example/src/swift/Test.swift +++ b/example/src/swift/SpmForSwiftSource.swift @@ -11,3 +11,10 @@ import CryptoSwift return "123".md5() } } + + +@objcMembers public class MySwiftDummyClass: NSObject { + public func mySwiftDummyFunction() -> String { + return "Hello from Swift!" + } +} diff --git a/plugin-build/build.gradle.kts b/plugin-build/build.gradle.kts index 01c1a4d..0ff0ffe 100644 --- a/plugin-build/build.gradle.kts +++ b/plugin-build/build.gradle.kts @@ -54,7 +54,6 @@ tasks.withType().configureEach { tasks.register("clean", Delete::class.java) { delete(rootProject.layout.buildDirectory) - delete("example/build") } tasks.wrapper { diff --git a/plugin-build/gradle.properties b/plugin-build/gradle.properties index 4d7f88c..48480e5 100644 --- a/plugin-build/gradle.properties +++ b/plugin-build/gradle.properties @@ -1,5 +1,5 @@ ID=io.github.frankois944.spmForKmp -VERSION=0.0.4 +VERSION=0.0.5 ARTIFACT_ID=Spm4Kmp GROUP=io.github.frankois944 DISPLAY_NAME=Swift Package Manager for Kotlin Multiplaform diff --git a/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/BasicPackageTest.kt b/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/BasicPackageTest.kt index 5349455..55bb9be 100644 --- a/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/BasicPackageTest.kt +++ b/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/BasicPackageTest.kt @@ -1,7 +1,6 @@ package io.github.frankois944.spmForKmp import com.autonomousapps.kit.GradleBuilder -import com.autonomousapps.kit.GradleBuilder.build import com.autonomousapps.kit.truth.TestKitTruth.Companion.assertThat import io.github.frankois944.spmForKmp.definition.SwiftDependency import io.github.frankois944.spmForKmp.fixture.KotlinSource @@ -54,7 +53,7 @@ class BasicPackageTest : BaseTest() { val cache = File("/tmp/spm4kmp/cache").also { it.deleteRecursively() } val security = File("/tmp/spm4kmp/security").also { it.deleteRecursively() } val config = File("/tmp/spm4kmp/config").also { it.deleteRecursively() } - val localPackageDirectory = File("src/functionalTest/resources/LocalDummyFramework") + val localPackageDirectory = File("src/functionalTest/resources/LocalSourceDummyFramework") // Given val fixture = @@ -76,7 +75,7 @@ class BasicPackageTest : BaseTest() { add( SwiftDependency.Package.Local( path = localPackageDirectory.absolutePath, - packageName = "LocalDummyFramework", + packageName = "LocalSourceDummyFramework", exportToKotlin = true, ), ) diff --git a/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/ComplexPackageTest.kt b/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/ComplexPackageTest.kt index 7889fe2..c0f764d 100644 --- a/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/ComplexPackageTest.kt +++ b/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/ComplexPackageTest.kt @@ -13,7 +13,7 @@ import java.io.File class ComplexPackageTest : BaseTest() { @Test fun `build with multiple packages type`() { - val localPackageDirectory = File("src/functionalTest/resources/LocalDummyFramework") + val localPackageDirectory = File("src/functionalTest/resources/LocalSourceDummyFramework") val xcFrameworkDirectory = File("src/functionalTest/resources/DummyFramework.xcframework") // Given val fixture = @@ -48,7 +48,7 @@ class ComplexPackageTest : BaseTest() { add( SwiftDependency.Package.Local( path = localPackageDirectory.absolutePath, - packageName = "LocalDummyFramework", + packageName = "LocalSourceDummyFramework", exportToKotlin = true, ), ) @@ -75,7 +75,7 @@ class ComplexPackageTest : BaseTest() { package com.example import CryptoSwift.SWIFT_TYPEDEFS import dummy.MySwiftClassEmbedded - import LocalDummyFramework.MySwiftClass + import LocalSourceDummyFramework.LocalSourceDummy """.trimIndent(), ), @@ -88,7 +88,7 @@ class ComplexPackageTest : BaseTest() { import KeychainAccess import Valet import DummyFramework - import LocalDummyFramework + import LocalSourceDummyFramework import FirebaseCore import FirebaseAnalytics import FirebaseCrashlytics diff --git a/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/LocalPackageTest.kt b/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/LocalPackageTest.kt index e214826..7b65866 100644 --- a/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/LocalPackageTest.kt +++ b/plugin-build/plugin/src/functionalTest/kotlin/io/github/frankois944/spmForKmp/LocalPackageTest.kt @@ -13,7 +13,7 @@ import java.io.File class LocalPackageTest : BaseTest() { @Test fun `build with local packages`() { - val localPackageDirectory = File("src/functionalTest/resources/LocalDummyFramework") + val localPackageDirectory = File("src/functionalTest/resources/LocalSourceDummyFramework") // Given val fixture = SmpKMPTestFixture @@ -26,7 +26,7 @@ class LocalPackageTest : BaseTest() { add( SwiftDependency.Package.Local( path = localPackageDirectory.absolutePath, - packageName = "LocalDummyFramework", + packageName = "LocalSourceDummyFramework", exportToKotlin = true, ), ) @@ -36,7 +36,7 @@ class LocalPackageTest : BaseTest() { content = """ package com.example - import LocalDummyFramework.MySwiftClass + import LocalDummyFramework.LocalSourceDummy """.trimIndent(), ), ).withSwiftSources( @@ -44,7 +44,7 @@ class LocalPackageTest : BaseTest() { content = """ import Foundation - import LocalDummyFramework + import LocalSourceDummyFramework @objc public class MySwiftDummyClass: NSObject { } """.trimIndent(), @@ -63,7 +63,7 @@ class LocalPackageTest : BaseTest() { @Test fun `build with local packages and no swift code`() { - val localPackageDirectory = File("src/functionalTest/resources/LocalDummyFramework") + val localPackageDirectory = File("src/functionalTest/resources/LocalSourceDummyFramework") // Given val fixture = SmpKMPTestFixture @@ -75,7 +75,7 @@ class LocalPackageTest : BaseTest() { add( SwiftDependency.Package.Local( path = localPackageDirectory.absolutePath, - packageName = "LocalDummyFramework", + packageName = "LocalSourceDummyFramework", exportToKotlin = true, ), ) @@ -85,7 +85,7 @@ class LocalPackageTest : BaseTest() { content = """ package com.example - import LocalDummyFramework.MySwiftClass + import LocalSourceDummyFramework.LocalSourceDummy """.trimIndent(), ), ).build() diff --git a/plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/Package.swift b/plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/Package.swift similarity index 75% rename from plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/Package.swift rename to plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/Package.swift index d3644bc..49aca33 100644 --- a/plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/Package.swift +++ b/plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/Package.swift @@ -4,18 +4,18 @@ import PackageDescription let package = Package( - name: "LocalDummyFramework", + name: "LocalSourceDummyFramework", products: [ // Products define the executables and libraries a package produces, making them visible to other packages. .library( - name: "LocalDummyFramework", - targets: ["LocalDummyFramework"]), + name: "LocalSourceDummyFramework", + targets: ["LocalSourceDummyFramework"]), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. // Targets can depend on other targets in this package and products from dependencies. .target( - name: "LocalDummyFramework"), + name: "LocalSourceDummyFramework"), ] ) diff --git a/plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/Sources/LocalDummyFramework/LocalDummyFramework.swift b/plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/Sources/LocalSourceDummyFramework/LocalSourceDummyFramework.swift similarity index 60% rename from plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/Sources/LocalDummyFramework/LocalDummyFramework.swift rename to plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/Sources/LocalSourceDummyFramework/LocalSourceDummyFramework.swift index 6af1c30..2ed454b 100644 --- a/plugin-build/plugin/src/functionalTest/resources/LocalDummyFramework/Sources/LocalDummyFramework/LocalDummyFramework.swift +++ b/plugin-build/plugin/src/functionalTest/resources/LocalSourceDummyFramework/Sources/LocalSourceDummyFramework/LocalSourceDummyFramework.swift @@ -3,8 +3,8 @@ import Foundation -@objcMembers public class MySwiftClass: NSObject { - func test() -> String { +@objcMembers public class LocalSourceDummy: NSObject { + public func test() -> String { return "TEST DUMMY FRAMEWORK" } } diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/SpmForKmpPlugin.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/SpmForKmpPlugin.kt index 94166e6..9f4aacc 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/SpmForKmpPlugin.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/SpmForKmpPlugin.kt @@ -5,12 +5,14 @@ package io.github.frankois944.spmForKmp import io.github.frankois944.spmForKmp.definition.PackageRootDefinitionExtension import io.github.frankois944.spmForKmp.tasks.CompileSwiftPackageTask import io.github.frankois944.spmForKmp.tasks.GenerateCInteropDefinitionTask +import io.github.frankois944.spmForKmp.tasks.GenerateExportableManifestTask import io.github.frankois944.spmForKmp.tasks.GenerateManifestTask import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.reflect.TypeOf +import org.gradle.api.tasks.TaskProvider import org.gradle.internal.extensions.stdlib.capitalized import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget @@ -24,6 +26,7 @@ internal const val EXTENSION_NAME: String = "swiftPackageConfig" internal const val TASK_GENERATE_MANIFEST: String = "generateSwiftPackage" internal const val TASK_COMPILE_PACKAGE: String = "compileSwiftPackage" internal const val TASK_GENERATE_CINTEROP_DEF: String = "generateCInteropDefinition" +internal const val TASK_GENERATE_EXPORTABLE_PACKAGE: String = "generateExportableSwiftPackage" @Suppress("UnnecessaryAbstractClass") public abstract class SpmForKmpPlugin : Plugin { @@ -121,7 +124,7 @@ public abstract class SpmForKmpPlugin : Plugin { // type = GenerateManifestTask::class.java, ) { manifest -> - manifest.packageDependencies.set(extension.packageDependencies.toList()) + manifest.packageDependencies.set(extension.packageDependencies) manifest.packageName.set(extension.name) manifest.minIos.set(extension.minIos) manifest.minTvos.set(extension.minTvos) @@ -134,6 +137,43 @@ public abstract class SpmForKmpPlugin : Plugin { manifest.sharedConfigDir.set(sharedConfigDir) manifest.sharedSecurityDir.set(sharedSecurityDir) } + val exportablePackage = + extension.packageDependencies.filter { product -> + product.exportToKotlin + } + val manifestDir = + project.layout.projectDirectory.asFile + .resolve("exported${extension.name.capitalized()}") + val task4: TaskProvider? = + if (exportablePackage.isNotEmpty()) { + tasks + .register( + // name = + getTaskName(TASK_GENERATE_EXPORTABLE_PACKAGE), + // type = + GenerateExportableManifestTask::class.java, + ) { manifest -> + manifest.packageDependencies.set(exportablePackage) + manifest.packageName.set("exported${extension.name.capitalized()}") + manifest.minIos.set(extension.minIos) + manifest.minTvos.set(extension.minTvos) + manifest.minMacos.set(extension.minMacos) + manifest.minWatchos.set(extension.minWatchos) + manifest.toolsVersion.set(extension.toolsVersion) + manifestDir.mkdirs() + manifest.manifestFile.set(manifestDir.resolve("Package.swift")) + logger.warn( + "Spm4Kmp: A local Swift package has been generated in $manifestDir", + ) + logger.warn( + "Please add it to your xcode project as a local package dependency.", + ) + } + } else { + manifestDir.deleteRecursively() + null + } + val taskGroup = mutableMapOf() val dependencyTaskNames = mutableMapOf() @@ -192,6 +232,7 @@ public abstract class SpmForKmpPlugin : Plugin { ), ) it.manifestFile.set(sourcePackageDir.resolve("Package.swift")) + it.scratchDir.set(packageScratchDir) } val dependenciesFiles = task3.get().outputFiles @@ -222,7 +263,7 @@ public abstract class SpmForKmpPlugin : Plugin { .dependsOn( task2 .get() - .dependsOn(task1.get()), + .dependsOn(listOfNotNull(task1.get(), task4?.get())), ) } } diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/dependency/PackageImplicitDependencies.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/PackageImplicitDependencies.kt similarity index 94% rename from plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/dependency/PackageImplicitDependencies.kt rename to plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/PackageImplicitDependencies.kt index 55fa476..95242c9 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/dependency/PackageImplicitDependencies.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/dump/PackageImplicitDependencies.kt @@ -1,6 +1,7 @@ -package io.github.frankois944.spmForKmp.dump.dependency +package io.github.frankois944.spmForKmp.dump import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.ObjectMapper import org.gradle.internal.cc.base.logger import org.gradle.internal.impldep.com.google.errorprone.annotations.Keep import java.io.File @@ -22,8 +23,7 @@ internal data class PackageImplicitDependencies( ) { companion object { private val objectMapper = - com.fasterxml.jackson.databind - .ObjectMapper() + ObjectMapper() fun fromString(input: String): PackageImplicitDependencies = objectMapper.readValue( diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/manifest/Template.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/manifest/Template.kt index 5da53d5..ec94ad2 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/manifest/Template.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/manifest/Template.kt @@ -50,9 +50,8 @@ internal fun generateManifest( path: "Sources") $binaryDependencies ] - ) - """.trimIndent() + """ } private fun getPlatformBlock( @@ -107,7 +106,7 @@ private fun getDependenciesTargets(dependencies: List): String } } } - }.joinToString(",\n") + }.joinToString(",") private fun buildLocaleBinary( dependencies: List, @@ -121,7 +120,7 @@ private fun buildLocaleBinary( val path = Path(dependency.path).relativeToOrSelf(swiftBuildDir) add(".binaryTarget(name: \"${dependency.packageName}\", path:\"${path}\")") } - }.joinToString(",\n") + }.joinToString(",") private fun buildRemoteBinary(dependencies: List): String = buildList { @@ -135,4 +134,4 @@ private fun buildRemoteBinary(dependencies: List): String = "checksum:\"${dependency.checksum}\")", ) } - }.joinToString(",\n") + }.joinToString(",") diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/operations/XcodeOperations.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/operations/XcodeOperations.kt index ce57f89..1b21271 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/operations/XcodeOperations.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/operations/XcodeOperations.kt @@ -1,23 +1,25 @@ package io.github.frankois944.spmForKmp.operations import io.github.frankois944.spmForKmp.CompileTarget -import io.github.frankois944.spmForKmp.dump.dependency.PackageImplicitDependencies -import org.gradle.api.logging.Logger -import org.gradle.process.ExecOperations +import io.github.frankois944.spmForKmp.dump.PackageImplicitDependencies +import io.github.frankois944.spmForKmp.utils.InjectedExecOps +import org.gradle.api.Project import java.io.ByteArrayOutputStream import java.io.File @Suppress("LongParameterList") -internal fun ExecOperations.resolvePackage( +internal fun Project.resolvePackage( workingDir: File, scratchPath: File, sharedCachePath: File?, sharedConfigPath: File?, sharedSecurityPath: File?, - logger: Logger? = null, ) { + val operation = objects.newInstance(InjectedExecOps::class.java) val args = mutableListOf( + "--sdk", + "macosx", "swift", "package", "resolve", @@ -39,90 +41,96 @@ internal fun ExecOperations.resolvePackage( val standardOutput = ByteArrayOutputStream() val errorOutput = ByteArrayOutputStream() - exec { - it.executable = "xcrun" - it.args = args - it.workingDir = workingDir - it.standardOutput = standardOutput - it.errorOutput = errorOutput - it.isIgnoreExitValue = true - }.also { - printExecLogs( - logger, - "resolvePackage", - args, - it.exitValue != 0, - standardOutput, - errorOutput, - ) - } + operation.execOps + .exec { + it.executable = "xcrun" + it.args = args + it.workingDir = workingDir + it.standardOutput = standardOutput + it.errorOutput = errorOutput + it.isIgnoreExitValue = true + }.also { + printExecLogs( + "resolvePackage", + args, + it.exitValue != 0, + standardOutput, + errorOutput, + ) + } } -internal fun ExecOperations.getXcodeVersion(logger: Logger? = null): String { +internal fun Project.getXcodeVersion(): String { + val operation = objects.newInstance(InjectedExecOps::class.java) val args = listOf( + "--sdk", + "macosx", "xcodebuild", "-version", ) val standardOutput = ByteArrayOutputStream() val errorOutput = ByteArrayOutputStream() - exec { - it.executable = "xcrun" - it.args = args - it.standardOutput = standardOutput - it.errorOutput = errorOutput - it.isIgnoreExitValue = true - }.also { - printExecLogs( - logger, - "getXcodeVersion", - args, - it.exitValue != 0, - standardOutput, - errorOutput, - ) - } + operation.execOps + .exec { + it.executable = "xcrun" + it.args = args + it.standardOutput = standardOutput + it.errorOutput = errorOutput + it.isIgnoreExitValue = true + }.also { + printExecLogs( + "getXcodeVersion", + args, + it.exitValue != 0, + standardOutput, + errorOutput, + ) + } val regex = """Xcode\s(\d+\.\d+)""".toRegex() val match = regex.find(standardOutput.toString()) return match?.groups?.get(1)?.value ?: throw RuntimeException("Can't find Xcode version with output $standardOutput") } -internal fun ExecOperations.getXcodeDevPath(logger: Logger? = null): String { +internal fun Project.getXcodeDevPath(): String { + val operation = objects.newInstance(InjectedExecOps::class.java) val args = listOf( + "--sdk", + "macosx", "xcode-select", "-p", ) val standardOutput = ByteArrayOutputStream() val errorOutput = ByteArrayOutputStream() - exec { - it.executable = "xcrun" - it.args = args - it.standardOutput = standardOutput - it.errorOutput = errorOutput - it.isIgnoreExitValue = true - }.also { - printExecLogs( - logger, - "getXcodeDevPath", - args, - it.exitValue != 0, - standardOutput, - errorOutput, - ) - } + operation.execOps + .exec { + it.executable = "xcrun" + it.args = args + it.standardOutput = standardOutput + it.errorOutput = errorOutput + it.isIgnoreExitValue = true + }.also { + printExecLogs( + "getXcodeDevPath", + args, + it.exitValue != 0, + standardOutput, + errorOutput, + ) + } return standardOutput.toString().trim() } -internal fun ExecOperations.getSDKPath( - target: CompileTarget, - logger: Logger? = null, -): String { +internal fun Project.getSDKPath(target: CompileTarget): String { + val operation = objects.newInstance(InjectedExecOps::class.java) val args = listOf( + "--sdk", + "macosx", "--sdk", target.sdk(), "--show-sdk-path", @@ -130,95 +138,125 @@ internal fun ExecOperations.getSDKPath( val standardOutput = ByteArrayOutputStream() val errorOutput = ByteArrayOutputStream() - exec { - it.executable = "xcrun" - it.args = args - it.standardOutput = standardOutput - it.errorOutput = errorOutput - it.isIgnoreExitValue = true - }.also { - printExecLogs( - logger, - "getSDKPath", - args, - it.exitValue != 0, - standardOutput, - errorOutput, - ) - } + operation.execOps + .exec { + it.executable = "xcrun" + it.args = args + it.standardOutput = standardOutput + it.errorOutput = errorOutput + it.isIgnoreExitValue = true + }.also { + printExecLogs( + "getSDKPath", + args, + it.exitValue != 0, + standardOutput, + errorOutput, + ) + } return standardOutput.toString().trim() } -internal fun ExecOperations.getPackageImplicitDependencies( +internal fun Project.getPackageImplicitDependencies( workingDir: File, - logger: Logger? = null, + scratchPath: File, ): PackageImplicitDependencies { + val operation = objects.newInstance(InjectedExecOps::class.java) val args = listOf( + "--sdk", + "macosx", "swift", "package", "show-dependencies", + "--scratch-path", + scratchPath.path, "--format", "json", ) val standardOutput = ByteArrayOutputStream() val errorOutput = ByteArrayOutputStream() - exec { - it.executable = "xcrun" - it.workingDir = workingDir - it.args = args - it.standardOutput = standardOutput - it.errorOutput = errorOutput - it.isIgnoreExitValue = true - }.also { - printExecLogs( - logger, - "show-dependencies", - args, - it.exitValue != 0, - standardOutput, - errorOutput, - ) - } + operation.execOps + .exec { + it.executable = "xcrun" + it.workingDir = workingDir + it.args = args + it.standardOutput = standardOutput + it.errorOutput = errorOutput + it.isIgnoreExitValue = true + }.also { + printExecLogs( + "show-dependencies", + args, + it.exitValue != 0, + standardOutput, + errorOutput, + ) + } return PackageImplicitDependencies.fromString(standardOutput.toString()) } +internal fun Project.swiftFormat(file: File) { + val operation = objects.newInstance(InjectedExecOps::class.java) + val args = + listOf( + "--sdk", + "macosx", + "swift-format", + "-i", + file.path, + ) + + val standardOutput = ByteArrayOutputStream() + val errorOutput = ByteArrayOutputStream() + operation.execOps + .exec { + it.executable = "xcrun" + it.args = args + it.standardOutput = standardOutput + it.errorOutput = errorOutput + it.isIgnoreExitValue = true + }.also { + printExecLogs( + "swift-format", + args, + it.exitValue != 0, + standardOutput, + errorOutput, + ) + } +} + @Suppress("LongParameterList") -internal fun printExecLogs( - logger: Logger?, +internal fun Project.printExecLogs( action: String, args: List, isError: Boolean, standardOutput: ByteArrayOutputStream, errorOutput: ByteArrayOutputStream, - extraString: String? = null, ) { if (isError) { - logger?.error( + logger.error( """ -ERROR FOUND WHEN EXEC -RUN $action -ARGS xcrun ${args.joinToString(" ")} -ERROR $errorOutput -OUTPUT $standardOutput -### -${extraString.orEmpty()} -### + ERROR FOUND WHEN EXEC + RUN $action + ARGS xcrun ${args.joinToString(" ")} + ERROR $errorOutput + OUTPUT $standardOutput + ### """.trimMargin(), ) throw RuntimeException( "RUN CMD $action failed", ) } else { - logger?.debug( + logger.debug( """ -RUN $action -ARGS xcrun ${args.joinToString(" ")} -OUTPUT $standardOutput -### -${extraString.orEmpty()} -### + RUN $action + ARGS xcrun ${args.joinToString(" ")} + OUTPUT $standardOutput + ### """.trimMargin(), ) } diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/CompileSwiftPackageTask.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/CompileSwiftPackageTask.kt index edc33ca..ef1e37f 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/CompileSwiftPackageTask.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/CompileSwiftPackageTask.kt @@ -88,11 +88,13 @@ internal abstract class CompileSwiftPackageTask : DefaultTask() { @TaskAction fun compilePackage() { logger.debug("Compile the manifest {}", manifestFile.get().path) - val sdkPath = operation.getSDKPath(target.get(), logger) + val sdkPath = project.getSDKPath(target.get()) val workingDir = prepareWorkingDir() val args = mutableListOf( + "--sdk", + "macosx", "swift", "build", "--sdk", @@ -117,14 +119,6 @@ internal abstract class CompileSwiftPackageTask : DefaultTask() { args.add(it.path) } - logger.debug( - """ -RUN compileManifest -ARGS xcrun ${args.joinToString(" ")} -From ${workingDir.path} - """.trimMargin(), - ) - val standardOutput = ByteArrayOutputStream() val errorOutput = ByteArrayOutputStream() operation @@ -136,9 +130,8 @@ From ${workingDir.path} it.errorOutput = errorOutput it.isIgnoreExitValue = true }.also { - printExecLogs( - logger, - "compilePackage", + project.printExecLogs( + "buildPackage", args, it.exitValue != 0, standardOutput, diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateCInteropDefinitionTask.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateCInteropDefinitionTask.kt index 6823403..a293e4b 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateCInteropDefinitionTask.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateCInteropDefinitionTask.kt @@ -6,6 +6,7 @@ import io.github.frankois944.spmForKmp.operations.getPackageImplicitDependencies import io.github.frankois944.spmForKmp.operations.getXcodeDevPath import io.github.frankois944.spmForKmp.operations.getXcodeVersion import io.github.frankois944.spmForKmp.utils.md5 +import io.github.frankois944.spmForKmp.xcodeconfig.ModuleConfig import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty @@ -14,16 +15,7 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputFiles import org.gradle.api.tasks.TaskAction -import org.gradle.process.ExecOperations import java.io.File -import javax.inject.Inject - -private data class ModuleConfig( - val isFramework: Boolean, - val name: String, - val buildDir: File, - val definitionFile: File, -) internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { @get:Input @@ -47,6 +39,9 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { @get:InputFile abstract val manifestFile: RegularFileProperty + @get:Input + abstract val scratchDir: Property + init { description = "Generate the cinterop definitions files" group = "io.github.frankois944.spmForKmp.tasks" @@ -61,9 +56,6 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { } } - @get:Inject - abstract val operation: ExecOperations - private fun getBuildDirectory(): File = compiledBinary .asFile @@ -135,24 +127,23 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { * @return A string of linker flags and options constructed based on the build configuration. */ private fun getExtraLinkers(): String { - val xcodeDevPath = operation.getXcodeDevPath(logger) + val xcodeDevPath = project.getXcodeDevPath() val linkerPlatformVersion = @Suppress("MagicNumber") - if (operation.getXcodeVersion(logger).toDouble() >= 15) { + if (project.getXcodeVersion().toDouble() >= 15) { target.get().linkerPlatformVersionName() } else { target.get().linkerMinOsVersionName() } - - return listOf( - "-$linkerPlatformVersion", - osVersion.get(), - osVersion.get(), - "-rpath", - "/usr/lib/swift", - "-L\"$xcodeDevPath/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/${target.get().sdk()}\"", - ).joinToString(" ") + return buildList { + // add("-$linkerPlatformVersion") + // add(osVersion.get()) + // add(osVersion.get()) + // add("-rpath") + // add("/usr/lib/swift") + add("-L\"$xcodeDevPath/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/${target.get().sdk()}\"") + }.joinToString(" ") } @Suppress("LongMethod") @@ -189,17 +180,16 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { ), ) } - }.also { - logger.debug( - """ - modulesConfigs found - $moduleConfigs - """.trimIndent(), - ) } - - moduleConfigs.forEach { moduleConfig -> + logger.debug( + """ + modulesConfigs found + $moduleConfigs + """.trimIndent(), + ) + moduleConfigs.forEachIndexed { index, moduleConfig -> logger.debug("Building definition file for: {}", moduleConfig) + var definition = "" try { val libName = compiledBinary.asFile.get().name val checksum = compiledBinary.asFile.get().md5() @@ -208,20 +198,16 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { val moduleName = extractModuleNameFromModuleMap(mapFile.readText()) ?: throw Exception("No module name from ${moduleConfig.name} in mapFile") - moduleConfig.definitionFile.writeText( + definition = """ +language = Objective-C +modules = $moduleName +package = ${moduleConfig.name} +# Set a checksum for avoid build cache +# checkum: $checksum +libraryPaths = "${getBuildDirectory().path}" +compilerOpts = -fmodules -framework "${moduleConfig.buildDir.nameWithoutExtension}" -F"${getBuildDirectory().path}" +linkerOpts = ${getExtraLinkers()} -framework "${moduleConfig.buildDir.nameWithoutExtension}" -F"${getBuildDirectory().path}" """ - language = Objective-C - modules = $moduleName - package = ${moduleConfig.name} - - # Set a checksum for avoid build cache - # checkum: $checksum - staticLibraries = $libName - libraryPaths = "${getBuildDirectory().path}" - compilerOpts = -fmodules -framework "${moduleConfig.buildDir.name}" -F"${getBuildDirectory().path}" - linkerOpts = ${getExtraLinkers()} - """.trimIndent(), - ) } else { val mapFile = moduleConfig.buildDir.resolve("module.modulemap") val mapFileContent = mapFile.readText() @@ -229,10 +215,10 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { extractModuleNameFromModuleMap(mapFileContent) ?: throw RuntimeException("No module name from ${moduleConfig.name} in mapFile") val implicitDependencies = - operation + project .getPackageImplicitDependencies( workingDir = manifestFile.asFile.get().parentFile, - logger = logger, + scratchPath = scratchDir.get(), ).getFolders("Public") val headersBuildPath = buildList { @@ -242,27 +228,34 @@ internal abstract class GenerateCInteropDefinitionTask : DefaultTask() { add(it) } }.joinToString(" ") { "-I\"${it}\"" } - - moduleConfig.definitionFile.writeText( + definition = """ - language = Objective-C - modules = $moduleName - package = ${moduleConfig.name} - - # Set a checksum for avoid build cache - # checkum: $checksum - staticLibraries = $libName - libraryPaths = "${getBuildDirectory().path}" - compilerOpts = -ObjC -fmodules $headersBuildPath -F"${getBuildDirectory().path}" - linkerOpts = ${getExtraLinkers()} -F"${getBuildDirectory().path}" - """.trimIndent(), - ) +language = Objective-C +modules = $moduleName +package = ${moduleConfig.name} +# Set a checksum for avoid build cache +# checkum: $checksum +libraryPaths = "${getBuildDirectory().path}" +compilerOpts = -fmodules $headersBuildPath -F"${getBuildDirectory().path}" +linkerOpts = ${getExtraLinkers()} -F"${getBuildDirectory().path}" + """ + } + if (index == 0) { + definition = """ +$definition +staticLibraries = $libName + """ + } + if (definition.isNotEmpty()) { + moduleConfig.definitionFile.writeText(definition.trimIndent()) } logger.debug( """ + ###### Definition File : ${moduleConfig.definitionFile.name} At Path: ${moduleConfig.definitionFile.path} - ${moduleConfig.definitionFile.readText()} + ${moduleConfig.definitionFile.readText()}moduleConfig.definitionFile.readText()} + ###### """.trimIndent(), ) } catch (ex: Exception) { diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateExportableManifestTask.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateExportableManifestTask.kt new file mode 100644 index 0000000..d7ab7c5 --- /dev/null +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateExportableManifestTask.kt @@ -0,0 +1,90 @@ +package io.github.frankois944.spmForKmp.tasks + +import io.github.frankois944.spmForKmp.definition.SwiftDependency +import io.github.frankois944.spmForKmp.manifest.generateManifest +import io.github.frankois944.spmForKmp.operations.swiftFormat +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +internal abstract class GenerateExportableManifestTask : DefaultTask() { + @get:Input + abstract val packageDependencies: ListProperty + + @get:Input + abstract val packageName: Property + + @get:Input + abstract val minIos: Property + + @get:Input + abstract val minMacos: Property + + @get:Input + abstract val minTvos: Property + + @get:Input + abstract val minWatchos: Property + + @get:Input + abstract val toolsVersion: Property + + @get:OutputFile + abstract val manifestFile: RegularFileProperty + + init { + description = "Generate a Swift Package manifest with exported product" + group = "io.github.frankois944.spmForKmp.tasks" + } + + private fun prepareExportableDir() { + val sourceDir = + manifestFile + .get() + .asFile + .parentFile + .resolve("Sources") + .also { it.mkdirs() } + sourceDir.resolve("DummySPMFile.swift").createNewFile() + sourceDir.resolve("DummySPMFile.swift").writeText("import Foundation") + } + + @TaskAction + fun generateFile() { + prepareExportableDir() + val manifest = + generateManifest( + packageDependencies.get(), + generatedPackageDirectory = + manifestFile + .get() + .asFile.parentFile + .toPath(), + productName = packageName.get(), + minIos = minIos.get(), + minMacos = minMacos.get(), + minTvos = minTvos.get(), + minWatchos = minWatchos.get(), + toolsVersion = toolsVersion.get(), + ) + manifestFile.asFile.get().writeText(manifest) + try { + project.swiftFormat( + manifestFile.asFile.get(), + ) + } catch (ex: Exception) { + logger.error( + """ + Manifest file generated : + ${manifestFile.get().asFile} + ${manifestFile.get().asFile.readText()} + """.trimIndent(), + ) + throw ex + } + } +} diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateManifestTask.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateManifestTask.kt index 8f6b60a..3c6cb02 100644 --- a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateManifestTask.kt +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/tasks/GenerateManifestTask.kt @@ -3,6 +3,7 @@ package io.github.frankois944.spmForKmp.tasks import io.github.frankois944.spmForKmp.definition.SwiftDependency import io.github.frankois944.spmForKmp.manifest.generateManifest import io.github.frankois944.spmForKmp.operations.resolvePackage +import io.github.frankois944.spmForKmp.operations.swiftFormat import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty @@ -12,15 +13,10 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.process.ExecOperations import java.io.File -import javax.inject.Inject @CacheableTask internal abstract class GenerateManifestTask : DefaultTask() { - @get:Inject - abstract val operation: ExecOperations - @get:Input abstract val packageDependencies: ListProperty @@ -83,15 +79,16 @@ internal abstract class GenerateManifestTask : DefaultTask() { toolsVersion = toolsVersion.get(), ) manifestFile.asFile.get().writeText(manifest) - try { - operation.resolvePackage( + project.swiftFormat( + manifestFile.asFile.get(), + ) + project.resolvePackage( workingDir = manifestFile.asFile.get().parentFile, scratchPath = packageScratchDir.get(), sharedCachePath = sharedCacheDir.orNull, sharedConfigPath = sharedConfigDir.orNull, sharedSecurityPath = sharedSecurityDir.orNull, - logger = logger, ) } catch (ex: Exception) { logger.error( diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/utils/InjectedExecOps.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/utils/InjectedExecOps.kt new file mode 100644 index 0000000..4c2a298 --- /dev/null +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/utils/InjectedExecOps.kt @@ -0,0 +1,9 @@ +package io.github.frankois944.spmForKmp.utils + +import org.gradle.process.ExecOperations +import javax.inject.Inject + +internal interface InjectedExecOps { + @get:Inject + val execOps: ExecOperations +} diff --git a/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/xcodeconfig/ModuleConfig.kt b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/xcodeconfig/ModuleConfig.kt new file mode 100644 index 0000000..f1d8c4a --- /dev/null +++ b/plugin-build/plugin/src/main/java/io/github/frankois944/spmForKmp/xcodeconfig/ModuleConfig.kt @@ -0,0 +1,10 @@ +package io.github.frankois944.spmForKmp.xcodeconfig + +import java.io.File + +internal data class ModuleConfig( + val isFramework: Boolean, + val name: String, + val buildDir: File, + val definitionFile: File, +)