From 7a0237e1e20a3675d124951db2694903c8acefcd Mon Sep 17 00:00:00 2001 From: Kristopher Wuollett Date: Fri, 10 Jan 2025 12:35:02 -0600 Subject: [PATCH 1/3] add uniffi-bindgen-swift example binary (#2382) --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + examples/app/uniffi-bindgen-swift-cli/Cargo.toml | 15 +++++++++++++++ .../uniffi-bindgen-swift.rs | 3 +++ 4 files changed, 26 insertions(+) create mode 100644 examples/app/uniffi-bindgen-swift-cli/Cargo.toml create mode 100644 examples/app/uniffi-bindgen-swift-cli/uniffi-bindgen-swift.rs diff --git a/Cargo.lock b/Cargo.lock index 7dffc9d6b..ce9fe328c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1601,6 +1601,13 @@ dependencies = [ "uniffi", ] +[[package]] +name = "uniffi-bindgen-swift-cli" +version = "0.1.0" +dependencies = [ + "uniffi", +] + [[package]] name = "uniffi-example-arithmetic" version = "0.22.0" diff --git a/Cargo.toml b/Cargo.toml index a6dc4a83e..569e7a205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "weedle2", "examples/app/uniffi-bindgen-cli", + "examples/app/uniffi-bindgen-swift-cli", "examples/arithmetic", "examples/arithmetic-procmacro", "examples/async-api-client", diff --git a/examples/app/uniffi-bindgen-swift-cli/Cargo.toml b/examples/app/uniffi-bindgen-swift-cli/Cargo.toml new file mode 100644 index 000000000..513e57642 --- /dev/null +++ b/examples/app/uniffi-bindgen-swift-cli/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "uniffi-bindgen-swift-cli" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "uniffi-bindgen-swift" +path = "uniffi-bindgen-swift.rs" + +[dependencies] +uniffi = { workspace = true, features = ["cli"] } + +[features] +ffi-trace = ["uniffi/ffi-trace"] diff --git a/examples/app/uniffi-bindgen-swift-cli/uniffi-bindgen-swift.rs b/examples/app/uniffi-bindgen-swift-cli/uniffi-bindgen-swift.rs new file mode 100644 index 000000000..5641fc6e9 --- /dev/null +++ b/examples/app/uniffi-bindgen-swift-cli/uniffi-bindgen-swift.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi::uniffi_bindgen_swift() +} From f27d86b4b34a47ee18de2fb8dee5a8407d2a58c5 Mon Sep 17 00:00:00 2001 From: Kristopher Wuollett Date: Fri, 10 Jan 2025 12:41:37 -0600 Subject: [PATCH 2/3] add staticlib support to fixtures/ext-types/lib (#2382) --- fixtures/ext-types/lib/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fixtures/ext-types/lib/Cargo.toml b/fixtures/ext-types/lib/Cargo.toml index 5a53e0d51..f491f3cd1 100644 --- a/fixtures/ext-types/lib/Cargo.toml +++ b/fixtures/ext-types/lib/Cargo.toml @@ -15,7 +15,7 @@ external-crates = [ ] [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["lib", "cdylib", "staticlib"] name = "uniffi_ext_types_lib" [dependencies] From c3b3149c709af148ad65a5b81f7b7de21748ac64 Mon Sep 17 00:00:00 2001 From: Kristopher Wuollett Date: Fri, 10 Jan 2025 12:42:06 -0600 Subject: [PATCH 3/3] add example swift-package building script (#2382) --- examples/app/swift-package/README.md | 33 ++++++ examples/app/swift-package/bindgen.sh | 165 ++++++++++++++++++++++++++ fixtures/ext-types/lib/run-bindgen.sh | 20 ++++ 3 files changed, 218 insertions(+) create mode 100644 examples/app/swift-package/README.md create mode 100755 examples/app/swift-package/bindgen.sh create mode 100755 fixtures/ext-types/lib/run-bindgen.sh diff --git a/examples/app/swift-package/README.md b/examples/app/swift-package/README.md new file mode 100644 index 000000000..fc61e5e5a --- /dev/null +++ b/examples/app/swift-package/README.md @@ -0,0 +1,33 @@ +# examples-app-swift-package + +Builds a Swift package, currently only static library supported, for a Uniffi Rust package including Uniffi dependencies. + +## Usage + +Example from `./fixtures/ext-types/lib`: + +```shell +% ./run-bindgen.sh + Finished release [optimized] target(s) in 0.28s +info: component 'rust-std' for target 'x86_64-apple-ios' is up to date +info: component 'rust-std' for target 'aarch64-apple-ios-sim' is up to date +info: component 'rust-std' for target 'aarch64-apple-ios' is up to date +info: component 'rust-std' for target 'x86_64-apple-darwin' is up to date +info: component 'rust-std' for target 'aarch64-apple-darwin' is up to date + Finished release [optimized] target(s) in 0.15s +Creating iOS sim fat library at path '/Users/kris/Code/kriswuollett/uniffi-rs/examples/app/swift-package/../../../target/apple-ios-sim/release/libuniffi_ext_types_lib.a' +Creating macOS fat library at path '/Users/kris/Code/kriswuollett/uniffi-rs/examples/app/swift-package/../../../target/apple-macos/release/libuniffi_ext_types_lib.a' +Creating XCFramework at path '/Users/kris/Code/kriswuollett/uniffi-rs/examples/app/swift-package/../../../target/swift-package/UniffiFixtureExtTypesFFI/UniffiFixtureExtTypesFFI.xcframework' +xcframework successfully written out to: /Users/kris/Code/kriswuollett/uniffi-rs/target/swift-package/UniffiFixtureExtTypesFFI/UniffiFixtureExtTypesFFI.xcframework +Building for debugging... + +... +error: emit-module command failed with exit code 1 (use -v to see invocation) +/Users/kris/Code/kriswuollett/uniffi-rs/target/swift-package/UniffiFixtureExtTypesFFI/Sources/ImportedTypesLibFFI/ImportedTypesLibFFI.swift:564:21: error: cannot find type 'UniffiOneEnum' in scope + 562 | + 563 | public struct CombinedType: Sendable { + 564 | public var uoe: UniffiOneEnum + | `- error: cannot find type 'UniffiOneEnum' in scope + 565 | public var uot: UniffiOneType + 566 | public var uots: [UniffiOneType] +``` diff --git a/examples/app/swift-package/bindgen.sh b/examples/app/swift-package/bindgen.sh new file mode 100755 index 000000000..c21a206d5 --- /dev/null +++ b/examples/app/swift-package/bindgen.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash + +set -eou pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" +REPO_DIR="$SCRIPT_DIR/../../.." + +if [[ "${MANIFEST_FILE:-}" == "" ]]; then + printf "MANIFEST_FILE env var not set!\n" 1>&2 + exit 1 +fi + +if [[ "${RUST_PACKAGE:-}" == "" ]]; then + printf "RUST_PACKAGE env var not set!\n" 1>&2 + exit 1 +fi + +DEFAULT_XCFRAMEWORK_MODULE_NAME="$(echo -n "$RUST_PACKAGE" | sed -e 's/-/_/g' | python3 -c "print(''.join(x.title() for x in input().split('_')) + 'FFI')")" +XCFRAMEWORK_MODULE_NAME="${XCFRAMEWORK_MODULE_NAME:-$DEFAULT_XCFRAMEWORK_MODULE_NAME}" + +DEFAULT_OUTPUT_DIR="$REPO_DIR/target/swift-package/$XCFRAMEWORK_MODULE_NAME" +OUTPUT_DIR="${OUTPUT_DIR:-$DEFAULT_OUTPUT_DIR}" +XCFRAMEWORK_DIR="$OUTPUT_DIR/$XCFRAMEWORK_MODULE_NAME.xcframework" + +if [[ -e "$OUTPUT_DIR" ]]; then + printf "The OUTPUT_DIR '%s' must not exist before running\n" "$OUTPUT_DIR" 1>&2 + exit 1 +fi + +DEFAULT_LIBRARY_NAME="lib${RUST_PACKAGE}.a" +LIBRARY_NAME="${LIBRARY_NAME:-$DEFAULT_LIBRARY_NAME}" + +if [[ "${UNIFFI_BINDGEN_SWIFT:-}" == "" ]]; then + UNIFFI_BINDGEN_SWIFT="$REPO_DIR/target/release/uniffi-bindgen-swift" + cargo build --manifest-path "$REPO_DIR/Cargo.toml" --release --bin uniffi-bindgen-swift +fi + +rustup target add x86_64-apple-ios +rustup target add aarch64-apple-ios-sim +rustup target add aarch64-apple-ios +rustup target add x86_64-apple-darwin +rustup target add aarch64-apple-darwin + +cargo build --manifest-path "$REPO_DIR/Cargo.toml" --release --package "$RUST_PACKAGE" \ + --target x86_64-apple-ios \ + --target aarch64-apple-ios-sim \ + --target aarch64-apple-ios \ + --target x86_64-apple-darwin \ + --target aarch64-apple-darwin + +# Generate Swift files based on sample library +SWIFT_FILES_DIR="$REPO_DIR/target/swift/$RUST_PACKAGE" +mkdir -p "$SWIFT_FILES_DIR" +"$UNIFFI_BINDGEN_SWIFT" \ + --swift-sources "$REPO_DIR/target/aarch64-apple-darwin/release/$LIBRARY_NAME" \ + "$SWIFT_FILES_DIR" + +# Generate headers based on sample library +HEADER_FILES_DIR="$REPO_DIR/target/include/$RUST_PACKAGE" +"$UNIFFI_BINDGEN_SWIFT" --module-name "$XCFRAMEWORK_MODULE_NAME" \ + --headers --modulemap --modulemap-filename module.modulemap \ + "$REPO_DIR/target/aarch64-apple-darwin/release/$LIBRARY_NAME" \ + "$HEADER_FILES_DIR" + +mkdir -p "$XCFRAMEWORK_DIR" + +SNAKE_NAMES=() +CAMEL_NAMES=() +while IFS= read -r -d $'\0'; do + FILENAME="$REPLY" + SNAKE_NAME="$(basename "$FILENAME" | sed -e 's/\.swift//g')" + CAMEL_NAME="$(echo -n "$SNAKE_NAME" | python3 -c "print(''.join(x.title() for x in input().split('_')) + 'FFI')")" + SNAKE_NAMES+=("$SNAKE_NAME") + CAMEL_NAMES+=("$CAMEL_NAME") +done < <(find "$SWIFT_FILES_DIR" -name '*.swift' -print0) +MODULE_COUNT="${#SNAKE_NAMES[@]}" + +# Create fat library for iOS Simulator +IOS_SIM_FAT_LIBRARY="$REPO_DIR/target/apple-ios-sim/release/$LIBRARY_NAME" +printf "Creating iOS sim fat library at path '%s'\n" "$IOS_SIM_FAT_LIBRARY" +mkdir -p "$(dirname "$IOS_SIM_FAT_LIBRARY")" +lipo -create "$REPO_DIR/target/aarch64-apple-ios-sim/release/$LIBRARY_NAME" \ + "$REPO_DIR/target/x86_64-apple-ios/release/$LIBRARY_NAME" \ + -output "$IOS_SIM_FAT_LIBRARY" + +# Create fat library for macOS +MACOS_FAT_LIBRARY="$REPO_DIR/target/apple-macos/release/$LIBRARY_NAME" +printf "Creating macOS fat library at path '%s'\n" "$MACOS_FAT_LIBRARY" +mkdir -p "$(dirname "$MACOS_FAT_LIBRARY")" +lipo -create "$REPO_DIR/target/aarch64-apple-darwin/release/$LIBRARY_NAME" \ + "$REPO_DIR/target/x86_64-apple-darwin/release/$LIBRARY_NAME" \ + -output "$MACOS_FAT_LIBRARY" + +printf "Creating XCFramework at path '%s'\n" "$XCFRAMEWORK_DIR" +xcodebuild -create-xcframework \ + -library "$IOS_SIM_FAT_LIBRARY" \ + -headers "$HEADER_FILES_DIR" \ + -library "$MACOS_FAT_LIBRARY" \ + -headers "$HEADER_FILES_DIR" \ + -library "$REPO_DIR/target/aarch64-apple-ios/release/$LIBRARY_NAME" \ + -headers "$HEADER_FILES_DIR" \ + -output "$XCFRAMEWORK_DIR" + +PKG_FILE="$OUTPUT_DIR/Package.swift" + +cat >"$PKG_FILE" <>"$PKG_FILE" " products: [" + +for ((i = 0; i < "$MODULE_COUNT"; ++i)); do + if [[ "$i" != "0" ]]; then + printf >>"$PKG_FILE" ",\n" + else + printf >>"$PKG_FILE" "\n" + fi + printf >>"$PKG_FILE" " .library(\n" + printf >>"$PKG_FILE" " name: \"%s\", targets: [\"%s\"]\n" "${CAMEL_NAMES[$i]}" "${CAMEL_NAMES[$i]}" + printf >>"$PKG_FILE" " )" +done +printf >>"$PKG_FILE" "\n" + +cat >>"$PKG_FILE" <"$MODULE_SRC_DIR/${CAMEL_NAMES[$i]}.swift" \ + -e "s/${SNAKE_NAMES[$i]}FFI/$XCFRAMEWORK_MODULE_NAME/g" \ + -e "s/private var initializationResult/private nonisolated(unsafe) var initializationResult/g" \ + -e "s/static var vtable:/static nonisolated(unsafe) var vtable:/g" \ + -e "s/fileprivate static var handleMap =/fileprivate static nonisolated(unsafe) var handleMap =/g" \ + -e "s/fileprivate let uniffiContinuationHandleMap =/fileprivate nonisolated(unsafe) let uniffiContinuationHandleMap =/g" \ + -e "s/\(public struct [[:alnum:]]\{1,\}\)/\1: Sendable/g" \ + -e "s/\(public enum [[:alnum:]]\{1,\}\)/\1: Sendable/g" \ + -e "s/\(public struct FfiConverter[[:alnum:]]\{1,\}\): Sendable/\1/g" \ + "$SWIFT_FILES_DIR/${SNAKE_NAMES[$i]}.swift" + cat >>"$PKG_FILE" <>"$PKG_FILE" </dev/null && pwd)" +REPO_DIR="$SCRIPT_DIR/../../.." + +export MANIFEST_FILE="$REPO_DIR/Cargo.toml" +export RUST_PACKAGE="uniffi-fixture-ext-types" +export LIBRARY_NAME="libuniffi_ext_types_lib.a" + +# clean before generating +rm -rf "$REPO_DIR/target/swift" \ + "$REPO_DIR/target/swift-package" \ + "$REPO_DIR/target/apple-ios-sim" \ + "$REPO_DIR/target/apple-macos" + +"$REPO_DIR/examples/app/swift-package/bindgen.sh" + +(cd "$REPO_DIR/target/swift-package/UniffiFixtureExtTypesFFI"; swift build)