From c61efa37e5800c87a54d634ef439d8f230584af9 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 17 Dec 2024 21:41:07 +1300 Subject: [PATCH 1/3] Integrate uniffi-swift-helper --- .buildkite/pipeline.yml | 2 +- .buildkite/swift-test.sh | 3 + .buildkite/validate-cocoapods.sh | 4 +- .gitignore | 1 + Cargo.lock | 172 ++++++-- Cargo.toml | 6 +- Makefile | 54 +-- Package.swift | 132 ------ .../Sources/wordpress-api-wrapper/README.md | 3 - scripts/swift-bindings.sh | 47 --- swift_helper_cli/Cargo.toml | 8 + swift_helper_cli/src/main.rs | 3 + xcframework/Cargo.toml | 9 - xcframework/src/main.rs | 379 ------------------ 14 files changed, 178 insertions(+), 645 deletions(-) delete mode 100644 Package.swift delete mode 100644 native/swift/Sources/wordpress-api-wrapper/README.md delete mode 100755 scripts/swift-bindings.sh create mode 100644 swift_helper_cli/Cargo.toml create mode 100644 swift_helper_cli/src/main.rs delete mode 100644 xcframework/Cargo.toml delete mode 100644 xcframework/src/main.rs diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ce8dfbb4..cb7e1c05 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -66,7 +66,7 @@ steps: echo "--- :swift: Building xcframework" make xcframework - zip -r target/libwordpressFFI.xcframework.zip target/libwordpressFFI.xcframework + zip -r target/libwordpressFFI.xcframework.zip target/libwordpressFFI/libwordpressFFI.xcframework artifact_paths: - target/libwordpressFFI.xcframework.zip - native/swift/Sources/wordpress-api-wrapper/wp_api.swift diff --git a/.buildkite/swift-test.sh b/.buildkite/swift-test.sh index 2155606e..16ad6c84 100755 --- a/.buildkite/swift-test.sh +++ b/.buildkite/swift-test.sh @@ -6,6 +6,9 @@ set -euo pipefail export SKIP_PACKAGE_WP_API=true +echo "--- :swift: Generating Package.swift" +make generate-swift-package-manifest + function run_tests() { local platform; platform=$1 echo "--- :swift: Testing on $platform simulator" diff --git a/.buildkite/validate-cocoapods.sh b/.buildkite/validate-cocoapods.sh index f9695797..dd463707 100755 --- a/.buildkite/validate-cocoapods.sh +++ b/.buildkite/validate-cocoapods.sh @@ -24,12 +24,12 @@ Pod::Spec.new do |spec| spec.ios.deployment_target = '13.0' spec.osx.deployment_target = '11.0' - # zip -r swift-source-archive.zip native/swift target/libwordpressFFI.xcframework + # zip -r swift-source-archive.zip native/swift target/libwordpressFFI/libwordpressFFI.xcframework spec.source = { :http => "http://s3.com/WordPressAPI.zip" } spec.swift_version = '5.10' spec.source_files = 'native/swift/Sources/**/*.{swift}' - spec.vendored_frameworks = 'target/libwordpressFFI.xcframework' + spec.vendored_frameworks = 'target/libwordpressFFI/libwordpressFFI.xcframework' spec.pod_target_xcconfig = { 'SWIFT_PACKAGE_NAME' => 'WordPressAPI' diff --git a/.gitignore b/.gitignore index df83c0b9..6d1c4e7c 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ xcuserdata DerivedData fastlane/report.xml libwordPressFFI.xcframework* +/Package.swift # Auto-generated Swift Files native/swift/Sources/wordpress-api-wrapper/*.swift diff --git a/Cargo.lock b/Cargo.lock index cca586ca..332ab826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,7 +353,21 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.7", ] [[package]] @@ -559,9 +573,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", @@ -569,9 +583,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "figment" @@ -902,6 +916,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "hyper" version = "0.14.30" @@ -1239,9 +1262,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "linux-raw-sys" @@ -1539,6 +1568,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pear" version = "0.2.9" @@ -1835,6 +1870,47 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rinja" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" +dependencies = [ + "humansize", + "itoa", + "percent-encoding", + "rinja_derive", +] + +[[package]] +name = "rinja_derive" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" +dependencies = [ + "basic-toml", + "memchr", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "rinja_parser", + "rustc-hash", + "serde", + "syn", +] + +[[package]] +name = "rinja_parser" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" +dependencies = [ + "memchr", + "nom", + "serde", +] + [[package]] name = "rocket" version = "0.5.1" @@ -1964,6 +2040,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1975,9 +2057,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags", "errno", @@ -2326,6 +2408,14 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swift_helper_cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "uniffi-swift-helper", +] + [[package]] name = "syn" version = "2.0.90" @@ -2386,12 +2476,13 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys", ] @@ -2420,7 +2511,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +dependencies = [ + "thiserror-impl 2.0.7", ] [[package]] @@ -2434,6 +2534,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -2777,7 +2888,7 @@ checksum = "4cb08c58c7ed7033150132febe696bef553f891b1ede57424b40d87a89e3c170" dependencies = [ "anyhow", "camino", - "cargo_metadata", + "cargo_metadata 0.15.4", "clap", "uniffi_bindgen", "uniffi_build", @@ -2785,6 +2896,22 @@ dependencies = [ "uniffi_macros", ] +[[package]] +name = "uniffi-swift-helper" +version = "0.1.0" +source = "git+https://github.com/automattic/uniffi-swift-helper.git?branch=real-stuff#d48ae27a66727a8fcc526e3325c2a35a8ca9eeda" +dependencies = [ + "anyhow", + "cargo_metadata 0.19.1", + "clap", + "pathdiff", + "rinja", + "serde_json", + "tempfile", + "uniffi", + "uniffi_bindgen", +] + [[package]] name = "uniffi_bindgen" version = "0.28.3" @@ -2794,7 +2921,7 @@ dependencies = [ "anyhow", "askama", "camino", - "cargo_metadata", + "cargo_metadata 0.15.4", "fs-err", "glob", "goblin", @@ -2881,7 +3008,7 @@ checksum = "6a6f984f0781f892cc864a62c3a5c60361b1ccbd68e538e6c9fbced5d82268ac" dependencies = [ "anyhow", "camino", - "cargo_metadata", + "cargo_metadata 0.15.4", "fs-err", "once_cell", ] @@ -3318,7 +3445,7 @@ dependencies = [ "serde", "serde_json", "strum_macros", - "thiserror", + "thiserror 1.0.69", "uniffi", "url", "uuid", @@ -3379,7 +3506,7 @@ dependencies = [ "quote", "serde", "syn", - "thiserror", + "thiserror 1.0.69", "trybuild", "uniffi", ] @@ -3397,7 +3524,7 @@ dependencies = [ "strum", "strum_macros", "syn", - "thiserror", + "thiserror 1.0.69", "toml 0.8.15", "trybuild", "uniffi", @@ -3432,15 +3559,6 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" -[[package]] -name = "xcframework" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "serde_json", -] - [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 77201bb9..65bb6f72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "wp_derive_request_builder", "wp_serde_helper", "wp_uniffi_bindgen", - "xcframework", + "swift_helper_cli", ] resolver = "2" @@ -43,5 +43,9 @@ tokio = "1.42" toml = "0.8" trybuild = "1.0" uniffi = "0.28" +uniffi-swift-helper = "0.1.0" url = "2.5" uuid = "1.11" + +[patch.crates-io] +uniffi-swift-helper = { git = "https://github.com/automattic/uniffi-swift-helper.git", branch = "real-stuff" } diff --git a/Makefile b/Makefile index 458e7327..03cb753b 100644 --- a/Makefile +++ b/Makefile @@ -61,58 +61,22 @@ release-on-ci: @echo "Swift package will be released by https://buildkite.com/automattic/wordpress-rs/builds/$$(jq -r '.number' .build/buildkite_release_job_response.json)" @echo "Once that job finishes, Android libraries will be release by https://buildkite.com/automattic/wordpress-rs/builds?branch=$(WORDPRESS_RS_NEW_VERSION)" -apple-platform-targets-macos := x86_64-apple-darwin aarch64-apple-darwin -apple-platform-targets-ios := aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim -apple-platform-targets-tvos := aarch64-apple-tvos aarch64-apple-tvos-sim -apple-platform-targets-watchos := arm64_32-apple-watchos x86_64-apple-watchos-sim aarch64-apple-watchos-sim -apple-platform-targets := \ - $(apple-platform-targets-macos) \ - $(apple-platform-targets-ios) \ - $(apple-platform-targets-tvos) \ - $(apple-platform-targets-watchos) - ifeq ($(BUILDKITE), true) CARGO_PROFILE ?= release -CARGO_PROFILE_DIRNAME := release else CARGO_PROFILE ?= dev -CARGO_PROFILE_DIRNAME := debug endif -cargo_config_library = --config profile.$(CARGO_PROFILE).debug=true --config 'profile.$(CARGO_PROFILE).panic="abort"' - -# Set deployment targets for each platform -_build-apple-%-darwin: export MACOSX_DEPLOYMENT_TARGET=$(swift_package_platform_macos) -_build-apple-%-ios _build-apple-%-ios-sim: export IPHONEOS_DEPLOYMENT_TARGET=$(swift_package_platform_ios) -_build-apple-%-tvos _build-apple-%-tvos-sim: export TVOS_DEPLOYMENT_TARGET=$(swift_package_platform_tvos) -_build-apple-%-watchos _build-apple-%-watchos-sim: export WATCHOS_DEPLOYMENT_TARGET=$(swift_package_platform_watchos) - -# Use nightly toolchain for tvOS and watchOS -_build-apple-%-tvos _build-apple-%-tvos-sim _build-apple-%-watchos _build-apple-%-watchos-sim: \ - CARGO_OPTS = +$(rust_nightly_toolchain) -Z build-std=panic_abort,std - -# Build the library for a specific target -_build-apple-%: - cargo $(CARGO_OPTS) $(cargo_config_library) build --target $* --package wp_api --profile $(CARGO_PROFILE) - ./scripts/swift-bindings.sh target/$*/$(CARGO_PROFILE_DIRNAME)/libwp_api.a - -# Build the library for one single platform, including real device and simulator. -build-apple-platform-macos := $(addprefix _build-apple-,$(apple-platform-targets-macos)) -build-apple-platform-ios := $(addprefix _build-apple-,$(apple-platform-targets-ios)) -build-apple-platform-tvos := $(addprefix _build-apple-,$(apple-platform-targets-tvos)) -build-apple-platform-watchos := $(addprefix _build-apple-,$(apple-platform-targets-watchos)) - # Creating xcframework for one single platform, including real device and simulator. -xcframework-only-macos: $(build-apple-platform-macos) -xcframework-only-ios: $(build-apple-platform-ios) -xcframework-only-tvos: $(build-apple-platform-tvos) -xcframework-only-watchos: $(build-apple-platform-watchos) -xcframework-only-%: - cargo run --quiet --bin xcframework -- --profile $(CARGO_PROFILE) --targets $(apple-platform-targets-$*) +xcframework-only-macos: + cargo run -q --bin swift_helper_cli build --package wp_api --profile $(CARGO_PROFILE) --ffi-module-name libwordpressFFI --only-macos + +xcframework-only-ios: + cargo run -q --bin swift_helper_cli build --package wp_api --profile $(CARGO_PROFILE) --ffi-module-name libwordpressFFI --only-ios # Creating xcframework for all platforms. -xcframework-all: $(build-apple-platform-macos) $(build-apple-platform-ios) $(build-apple-platform-tvos) $(build-apple-platform-watchos) - cargo run --quiet --bin xcframework -- --profile $(CARGO_PROFILE) --targets $(apple-platform-targets) +xcframework-all: + cargo run -q --bin swift_helper_cli build --package wp_api --profile $(CARGO_PROFILE) --ffi-module-name libwordpressFFI ifeq ($(SKIP_PACKAGE_WP_API),true) xcframework: @@ -123,11 +87,13 @@ endif xcframework-package: xcframework-all rm -rf libwordpressFFI.xcframework.zip - ditto -c -k --sequesterRsrc --keepParent target/libwordpressFFI.xcframework/ libwordpressFFI.xcframework.zip + ditto -c -k --sequesterRsrc --keepParent target/libwordpressFFI/libwordpressFFI.xcframework/ libwordpressFFI.xcframework.zip xcframework-package-checksum: swift package compute-checksum libwordpressFFI.xcframework.zip | tee libwordpressFFI.xcframework.zip.checksum.txt +generate-swift-package-manifest: + cargo run -q --bin swift_helper_cli generate-package --package wp_api --ffi-module-name libwordpressFFI --project-name wordpress-rs --package-name-map wp_api:WordpressAPI docker-image-swift: docker build -t wordpress-rs-swift -f Dockerfile.swift . diff --git a/Package.swift b/Package.swift deleted file mode 100644 index cba698bc..00000000 --- a/Package.swift +++ /dev/null @@ -1,132 +0,0 @@ -// swift-tools-version: 6.0 - -import Foundation -import PackageDescription - -let libwordpressFFIVersion: WordPressRSVersion = .local - -#if os(Linux) -let libwordpressFFI: Target = .systemLibrary( - name: "libwordpressFFI", - path: "target/release/libwordpressFFI-linux/" - ) -#elseif os(macOS) -let libwordpressFFI: Target = libwordpressFFIVersion.target -#endif - -var package = Package( - name: "WordPressAPI", - platforms: [ - .iOS(.v13), - .macOS(.v11), - .tvOS(.v13), - .watchOS(.v8) - ], - products: [ - .library( - name: "WordPressAPI", - targets: ["WordPressAPI"] - ) - ], - dependencies: [], - targets: [ - .target( - name: "WordPressAPI", - dependencies: [ - .target(name: "WordPressAPIInternal") - ], - path: "native/swift/Sources/wordpress-api", - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency"), - ] - ), - .target( - name: "WordPressAPIInternal", - dependencies: [ - .target(name: libwordpressFFI.name) - ], - path: "native/swift/Sources/wordpress-api-wrapper", - exclude: [ - "README.md" - ], - swiftSettings: [ - .swiftLanguageMode(.v5) - ] - ), - libwordpressFFI, - .testTarget( - name: "WordPressAPITests", - dependencies: [ - .target(name: "WordPressAPI"), - .target(name: libwordpressFFI.name) - ], - path: "native/swift/Tests/wordpress-api", - resources: [.copy("Resources/Responses/")] - ) - ] -) - -// MARK: - Enable local development toolings - -let localDevelopment = libwordpressFFIVersion.isLocal - -if localDevelopment { - try enableSwiftLint() -} - -// MARK: - Helpers - -enum WordPressRSVersion { - case local - case release(version: String, checksum: String) - - var isLocal: Bool { - if case .local = self { - return true - } - return false - } - - var target: Target { - switch libwordpressFFIVersion { - case .local: - return .binaryTarget(name: "libwordpressFFI", path: "target/libwordpressFFI.xcframework") - case let .release(version, checksum): - return .binaryTarget( - name: "libwordpressFFI", - url: "https://cdn.a8c-ci.services/wordpress-rs/\(version)/libwordpressFFI.xcframework.zip", - checksum: checksum - ) - } - } -} - -// Add SwiftLint to the package so that we can see linting issues directly from Xcode. -@MainActor -func enableSwiftLint() throws { -#if os(macOS) - let filePath = URL(string:"./.swiftlint.yml", relativeTo: URL(filePath: #filePath))! - let version = try String(contentsOf: filePath, encoding: .utf8) - .split(separator: "\n") - .first(where: { $0.starts(with: "swiftlint_version") })? - .split(separator: ":") - .last? - .trimmingCharacters(in: .whitespaces) - guard let version else { - fatalError("Can't find swiftlint_version in .swiftlint.yml") - } - - package.dependencies.append(.package(url: "https://github.com/realm/SwiftLint", exact: .init(version)!)) - - var platforms = package.platforms ?? [] - if let mac = platforms.firstIndex(where: { $0 == .macOS(.v11) }) { - platforms.remove(at: mac) - platforms.append(.macOS(.v12)) - } - package.platforms = platforms - - if let target = package.targets.first(where: { $0.name == "WordPressAPI" }) { - target.plugins = (target.plugins ?? []) + [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint")] - } -#endif -} diff --git a/native/swift/Sources/wordpress-api-wrapper/README.md b/native/swift/Sources/wordpress-api-wrapper/README.md deleted file mode 100644 index 38a7f7f2..00000000 --- a/native/swift/Sources/wordpress-api-wrapper/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# wordpress-api-wrapper - -The contents of this directory are auto-generated by Rust and uniffi – you should not try to change them manually! diff --git a/scripts/swift-bindings.sh b/scripts/swift-bindings.sh deleted file mode 100755 index d55c7c82..00000000 --- a/scripts/swift-bindings.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -if [ $# -ne 1 ]; then - echo "Usage: $0 /path/to/library" - exit 1 -fi - -module_name="libwordpressFFI" -library_path=$1 -output_dir="$(dirname "$library_path")/swift-bindings" -rm -rf "$output_dir" && mkdir "$output_dir" - -cargo run --release --quiet --bin wp_uniffi_bindgen generate \ - --library "$library_path" \ - --out-dir "$output_dir" \ - --language swift - -# The search-and-replace below can be removed after updating to a uniffi-rs -# version that includes this PR https://github.com/mozilla/uniffi-rs/pull/2341 -for swift_binding in "$output_dir"/*.swift; do - options=("-i") - if [[ $(uname) == "Darwin" ]]; then - options+=("") - fi - sed "${options[@]}" 's/^protocol UniffiForeignFutureTask /fileprivate protocol UniffiForeignFutureTask /' "$swift_binding" -done - -mv "$output_dir"/*.swift native/swift/Sources/wordpress-api-wrapper/ - -header_dir="$output_dir/Headers" -mkdir -p "$header_dir" - -{ - for header_file in "$output_dir"/*.h; do - echo "#include \"$(basename "$header_file")\"" - done -} > "$header_dir/$module_name.h" -mv "$output_dir"/*.h "$header_dir/" - -cat < "$header_dir/module.modulemap" -module $module_name { - header "$module_name.h" - export * -} -EOF diff --git a/swift_helper_cli/Cargo.toml b/swift_helper_cli/Cargo.toml new file mode 100644 index 00000000..fd22a517 --- /dev/null +++ b/swift_helper_cli/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "swift_helper_cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +uniffi-swift-helper = { workspace = true } +anyhow = { version = "1.0" } diff --git a/swift_helper_cli/src/main.rs b/swift_helper_cli/src/main.rs new file mode 100644 index 00000000..2a23995f --- /dev/null +++ b/swift_helper_cli/src/main.rs @@ -0,0 +1,3 @@ +fn main() -> anyhow::Result<()> { + uniffi_swift_helper::cli_main() +} diff --git a/xcframework/Cargo.toml b/xcframework/Cargo.toml deleted file mode 100644 index 5ec71cdf..00000000 --- a/xcframework/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "xcframework" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = { workspace = true } -clap = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } diff --git a/xcframework/src/main.rs b/xcframework/src/main.rs deleted file mode 100644 index 50f8ad22..00000000 --- a/xcframework/src/main.rs +++ /dev/null @@ -1,379 +0,0 @@ -use anyhow::{Context, Result}; -use clap::*; -use std::collections::HashMap; -use std::fmt::Display; -use std::path::{Path, PathBuf}; -use std::process::Command; - -const XCFRAMEWORK_OUTPUT_PATH: &str = "target/libwordpressFFI.xcframework"; -const LIBRARY_FILENAME: &str = "libwordpress.a"; - -fn main() -> Result<()> { - CreateXCFramework::parse().run() -} - -#[derive(Debug, Parser)] -pub struct CreateXCFramework { - // Non-empty list of targets - #[clap( - long, - num_args = 1.., - required = true, - help = "List of targets whose static libraries should be included in the xcframework" - )] - targets: Vec, - - #[clap( - long, - default_value = "release", - help = "Cargo profile used to build the targets" - )] - profile: String, -} - -impl CreateXCFramework { - fn run(&self) -> Result<()> { - let temp_dir = std::env::temp_dir().join("wp-rs-xcframework"); - recreate_directory(&temp_dir)?; - - XCFramework::new(&self.targets, &self.profile)?.create(&temp_dir)?; - - Ok(()) - } -} - -// Represent a xcframework that contains static libraries for multiple platforms. -// -// Since `xcodebuild -create-xcframework` command requires its `-libraray` not -// having duplicated platform. This type along with `LibraryGroup` and `Slice` -// work together to make it easier to create a xcframework. -struct XCFramework { - libraries: Vec, -} - -// Represent a group of static libraries that are built for the same platform. -struct LibraryGroup { - id: LibraryGroupId, - slices: Vec, -} - -// Represent a thin static library which is built with `cargo build --target --profile ` -struct Slice { - target: String, - profile: String, -} - -impl XCFramework { - fn new(targets: &Vec, profile: &str) -> Result { - let mut groups = HashMap::::new(); - for target in targets { - let id = LibraryGroupId::from_target(target)?; - let id_clone = id.clone(); - groups - .entry(id) - .or_insert(LibraryGroup { - id: id_clone, - slices: Vec::new(), - }) - .slices - .push(Slice { - target: target.clone(), - profile: profile.to_owned(), - }); - } - - Ok(Self { - libraries: groups.into_values().collect(), - }) - } - - fn create(&self, temp_dir: &Path) -> Result { - self.preview(); - - let temp_dest = self.create_xcframework(temp_dir)?; - self.patch_xcframework(&temp_dest)?; - - let dest = PathBuf::from(XCFRAMEWORK_OUTPUT_PATH); - recreate_directory(&dest)?; - std::fs::rename(temp_dest, &dest).with_context(|| "Failed to move xcframework")?; - - println!("xcframework created at {}", &dest.display()); - Ok(dest) - } - - fn preview(&self) { - println!("Creating xcframework to include the following targets:"); - for lib in &self.libraries { - println!(" Platform: {}", lib.id); - for slice in &lib.slices { - println!(" - {}", slice.target); - } - } - } - - fn create_xcframework(&self, temp_dir: &Path) -> Result { - let temp_dest = temp_dir.join("libwordpressFFI.xcframework"); - std::fs::remove_dir_all(&temp_dest).ok(); - - let library_args: Result> = self - .libraries - .iter() - .map(|library| { - let lib = library.create(temp_dir)?; - let header = library.headers_dir()?; - Ok((lib, header)) - }) - .collect(); - let library_args = library_args?; - - let library_args = library_args.iter().flat_map(|(lib, headers)| { - [ - "-library".as_ref(), - lib.as_os_str(), - "-headers".as_ref(), - headers.as_os_str(), - ] - }); - Command::new("xcodebuild") - .arg("-create-xcframework") - .args(library_args) - .arg("-output") - .arg(&temp_dest) - .successful_output()?; - - Ok(temp_dest) - } - - // Fixes an issue including the XCFramework in an Xcode project that already contains an XCFramework: https://github.com/jessegrosjean/module-map-error - fn patch_xcframework(&self, temp_dir: &Path) -> Result<()> { - println!("Patching XCFramework to have a unique header directory"); - - for dir_entry in std::fs::read_dir(temp_dir)? { - let path = dir_entry.expect("Invalid Path").path(); - if path.is_dir() { - let headers_dir = temp_dir.join(&path).join("Headers"); - let non_lib_files: Vec = std::fs::read_dir(&headers_dir)? - .flat_map(|f| f.ok()) - .filter_map(|f| { - if f.path().ends_with(".a") { - None - } else { - Some(f.path()) - } - }) - .collect(); - - let new_headers_dir = headers_dir.join("libwordpressFFI"); - recreate_directory(&new_headers_dir)?; - - for file in non_lib_files { - std::fs::rename(&file, new_headers_dir.join(file.file_name().unwrap()))?; - } - } - } - - Ok(()) - } -} - -impl LibraryGroup { - fn create(&self, temp_dir: &Path) -> Result { - let mut libraries: Vec = Vec::new(); - for slice in &self.slices { - libraries.push(slice.create(temp_dir)?); - } - - let dir = temp_dir.join(self.id.to_string()); - recreate_directory(&dir)?; - - let dest = dir.join(LIBRARY_FILENAME); - Command::new("xcrun") - .arg("lipo") - .arg("-create") - .args(libraries) - .arg("-output") - .arg(&dest) - .successful_output()?; - - Ok(dest) - } - - fn headers_dir(&self) -> Result { - let slice = self - .slices - .first() - .with_context(|| "No slices in library group")?; - let path = slice.built_product_dir().join("swift-bindings/headers"); - if !path.exists() { - anyhow::bail!("Headers not found: {}", path.display()) - } - Ok(path) - } -} - -impl Slice { - fn create(&self, temp_dir: &Path) -> Result { - let libs = self.built_libraries(); - - // If there are more static libraries (a.k.a cargo packages), we'll - // need to bundle them together into one static library. - // At the moment, we only have one libwp_api, so we can just copy it. - assert!( - libs.len() == 1, - "Expected exactly one library for each slice" - ); - - let lib = &libs[0]; - if !lib.exists() { - anyhow::bail!("Library not found: {}", lib.display()) - } - - let dir = temp_dir.join(&self.target); - recreate_directory(&dir)?; - - let dest = dir.join(LIBRARY_FILENAME); - std::fs::copy(lib, &dest) - .with_context(|| format!("Failed to copy {} to {}", lib.display(), dest.display()))?; - - Ok(dest) - } - - /// Returns the directory where the built static libraries are located. - fn built_product_dir(&self) -> PathBuf { - let mut target_dir: PathBuf = ["target", &self.target].iter().collect(); - if self.profile == "dev" { - target_dir.push("debug"); - } else { - target_dir.push(&self.profile); - } - - target_dir - } - - fn built_libraries(&self) -> Vec { - vec![self.built_product_dir().join("libwp_api.a")] - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -struct LibraryGroupId { - os: ApplePlatform, - is_sim: bool, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -enum ApplePlatform { - MacOS, - #[allow(clippy::upper_case_acronyms)] - IOS, - TvOS, - WatchOS, -} - -impl TryFrom<&str> for ApplePlatform { - type Error = anyhow::Error; - - fn try_from(s: &str) -> std::result::Result { - match s { - "darwin" => Ok(ApplePlatform::MacOS), - "ios" => Ok(ApplePlatform::IOS), - "tvos" => Ok(ApplePlatform::TvOS), - "watchos" => Ok(ApplePlatform::WatchOS), - _ => anyhow::bail!("Unknown Apple platform: {}", s), - } - } -} - -impl Display for ApplePlatform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let name = match self { - ApplePlatform::MacOS => "macos", - ApplePlatform::IOS => "ios", - ApplePlatform::TvOS => "tvos", - ApplePlatform::WatchOS => "watchos", - }; - write!(f, "{}", name) - } -} - -impl LibraryGroupId { - fn from_target(target: &str) -> Result { - let mut parts = target.split('-'); - _ /* arch */= parts.next(); - if parts.next() != Some("apple") { - anyhow::bail!("{} is not an Apple platform", target) - } - - let os: ApplePlatform = parts - .next() - .with_context(|| format!("No OS in target: {}", target))? - .try_into()?; - - let output = Command::new("rustc") - .env("RUSTC_BOOTSTRAP", "1") - .args([ - "-Z", - "unstable-options", - "--print", - "target-spec-json", - "--target", - ]) - .arg(target) - .successful_output()?; - let json = serde_json::from_slice::(&output.stdout) - .with_context(|| "Failed to parse command output as JSON")?; - let llvm_target = json - .get("llvm-target") - .and_then(|t| t.as_str()) - .with_context(|| "No llvm-target in command output")?; - - Ok(Self { - os, - is_sim: llvm_target.ends_with("-simulator"), - }) - } -} - -impl Display for LibraryGroupId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.os)?; - - if self.is_sim { - write!(f, "-sim") - } else { - Ok(()) - } - } -} - -trait ExecuteCommand { - fn successful_output(&mut self) -> Result; -} - -impl ExecuteCommand for Command { - fn successful_output(&mut self) -> Result { - let output = self - .output() - .with_context(|| format!("Command failed: $ {:?}", self))?; - if output.status.success() { - Ok(output) - } else { - anyhow::bail!( - "Command failed with exit code: {}\nstdout: {:?}\nstderr: {:?}\n$ {:?}", - output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr), - self - ) - } - } -} - -fn recreate_directory(dir: &PathBuf) -> Result<()> { - if dir.exists() { - std::fs::remove_dir_all(dir) - .with_context(|| format!("Failed to remove directory at {:?}", dir))?; - } - - std::fs::create_dir_all(dir).with_context(|| format!("Failed to create directory: {:?}", dir)) -} From 3b149f18e0b1d43bb4337e9960dbd6e4142cb0e4 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Thu, 19 Dec 2024 11:28:51 +1300 Subject: [PATCH 2/3] Update to the latest uniffi-swift-helper --- Cargo.lock | 4 ++-- Makefile | 18 +++++++----------- wp_api/uniffi.toml | 1 + 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 332ab826..d8762874 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2899,7 +2899,7 @@ dependencies = [ [[package]] name = "uniffi-swift-helper" version = "0.1.0" -source = "git+https://github.com/automattic/uniffi-swift-helper.git?branch=real-stuff#d48ae27a66727a8fcc526e3325c2a35a8ca9eeda" +source = "git+https://github.com/automattic/uniffi-swift-helper.git?branch=real-stuff#dea8a44077fab77d4f2857f98adb1fb92e8cda12" dependencies = [ "anyhow", "cargo_metadata 0.19.1", @@ -2907,7 +2907,7 @@ dependencies = [ "pathdiff", "rinja", "serde_json", - "tempfile", + "toml 0.8.15", "uniffi", "uniffi_bindgen", ] diff --git a/Makefile b/Makefile index 03cb753b..88c01ce5 100644 --- a/Makefile +++ b/Makefile @@ -69,14 +69,14 @@ endif # Creating xcframework for one single platform, including real device and simulator. xcframework-only-macos: - cargo run -q --bin swift_helper_cli build --package wp_api --profile $(CARGO_PROFILE) --ffi-module-name libwordpressFFI --only-macos + cargo run -q --bin swift_helper_cli build --profile $(CARGO_PROFILE) --only-macos xcframework-only-ios: - cargo run -q --bin swift_helper_cli build --package wp_api --profile $(CARGO_PROFILE) --ffi-module-name libwordpressFFI --only-ios + cargo run -q --bin swift_helper_cli build --profile $(CARGO_PROFILE) --only-ios # Creating xcframework for all platforms. xcframework-all: - cargo run -q --bin swift_helper_cli build --package wp_api --profile $(CARGO_PROFILE) --ffi-module-name libwordpressFFI + cargo run -q --bin swift_helper_cli build --profile $(CARGO_PROFILE) ifeq ($(SKIP_PACKAGE_WP_API),true) xcframework: @@ -93,17 +93,13 @@ xcframework-package-checksum: swift package compute-checksum libwordpressFFI.xcframework.zip | tee libwordpressFFI.xcframework.zip.checksum.txt generate-swift-package-manifest: - cargo run -q --bin swift_helper_cli generate-package --package wp_api --ffi-module-name libwordpressFFI --project-name wordpress-rs --package-name-map wp_api:WordpressAPI + cargo run -q --bin swift_helper_cli generate-package --project-name wordpress-rs docker-image-swift: docker build -t wordpress-rs-swift -f Dockerfile.swift . swift-linux-library: - cargo build --release --package wp_api - ./scripts/swift-bindings.sh target/release/libwp_api.a - mkdir -p target/release/libwordpressFFI-linux - cp target/release/swift-bindings/Headers/* target/release/libwordpressFFI-linux/ - cp target/release/libwp_api.a target/release/libwordpressFFI-linux/ + cargo run -q --bin swift_helper_cli build --profile $(CARGO_PROFILE) swift-example-app: swift-example-app-mac swift-example-app-ios @@ -119,8 +115,8 @@ test-swift: test-swift-linux: docker-image-swift docker run $(docker_opts_shared) -it wordpress-rs-swift make test-swift-linux-in-docker -test-swift-linux-in-docker: swift-linux-library - swift test -Xlinker -Ltarget/release/libwordpressFFI-linux -Xlinker -lwp_api +test-swift-linux-in-docker: swift-linux-library generate-swift-package-manifest + swift test -Xlinker -Ltarget/libwordpressFFI/linux -Xlinker -lwordpressFFI test-swift-darwin: xcframework swift test diff --git a/wp_api/uniffi.toml b/wp_api/uniffi.toml index 09850341..f5d2ec62 100644 --- a/wp_api/uniffi.toml +++ b/wp_api/uniffi.toml @@ -10,3 +10,4 @@ ffi_module_filename = "wp_api_uniffi" generate_module_map = false generate_immutable_records = true experimental_sendable_value_types = true +wp_spm_public_module_name = "WordPressAPI" From fb90e1ba1ff31ce25e7b42ba9c240d305425067b Mon Sep 17 00:00:00 2001 From: Tony Li Date: Thu, 19 Dec 2024 11:29:08 +1300 Subject: [PATCH 3/3] Remove /Package.resolved from git --- .gitignore | 1 + Package.resolved | 87 ------------------------------------------------ 2 files changed, 1 insertion(+), 87 deletions(-) delete mode 100644 Package.resolved diff --git a/.gitignore b/.gitignore index 6d1c4e7c..e30c380e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ DerivedData fastlane/report.xml libwordPressFFI.xcframework* /Package.swift +/Package.resolved # Auto-generated Swift Files native/swift/Sources/wordpress-api-wrapper/*.swift diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 1ec466e7..00000000 --- a/Package.resolved +++ /dev/null @@ -1,87 +0,0 @@ -{ - "originHash" : "4c30bcea863fdb29514c7daf20b8911f79465cc6626c1c7fdc311c43d9a8367a", - "pins" : [ - { - "identity" : "collectionconcurrencykit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", - "state" : { - "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", - "version" : "0.2.0" - } - }, - { - "identity" : "cryptoswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", - "state" : { - "revision" : "c9c3df6ab812de32bae61fc0cd1bf6d45170ebf0", - "version" : "1.8.2" - } - }, - { - "identity" : "sourcekitten", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/SourceKitten.git", - "state" : { - "revision" : "fd4df99170f5e9d7cf9aa8312aa8506e0e7a44e7", - "version" : "0.35.0" - } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", - "state" : { - "revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b", - "version" : "1.4.0" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftlang/swift-syntax.git", - "state" : { - "revision" : "cb53fa1bd3219b0b23ded7dfdd3b2baff266fd25", - "version" : "600.0.0" - } - }, - { - "identity" : "swiftlint", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/SwiftLint", - "state" : { - "revision" : "25f2776977e663305bee71309ea1e34d435065f1", - "version" : "0.57.1" - } - }, - { - "identity" : "swiftytexttable", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", - "state" : { - "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", - "version" : "0.9.0" - } - }, - { - "identity" : "swxmlhash", - "kind" : "remoteSourceControl", - "location" : "https://github.com/drmohundro/SWXMLHash.git", - "state" : { - "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", - "version" : "7.0.2" - } - }, - { - "identity" : "yams", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/Yams.git", - "state" : { - "revision" : "9234124cff5e22e178988c18d8b95a8ae8007f76", - "version" : "5.1.2" - } - } - ], - "version" : 3 -}