diff --git a/gradle.properties b/gradle.properties index 287b21b..723fef4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,8 +7,10 @@ android.useAndroidX=true # Increase memory for in-process compiler execution. org.gradle.jvmargs=-Xmx3g # https://kotlinlang.org/docs/migrating-multiplatform-project-to-14.html#migrate-to-the-hierarchical-project-structure +kotlin.experimental.swift-export.enabled=true kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.enableHierarchicalCommonization=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.native.ignoreDisabledTargets=true org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true diff --git a/libs.versions.toml b/libs.versions.toml index e11ab7e..5bdefe9 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -43,6 +43,9 @@ ktorClientDarwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" ktorClientLogging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktorClientOkHttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktorClientWinHttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor" } +ktorEvents = { module = "io.ktor:ktor-events", version.ref = "ktor" } +ktorHttp = { module = "io.ktor:ktor-http", version.ref = "ktor" } +ktorIo = { module = "io.ktor:ktor-io", version.ref = "ktor" } ktorSerializationKotlinxJson = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } ktorUtils = { module = "io.ktor:ktor-utils", version.ref = "ktor" } okio = { module = "com.squareup.okio:okio", version.ref = "okio" } diff --git a/solana-kotlin/build.gradle.kts b/solana-kotlin/build.gradle.kts index 380dcc6..4124d04 100644 --- a/solana-kotlin/build.gradle.kts +++ b/solana-kotlin/build.gradle.kts @@ -1,5 +1,4 @@ -import co.touchlab.skie.configuration.ClassInterop -import co.touchlab.skie.configuration.DefaultArgumentInterop +import org.jetbrains.kotlin.gradle.swiftexport.ExperimentalSwiftExportDsl plugins { alias(libs.plugins.kotlinMultiplatform) @@ -8,14 +7,13 @@ plugins { alias(libs.plugins.dokka) signing alias(libs.plugins.multiplatform.swiftpackage) - alias(libs.plugins.skie) } group = "net.avianlabs.solana" version = properties["version"] as String kotlin { - targetHierarchy.default() + applyDefaultHierarchyTemplate() explicitApi() jvm { @@ -27,20 +25,29 @@ kotlin { } } - listOf( - iosArm64(), - iosSimulatorArm64(), - ).forEach { iosTarget -> - iosTarget.binaries.framework { - baseName = "SolanaKotlin" - export(project(":tweetnacl-multiplatform")) - isStatic = true - } - } + iosArm64() + iosSimulatorArm64() mingwX64() linuxX64() + @OptIn(ExperimentalSwiftExportDsl::class) + swiftExport { + // Root module name + moduleName = "SolanaKotlin" + + // Collapse rule + flattenPackage = "net.avianlabs.solana" + +// // Export external modules +// export(project(":tweetnacl-multiplatform")) { +// // Exported module name +// moduleName = "TweetNaCl" +// // Collapse exported dependency rule +// flattenPackage = "net.avianlabs.solana.tweetnacl" +// } + } + sourceSets { val jvmMain by getting { dependencies { @@ -97,15 +104,6 @@ kotlin { } } -skie { - features { - group("net.avianlabs.solana.tweetnacl") { - ClassInterop.CInteropFrameworkName("TweetNaClMultiplatform") - DefaultArgumentInterop.Enabled(true) - } - } -} - multiplatformSwiftPackage { swiftToolsVersion("5.9") targetPlatforms { @@ -140,6 +138,7 @@ publishing { } } } + publications { withType { pom { diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt index d48323b..f5beb57 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt @@ -22,7 +22,6 @@ public class SolanaClient( ) : this( client = RpcKtorClient( url = url, - httpClient = HttpClient(), ), headerProviders = mapOf( HttpHeaders.Authorization to { diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/RpcKtorClient.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/RpcKtorClient.kt index 551e81f..d80888b 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/RpcKtorClient.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/RpcKtorClient.kt @@ -10,17 +10,16 @@ import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.* import kotlin.collections.set -public class RpcKtorClient( +public class RpcKtorClient internal constructor( internal val url: Url, httpClient: HttpClient = HttpClient(), ) { public constructor( url: String, - httpClient: HttpClient = HttpClient(), ) : this( url = Url(url), - httpClient = httpClient, + httpClient = HttpClient(), ) internal val requestIdGenerator: RequestIdGenerator = RequestIdGenerator() diff --git a/solana-kotlin/src/commonTest/kotlin/net/avianlabs/solana/domain/program/RPCIntegrationTest.kt b/solana-kotlin/src/commonTest/kotlin/net/avianlabs/solana/domain/program/RPCIntegrationTest.kt index 5f8f61f..c89f284 100644 --- a/solana-kotlin/src/commonTest/kotlin/net/avianlabs/solana/domain/program/RPCIntegrationTest.kt +++ b/solana-kotlin/src/commonTest/kotlin/net/avianlabs/solana/domain/program/RPCIntegrationTest.kt @@ -29,7 +29,6 @@ class RPCIntegrationTest { private val client = SolanaClient( client = RpcKtorClient( "http://localhost:8899", - httpClient = HttpClient {} ), ) diff --git a/swiftexport.sh b/swiftexport.sh new file mode 100755 index 0000000..ed8be6c --- /dev/null +++ b/swiftexport.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +set -e # Exit on error + +# Function to check if xcodebuild is available +check_xcode() { + if ! command -v xcodebuild >/dev/null 2>&1; then + echo "Error: xcodebuild not found. Please install Xcode Command Line Tools." + exit 1 + fi +} + +# Function to get the active Xcode developer directory +get_developer_dir() { + local developer_dir + developer_dir=$(xcode-select -p) + echo "$developer_dir" +} + +# Function to get SDK information +get_sdk_info() { + local sdk_type="$1" # iphoneos or iphonesimulator + xcodebuild -version -sdk "$sdk_type" Path +} + +# Function to get default architecture for SDK +get_sdk_archs() { + local sdk_type="$1" + if [[ "$sdk_type" == "iphonesimulator" ]]; then + echo "arm64 x86_64" + else + echo "arm64" + fi +} + +# Main script +echo "Detecting Xcode environment..." + +# Check for Xcode installation +check_xcode + +# Get developer directory +DEVELOPER_DIR=$(get_developer_dir) +echo "Developer Directory: $DEVELOPER_DIR" + +# Determine SDK (preferring physical device SDK, falling back to simulator) +if xcodebuild -version -sdk iphoneos >/dev/null 2>&1; then + SDK_NAME="iphoneos" + PLATFORM_NAME="iphoneos" + EFFECTIVE_PLATFORM_NAME="-iphoneos" +else + SDK_NAME="iphonesimulator" + PLATFORM_NAME="iphonesimulator" + EFFECTIVE_PLATFORM_NAME="-iphonesimulator" +fi + +# Read SDK information +IFS=$'\n' read -r -d '' SDKROOT SDK_PLATFORM_VERSION SDK_VERSION < <(get_sdk_info "$SDK_NAME" && printf '\0') + +# Get architecture +ARCHS=$(get_sdk_archs "$SDK_NAME") +VALID_ARCHS=${ARCHS:-"arm64"} # Fallback to arm64 if detection fails + +# Set up build directories +BASE_DIR="$(pwd)/build/xcode-env" +CONFIGURATION="Debug" # Can be parameterized if needed +KOTLIN_FRAMEWORK_BUILD_TYPE="debug" # Matches CONFIGURATION in lowercase +TARGET_BUILD_DIR="$BASE_DIR/Products" +BUILT_PRODUCTS_DIR="$TARGET_BUILD_DIR" # Required by Kotlin plugin +FRAMEWORKS_FOLDER_PATH="$BASE_DIR/Frameworks" + +# Create required directories +mkdir -p "$TARGET_BUILD_DIR" +mkdir -p "$FRAMEWORKS_FOLDER_PATH" + +# Export all required variables +export SDK_NAME +export SDKROOT +export CONFIGURATION +export KOTLIN_FRAMEWORK_BUILD_TYPE +export TARGET_BUILD_DIR +export BUILT_PRODUCTS_DIR +export ARCHS="$VALID_ARCHS" +export FRAMEWORKS_FOLDER_PATH +export PLATFORM_NAME +export EFFECTIVE_PLATFORM_NAME + +# Print environment setup +echo "Environment variables set:" +echo "SDK_NAME=$SDK_NAME" +echo "SDKROOT=$SDKROOT" +echo "CONFIGURATION=$CONFIGURATION" +echo "KOTLIN_FRAMEWORK_BUILD_TYPE=$KOTLIN_FRAMEWORK_BUILD_TYPE" +echo "TARGET_BUILD_DIR=$TARGET_BUILD_DIR" +echo "BUILT_PRODUCTS_DIR=$BUILT_PRODUCTS_DIR" +echo "ARCHS=$ARCHS" +echo "FRAMEWORKS_FOLDER_PATH=$FRAMEWORKS_FOLDER_PATH" +echo "PLATFORM_NAME=$PLATFORM_NAME" +echo "EFFECTIVE_PLATFORM_NAME=$EFFECTIVE_PLATFORM_NAME" + +# Run the gradle task +echo "Running gradle task..." +./gradlew :tweetnacl-multiplatform:embedSwiftExportForXcode diff --git a/tweetnacl-multiplatform/build.gradle.kts b/tweetnacl-multiplatform/build.gradle.kts index fc49cbd..6d7d5b5 100644 --- a/tweetnacl-multiplatform/build.gradle.kts +++ b/tweetnacl-multiplatform/build.gradle.kts @@ -1,5 +1,6 @@ import co.touchlab.cklib.gradle.CompileToBitcode.Language import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.swiftexport.ExperimentalSwiftExportDsl plugins { alias(libs.plugins.kotlinMultiplatform) @@ -8,14 +9,13 @@ plugins { alias(libs.plugins.dokka) signing alias(libs.plugins.multiplatform.swiftpackage) - alias(libs.plugins.skie) } group = "net.avianlabs.solana" version = properties["version"] as String kotlin { - targetHierarchy.default() + applyDefaultHierarchyTemplate() explicitApi() jvm { @@ -27,16 +27,18 @@ kotlin { } } - listOf( - iosArm64(), - iosSimulatorArm64(), - ).forEach { iosTarget -> - iosTarget.binaries.framework { - baseName = "TweetNaClMultiplatform" - isStatic = true - } - } + iosArm64() + iosSimulatorArm64() + @OptIn(ExperimentalSwiftExportDsl::class) + swiftExport { + // Root module name + moduleName = "TweetNaCl" + + // Collapse rule + flattenPackage = "net.avianlabs.solana.tweetnacl" + } + mingwX64() linuxX64() diff --git a/tweetnacl-multiplatform/src/iosMain/kotlin/net/avianlabs/solana/tweetnacl/ed25519/NSData.ios.kt b/tweetnacl-multiplatform/src/iosMain/kotlin/net/avianlabs/solana/tweetnacl/ed25519/NSData.ios.kt deleted file mode 100644 index b8ba6d5..0000000 --- a/tweetnacl-multiplatform/src/iosMain/kotlin/net/avianlabs/solana/tweetnacl/ed25519/NSData.ios.kt +++ /dev/null @@ -1,28 +0,0 @@ -@file:OptIn( - ExperimentalForeignApi::class, - BetaInteropApi::class, -) - -package net.avianlabs.solana.tweetnacl.ed25519 - -import kotlinx.cinterop.* -import platform.Foundation.NSData -import platform.Foundation.create -import platform.posix.memcpy - -public fun NSData.toKotlinByteArray(): ByteArray = ByteArray(length.toInt()).apply { - usePinned { pinned: Pinned -> - memcpy( - __dst = pinned.addressOf(0), - __src = this@toKotlinByteArray.bytes, - __n = this@toKotlinByteArray.length, - ) - } -} - -public fun ByteArray.toNSData(): NSData = memScoped { - NSData.create( - bytes = allocArrayOf(this@toNSData), - length = size.toULong(), - ) -} \ No newline at end of file diff --git a/tweetnacl-multiplatform/src/iosMain/kotlin/net/avianlabs/solana/tweetnacl/ed25519/PublicKey.ios.kt b/tweetnacl-multiplatform/src/iosMain/kotlin/net/avianlabs/solana/tweetnacl/ed25519/PublicKey.ios.kt deleted file mode 100644 index ea38762..0000000 --- a/tweetnacl-multiplatform/src/iosMain/kotlin/net/avianlabs/solana/tweetnacl/ed25519/PublicKey.ios.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.avianlabs.solana.tweetnacl.ed25519 - -import kotlinx.cinterop.* -import platform.Foundation.NSData - -public fun PublicKey(data: NSData): PublicKey = PublicKey( - bytes = data.toKotlinByteArray(), -) - -public val PublicKey.data: NSData - get() = bytes.toNSData()