From 882b206144803568b50fa6f6c8d3613afdd69ec8 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 15 Nov 2022 13:06:33 +0100 Subject: [PATCH] feat(xtask): build xcframework * Move swift build scripts into xtask (#1201) * fix(ffi): use target_path from `cargo metadata` rather than guessing * ci(ffi): install necessary target arch for build-framework test * feat(xtask): copy to target without rsync. --- .github/workflows/bindings_ci.yml | 12 +- Cargo.lock | 1 + bindings/apple/build_xcframework.sh | 87 +--------- bindings/apple/debug_build_xcframework.sh | 63 +------- bindings/matrix-sdk-ffi/src/platform.rs | 3 + xtask/Cargo.toml | 1 + xtask/src/swift.rs | 187 ++++++++++++++++++---- xtask/src/workspace.rs | 11 ++ 8 files changed, 184 insertions(+), 181 deletions(-) diff --git a/.github/workflows/bindings_ci.yml b/.github/workflows/bindings_ci.yml index 9f048a818d1..23bed08e842 100644 --- a/.github/workflows/bindings_ci.yml +++ b/.github/workflows/bindings_ci.yml @@ -243,6 +243,9 @@ jobs: profile: minimal override: true + - name: Install aarch64-apple-ios target + run: rustup target install aarch64-apple-ios + - name: Load cache uses: Swatinem/rust-cache@v1 @@ -252,15 +255,12 @@ jobs: path: target/debug/xtask key: xtask-macos-${{ hashFiles('Cargo.toml', 'xtask/**') }} - - name: Install Uniffi - uses: actions-rs/cargo@v1 - with: - command: install - args: uniffi_bindgen --git https://github.com/mozilla/uniffi-rs --rev ${{ env.UNIFFI_REV }} - - name: Build library & bindings run: target/debug/xtask swift build-library - name: Run XCTests working-directory: bindings/apple run: swift test + + - name: Build Framework + run: cargo xtask swift build-framework --only-target=aarch64-apple-ios \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7d1691c522f..1a83c107df9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5596,6 +5596,7 @@ version = "0.1.0" dependencies = [ "camino", "clap 4.0.18", + "fs_extra", "serde", "serde_json", "uniffi_bindgen", diff --git a/bindings/apple/build_xcframework.sh b/bindings/apple/build_xcframework.sh index 222d0e48d6f..46d7474a95e 100755 --- a/bindings/apple/build_xcframework.sh +++ b/bindings/apple/build_xcframework.sh @@ -2,89 +2,6 @@ set -eEu cd "$(dirname "$0")" +cd ../.. -# Path to the repo root -SRC_ROOT=../.. - -TARGET_DIR="${SRC_ROOT}/target" - -GENERATED_DIR="${SRC_ROOT}/generated" -mkdir -p ${GENERATED_DIR} - -REL_FLAG="--release" -REL_TYPE_DIR="release" - -# Build static libs for all the different architectures - -# iOS -echo -e "Building for iOS [1/5]" -cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios" - -# MacOS -echo -e "\nBuilding for macOS (Apple Silicon) [2/5]" -cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-darwin" -echo -e "\nBuilding for macOS (Intel) [3/5]" -cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-darwin" - -# iOS Simulator -echo -e "\nBuilding for iOS Simulator (Apple Silicon) [4/5]" -cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim" -echo -e "\nBuilding for iOS Simulator (Intel) [5/5]" -cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios" - -echo -e "\nCreating XCFramework" -# Lipo together the libraries for the same platform - -# MacOS -lipo -create \ - "${TARGET_DIR}/x86_64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - "${TARGET_DIR}/aarch64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - -output "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" - -# iOS Simulator -lipo -create \ - "${TARGET_DIR}/x86_64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - "${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - -output "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" - - -# Generate uniffi files -# Architecture for the .a file argument doesn't matter, since the API is the same on all -uniffi-bindgen generate \ - --language swift \ - --lib-file "${TARGET_DIR}/x86_64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - --out-dir ${GENERATED_DIR} \ - "${SRC_ROOT}/bindings/matrix-sdk-ffi/src/api.udl" - -# Move them to the right place -HEADERS_DIR=${GENERATED_DIR}/headers -mkdir -p ${HEADERS_DIR} - -mv ${GENERATED_DIR}/*.h ${HEADERS_DIR} - -# Rename and move modulemap to the right place -mv ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR}/module.modulemap - -SWIFT_DIR="${GENERATED_DIR}/swift" -mkdir -p ${SWIFT_DIR} - -mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR} - -# Build the xcframework - -if [ -d "${GENERATED_DIR}/MatrixSDKFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKFFI.xcframework"; fi - -xcodebuild -create-xcframework \ - -library "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" \ - -headers ${HEADERS_DIR} \ - -library "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" \ - -headers ${HEADERS_DIR} \ - -library "${TARGET_DIR}/aarch64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - -headers ${HEADERS_DIR} \ - -output "${GENERATED_DIR}/MatrixSDKFFI.xcframework" - -# Cleanup - -if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a"; fi -if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"; fi -if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi +cargo xtask swift build-framework --release $* diff --git a/bindings/apple/debug_build_xcframework.sh b/bindings/apple/debug_build_xcframework.sh index 920a2d47498..1c8b89c0369 100755 --- a/bindings/apple/debug_build_xcframework.sh +++ b/bindings/apple/debug_build_xcframework.sh @@ -19,19 +19,6 @@ else echo "Running debug build" fi -echo "Active architecture ${ACTIVE_ARCH}" - -# Path to the repo root -SRC_ROOT=../.. - -TARGET_DIR="${SRC_ROOT}/target" - -GENERATED_DIR="${SRC_ROOT}/bindings/apple/generated" -mkdir -p ${GENERATED_DIR} - -REL_FLAG="" -REL_TYPE_DIR="debug" - # iOS Simulator arm64 if [ "$ACTIVE_ARCH" = "arm64" ]; then TARGET="aarch64-apple-ios-sim" @@ -39,52 +26,6 @@ if [ "$ACTIVE_ARCH" = "arm64" ]; then else TARGET="x86_64-apple-ios" fi +echo "Active architecture ${ACTIVE_ARCH}" -cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "$TARGET" - -lipo -create \ - "${TARGET_DIR}/$TARGET/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - -output "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" - -# Generate uniffi files -uniffi-bindgen generate \ - --language swift \ - --lib-file "${TARGET_DIR}/$TARGET/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \ - --out-dir ${GENERATED_DIR} \ - "${SRC_ROOT}/bindings/matrix-sdk-ffi/src/api.udl" - -# Move them to the right place -HEADERS_DIR=${GENERATED_DIR}/headers -mkdir -p ${HEADERS_DIR} - -mv ${GENERATED_DIR}/*.h ${HEADERS_DIR} - -# Rename and move modulemap to the right place -mv ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR}/module.modulemap - -SWIFT_DIR="${GENERATED_DIR}/swift" -mkdir -p ${SWIFT_DIR} - -mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR} - -# Build the xcframework - -if [ -d "${GENERATED_DIR}/MatrixSDKFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKFFI.xcframework"; fi - -xcodebuild -create-xcframework \ - -library "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" \ - -headers ${HEADERS_DIR} \ - -output "${GENERATED_DIR}/MatrixSDKFFI.xcframework" - -# Cleanup - -if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"; fi -if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi - -if [ "$IS_CI" = false ] ; then - echo "Preparing matrix-rust-components-swift" - - # Debug -> Copy generated files over to ../../../matrix-rust-components-swift - rsync -a --delete "${GENERATED_DIR}/MatrixSDKFFI.xcframework" "${SRC_ROOT}/../matrix-rust-components-swift/" - rsync -a --delete "${GENERATED_DIR}/swift/" "${SRC_ROOT}/../matrix-rust-components-swift/Sources/MatrixRustSDK" -fi +cargo xtask swift build-framework --sim-only-target=${TARGET} $* diff --git a/bindings/matrix-sdk-ffi/src/platform.rs b/bindings/matrix-sdk-ffi/src/platform.rs index 4b7d63efed0..b1ed9702b13 100644 --- a/bindings/matrix-sdk-ffi/src/platform.rs +++ b/bindings/matrix-sdk-ffi/src/platform.rs @@ -26,6 +26,9 @@ mod android { #[cfg(target_os = "ios")] mod ios { + use std::io; + + use tracing_subscriber::{fmt, prelude::*, EnvFilter}; pub fn setup_tracing(configuration: String) { tracing_subscriber::registry() .with(EnvFilter::new(configuration)) diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 1a2065f8c86..04d48d27990 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -13,5 +13,6 @@ camino = "1.0.8" clap = { version = "4.0.18", features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" +fs_extra = "1" uniffi_bindgen = { workspace = true } xshell = "0.1.17" diff --git a/xtask/src/swift.rs b/xtask/src/swift.rs index 76b5799a16f..2b95c2dea72 100644 --- a/xtask/src/swift.rs +++ b/xtask/src/swift.rs @@ -1,4 +1,7 @@ -use std::fs; +use std::{ + fs::{create_dir_all, remove_dir_all, remove_file, rename}, + path::PathBuf, +}; use clap::{Args, Subcommand}; use xshell::{cmd, pushd}; @@ -17,7 +20,18 @@ enum SwiftCommand { /// Builds the SDK for Swift as a static lib. BuildLibrary, /// Builds the SDK for Swift as an XCFramework. - BuildFramework, + BuildFramework { + /// Build in release mode + #[clap(long)] + release: bool, + /// Build the given target only + #[clap(long)] + only_target: Option, + /// Move the generated xcframework and swift sources into the given + /// components-folder + #[clap(long)] + components_path: Option, + }, } impl SwiftArgs { @@ -26,7 +40,9 @@ impl SwiftArgs { match self.cmd { SwiftCommand::BuildLibrary => build_library(), - SwiftCommand::BuildFramework => build_xcframework(), + SwiftCommand::BuildFramework { release, components_path, only_target } => { + build_xcframework(release, only_target, components_path) + } } } } @@ -38,54 +54,167 @@ fn build_library() -> Result<()> { let static_lib_filename = "libmatrix_sdk_ffi.a"; let root_directory = workspace::root_path()?; - let target_directory = root_directory.join("target"); + let target_directory = workspace::target_path()?; let ffi_directory = root_directory.join("bindings/apple/generated/matrix_sdk_ffi"); - let swift_directory = root_directory.join("bindings/apple/generated/swift"); - let udl_file = camino::Utf8PathBuf::from_path_buf( - root_directory.join("bindings/matrix-sdk-ffi/src/api.udl"), - ) - .expect("Root Dir contains non-utf8 characters"); - let outdir_overwrite = camino::Utf8PathBuf::from_path_buf(ffi_directory.clone()) - .expect("Root Dir contains non-utf8 characters"); - let library_file = camino::Utf8PathBuf::from_path_buf(ffi_directory.join(static_lib_filename)) - .expect("Root Dir contains non-utf8 characters"); + let library_file = ffi_directory.join(static_lib_filename); - fs::create_dir_all(ffi_directory.as_path())?; - fs::create_dir_all(swift_directory.as_path())?; + create_dir_all(ffi_directory.as_path())?; cmd!("cargo build -p matrix-sdk-ffi").run()?; - fs::rename( + rename( target_directory.join(release_type).join(static_lib_filename), ffi_directory.join(static_lib_filename), )?; + let swift_directory = root_directory.join("bindings/apple/generated/swift"); + create_dir_all(swift_directory.as_path())?; - uniffi_bindgen::generate_bindings( - udl_file.as_path(), - None, - vec!["swift"], - Some(outdir_overwrite.as_path()), - Some(library_file.as_path()), - false, - )?; + generate_uniffi(&library_file, &ffi_directory)?; let module_map_file = ffi_directory.join("module.modulemap"); if module_map_file.exists() { - fs::remove_file(module_map_file.as_path())?; + remove_file(module_map_file.as_path())?; } // TODO: Find the modulemap in the ffi directory. - fs::rename(ffi_directory.join("matrix_sdk_ffiFFI.modulemap"), module_map_file)?; + rename(ffi_directory.join("matrix_sdk_ffiFFI.modulemap"), module_map_file)?; // TODO: Move all swift files. - fs::rename( + rename( ffi_directory.join("matrix_sdk_ffi.swift"), swift_directory.join("matrix_sdk_ffi.swift"), )?; + Ok(()) +} + +fn generate_uniffi(library_file: &PathBuf, ffi_directory: &PathBuf) -> Result<()> { + let root_directory = workspace::root_path()?; + let udl_file = camino::Utf8PathBuf::from_path_buf( + root_directory.join("bindings/matrix-sdk-ffi/src/api.udl"), + ) + .unwrap(); + let outdir_overwrite = camino::Utf8PathBuf::from_path_buf(ffi_directory.clone()).unwrap(); + let library_path = camino::Utf8PathBuf::from_path_buf(library_file.clone()).unwrap(); + uniffi_bindgen::generate_bindings( + udl_file.as_path(), + None, + vec!["swift"], + Some(outdir_overwrite.as_path()), + Some(library_path.as_path()), + false, + )?; Ok(()) } -fn build_xcframework() -> Result<()> { - println!("XCFramework not yet implemented."); +fn build_for_target(target: &str, release: bool) -> Result { + let mut cmd = cmd!("cargo build -p matrix-sdk-ffi --target {target}"); + if release { + cmd = cmd.arg("--release"); + } + cmd.run()?; + Ok(workspace::target_path()? + .join(target) + .join(if release { "release" } else { "debug" }) + .join("libmatrix_sdk_ffi.a")) +} + +fn build_xcframework( + release_mode: bool, + only_target: Option, + components_path: Option, +) -> Result<()> { + let root_dir = workspace::root_path()?; + let generated_dir = root_dir.join("generated"); + let headers_dir = generated_dir.join("ls"); + let swift_dir = generated_dir.join("swift"); + create_dir_all(headers_dir.clone())?; + create_dir_all(swift_dir.clone())?; + + let (libs, uniff_lib_path) = if let Some(target) = only_target { + println!("-- Building for {target} 1/1"); + let build_path = build_for_target(target.as_str(), release_mode)?; + + (vec![build_path.clone()], build_path) + } else { + println!("-- Building for iOS [1/5]"); + let ios_path = build_for_target("aarch64-apple-ios", release_mode)?; + + println!("-- Building for macOS (Apple Silicon) [2/5]"); + let darwin_arm_path = build_for_target("aarch64-apple-darwin", release_mode)?; + println!("-- Building for macOS (Intel) [3/5]"); + let darwin_x86_path = build_for_target("x86_64-apple-darwin", release_mode)?; + + println!("-- Building for iOS Simulator (Apple Silicon) [4/5]"); + let ios_sim_arm_path = build_for_target("aarch64-apple-ios-sim", release_mode)?; + println!("-- Building for iOS Simulator (Intel) [5/5]"); + let ios_sim_x86_path = build_for_target("x86_64-apple-ios", release_mode)?; + + println!("-- Running Lipo for MacOs [1/2]"); + // # MacOS + let lipo_target_macos = generated_dir.join("libmatrix_sdk_ffi_macos.a"); + cmd!( + "lipo -create {darwin_x86_path} {darwin_arm_path} + -output {lipo_target_macos}" + ) + .run()?; + + println!("-- Running Lipo for iOS Simulator [2/2]"); + // # iOS Simulator + let lipo_target_sim = generated_dir.join("libmatrix_sdk_ffi_iossimulator.a"); + cmd!( + "lipo -create {ios_sim_arm_path} {ios_sim_x86_path} + -output {lipo_target_sim}" + ) + .run()?; + (vec![lipo_target_macos, lipo_target_sim, ios_path], darwin_x86_path) + }; + + println!("-- Generate uniffi files"); + generate_uniffi(&uniff_lib_path, &generated_dir)?; + + rename(generated_dir.join("matrix_sdk_ffiFFI.h"), headers_dir.join("matrix_sdk_ffiFFI.h"))?; + + rename(generated_dir.join("matrix_sdk_ffi.swift"), swift_dir.join("matrix_sdk_ffi.swift"))?; + + println!("-- Generate MatrixSDKFFI.xcframework framework"); + let xcframework_path = generated_dir.join("MatrixSDKFFI.xcframework"); + if xcframework_path.exists() { + remove_dir_all(xcframework_path.as_path())?; + } + let mut cmd = cmd!("xcodebuild -create-xcframework"); + for p in libs { + cmd = cmd.arg("-library").arg(p).arg("-headers").arg(headers_dir.as_path()) + } + cmd.arg("-output").arg(xcframework_path.as_path()).run()?; + + // cleaning up the intermediate data + remove_dir_all(headers_dir.as_path())?; + + if let Some(path) = components_path { + println!("-- Copying MatrixSDKFFI.xcframework to {path:?}"); + let framework_target = path.join("MatrixSDKFFI.xcframework"); + let swift_target = path.join("Sources/MatrixRustSDK"); + if framework_target.exists() { + remove_dir_all(framework_target.as_path())?; + } + if swift_target.exists() { + remove_dir_all(swift_target.as_path())?; + } + create_dir_all(framework_target.as_path())?; + create_dir_all(swift_target.as_path())?; + fs_extra::dir::copy( + xcframework_path.as_path(), + framework_target.as_path(), + &fs_extra::dir::CopyOptions::default(), + )?; + fs_extra::dir::copy( + swift_dir.as_path(), + framework_target.as_path(), + &fs_extra::dir::CopyOptions::default(), + )?; + } + + println!("-- All done and hunky dory. Enjoy!"); + Ok(()) } diff --git a/xtask/src/workspace.rs b/xtask/src/workspace.rs index c97ffd5e656..a4a3da3b97c 100644 --- a/xtask/src/workspace.rs +++ b/xtask/src/workspace.rs @@ -15,3 +15,14 @@ pub fn root_path() -> Result { let metadata_json = cmd!("{cargo} metadata --no-deps --format-version 1").read()?; Ok(serde_json::from_str::(&metadata_json)?.workspace_root) } + +pub fn target_path() -> Result { + #[derive(Deserialize)] + struct Metadata { + target_directory: PathBuf, + } + + let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned()); + let metadata_json = cmd!("{cargo} metadata --no-deps --format-version 1").read()?; + Ok(serde_json::from_str::(&metadata_json)?.target_directory) +}