atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: xtask/src/swift.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
use std::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
    collections::HashMap,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
    fs::{copy, create_dir_all, remove_dir_all, remove_file, rename},
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
use camino::{Utf8Path, Utf8PathBuf};
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
use clap::{Args, Subcommand};
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
use uniffi_bindgen::bindings::{GenerateOptions, TargetLanguage, generate};
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
use xshell::cmd;
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
use crate::{Result, sh, workspace};
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
/// Builds the SDK for Swift as a Static Library or XCFramework.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
#[derive(Args)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
pub struct SwiftArgs {
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
    #[clap(subcommand)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
    cmd: SwiftCommand,
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
#[derive(Subcommand)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
enum SwiftCommand {
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
    /// Builds the SDK for Swift as a static lib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
    BuildLibrary,
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
    /// Builds the SDK for Swift as an XCFramework.
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
    BuildFramework {
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
        /// Build with the release profile
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
        release: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
        /// Build with a custom profile, takes precedence over `--release`
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
        profile: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
        /// Build the given target. This option can be specified multiple times
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
        /// to build more than one. Omitting this option will build all
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
        /// supported targets.
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
        target: Option<Vec<String>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
        /// Includes the Tier 3 targets (such as watchOS) when building all
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
        /// supported targets. Requires a nightly toolchain with the `rust-src`
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
        /// component installed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
        tier3_targets: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
        /// Move the generated xcframework and swift sources into the given
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
        /// components-folder
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
        components_path: Option<Utf8PathBuf>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
        /// The iOS deployment target to use when building the framework.
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
        ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
        /// Defaults to not being set, which implies that the build will use the
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
        /// default values provided by the Rust and Xcode toolchains.
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
        ios_deployment_target: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
        /// The wachOS deployment target to use when building the framework.
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
        ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
        /// Defaults to not being set, which implies that the build will use the
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
        /// default values provided by the Rust and Xcode toolchains.
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
        watchos_deployment_target: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
        /// Build the targets one by one instead of passing all of them
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
        /// to cargo in one go, which makes it hang on lesser devices like plain
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
        /// Apple Silicon M1s
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
        #[clap(long)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
        sequentially: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
impl SwiftArgs {
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
    pub fn run(self) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
        let sh = sh();
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
        let _p = sh.push_dir(workspace::root_path()?);
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
        match self.cmd {
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
            SwiftCommand::BuildLibrary => build_library(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
            SwiftCommand::BuildFramework {
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
                release,
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
                profile,
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
                target: targets,
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
                tier3_targets,
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
                components_path,
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
                ios_deployment_target,
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
                watchos_deployment_target,
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
                sequentially,
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
            } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
                // The dev profile seems to cause crashes on some platforms so we default to
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
                // reldbg (https://github.com/matrix-org/matrix-rust-sdk/issues/4009)
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
                let profile =
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
                    profile.as_deref().unwrap_or(if release { "release" } else { "reldbg" });
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
                build_xcframework(
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
                    profile,
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
                    targets,
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
                    tier3_targets,
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
                    components_path,
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
                    sequentially,
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
                    ios_deployment_target.as_deref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
                    watchos_deployment_target.as_deref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
                )
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
/// A specific build target supported by the SDK.
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
struct Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    triple: &'static str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    platform: Platform,
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    status: TargetStatus,
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
    description: &'static str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
#[derive(Hash, PartialEq, Eq, Clone)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
enum TargetStatus {
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
    /// A tier 1 or 2 target that can be built with stable Rust.
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
    TopTier,
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
    /// A tier 3 target that requires nightly Rust and `-Zbuild-std`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
    Tier3,
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
/// The platform for which a particular target can run on.
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
#[derive(Hash, PartialEq, Eq, Clone)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
enum Platform {
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
    Macos,
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    Ios,
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    IosSimulator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
    Watchos,
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
    WatchosSimulator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
impl Platform {
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    /// The human-readable name of the platform.
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
    fn as_str(&self) -> &str {
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
        match self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
            Platform::Macos => "macOS",
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
            Platform::Ios => "iOS",
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
            Platform::IosSimulator => "iOS Simulator",
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
            Platform::Watchos => "watchOS",
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
            Platform::WatchosSimulator => "watchOS Simulator",
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
    /// The name of the subfolder in which to place the library for the platform
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
    /// once all architectures are lipo'd together.
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
    fn lib_folder_name(&self) -> &str {
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
        match self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
            Platform::Macos => "macos",
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
            Platform::Ios => "ios",
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
            Platform::IosSimulator => "ios-simulator",
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
            Platform::Watchos => "watchos",
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
            Platform::WatchosSimulator => "watchos-simulator",
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
/// The base name of the FFI library.
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
const FFI_LIBRARY_NAME: &str = "libmatrix_sdk_ffi.a";
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
/// The features enabled for the FFI library.
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
const FFI_FEATURES: &str = "sentry";
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
/// The list of targets supported by the SDK.
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
const TARGETS: &[Target] = &[
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
        triple: "aarch64-apple-ios",
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
        platform: Platform::Ios,
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
        status: TargetStatus::TopTier,
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
        description: "iOS",
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
        triple: "aarch64-apple-darwin",
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
        platform: Platform::Macos,
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
        status: TargetStatus::TopTier,
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
        description: "macOS (Apple Silicon)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
        triple: "x86_64-apple-darwin",
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
        platform: Platform::Macos,
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
        status: TargetStatus::TopTier,
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
        description: "macOS (Intel)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
        triple: "aarch64-apple-ios-sim",
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
        platform: Platform::IosSimulator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
        status: TargetStatus::TopTier,
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
        description: "iOS Simulator (Apple Silicon)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
        triple: "x86_64-apple-ios",
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
        platform: Platform::IosSimulator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
        status: TargetStatus::TopTier,
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
        description: "iOS Simulator (Intel) ",
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
        triple: "aarch64-apple-watchos",
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
        platform: Platform::Watchos,
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
        status: TargetStatus::Tier3,
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
        description: "watchOS (ARM64)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
        triple: "arm64_32-apple-watchos",
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
        platform: Platform::Watchos,
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
        status: TargetStatus::Tier3,
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
        description: "watchOS (ARM64_32)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
        triple: "aarch64-apple-watchos-sim",
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
        platform: Platform::WatchosSimulator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
        status: TargetStatus::Tier3,
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
        description: "watchOS Simulator (ARM64)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
    Target {
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
        triple: "x86_64-apple-watchos-sim",
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
        platform: Platform::WatchosSimulator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
        status: TargetStatus::Tier3,
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
        description: "watchOS Simulator (Intel)",
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
];
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
fn build_library() -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
    println!("Running debug library build.");
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
    let root_directory = workspace::root_path()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
    let target_directory = workspace::target_path()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
    let ffi_directory = root_directory.join("bindings/apple/generated/matrix_sdk_ffi");
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
    let lib_output_dir = target_directory.join("debug");
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
    create_dir_all(ffi_directory.as_path())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
    let sh = sh();
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
    cmd!(sh, "rustup run stable cargo build -p matrix-sdk-ffi --features {FFI_FEATURES}").run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
    rename(lib_output_dir.join(FFI_LIBRARY_NAME), ffi_directory.join(FFI_LIBRARY_NAME))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
    let swift_directory = root_directory.join("bindings/apple/generated/swift");
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
    create_dir_all(swift_directory.as_path())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
    generate_uniffi(&ffi_directory.join(FFI_LIBRARY_NAME), &ffi_directory)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
    let module_map_file = ffi_directory.join("module.modulemap");
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
    if module_map_file.exists() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
        remove_file(module_map_file.as_path())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
    consolidate_modulemap_files(&ffi_directory, &ffi_directory)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
    move_files("swift", &ffi_directory, &swift_directory)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
    update_swift_module_imports(&swift_directory)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
    Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
fn generate_uniffi(library_path: &Utf8Path, ffi_directory: &Utf8Path) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
    generate(GenerateOptions {
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
        languages: vec![TargetLanguage::Swift],
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
        source: library_path.to_path_buf(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
        out_dir: ffi_directory.to_path_buf(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
        ..GenerateOptions::default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
    })?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
    Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
fn build_xcframework(
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
    profile: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
    targets: Option<Vec<String>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
    tier3_targets: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
    components_path: Option<Utf8PathBuf>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
    sequentially: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
    ios_deployment_target: Option<&str>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
    watchos_deployment_target: Option<&str>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
    let root_dir = workspace::root_path()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
    let apple_dir = root_dir.join("bindings/apple");
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
    let generated_dir = apple_dir.join("generated");
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
    // Cleanup destination folder
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
    let _ = remove_dir_all(&generated_dir);
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
    create_dir_all(&generated_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
    let headers_dir = generated_dir.join("headers");
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
    // Use a subdirectory to fix conflicts with other UniFFI libraries.
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
    let headers_module_dir = headers_dir.join("MatrixSDKFFI");
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
    let swift_dir = generated_dir.join("swift");
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
    create_dir_all(headers_module_dir.clone())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
    create_dir_all(swift_dir.clone())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
    let targets = if let Some(triples) = targets {
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
        triples
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
            .map(|t| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
                TARGETS.iter().find(|target| target.triple == *t).expect("Invalid target specified")
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
            .collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
    } else if tier3_targets {
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
        TARGETS.iter().collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
    } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
        TARGETS.iter().filter(|target| target.status == TargetStatus::TopTier).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
    let platform_build_paths = build_targets(
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
        targets,
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
        profile,
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
        sequentially,
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
        ios_deployment_target,
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
        watchos_deployment_target,
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
    )?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
    let libs = lipo_platform_libraries(&platform_build_paths, &generated_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
    println!("-- Generating uniffi files");
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
    let uniffi_lib_path = platform_build_paths.values().next().unwrap().first().unwrap().clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
    generate_uniffi(&uniffi_lib_path, &generated_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    move_files("h", &generated_dir, &headers_module_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
    consolidate_modulemap_files(&generated_dir, &headers_module_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
    move_files("swift", &generated_dir, &swift_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
    update_swift_module_imports(&swift_dir)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
    println!("-- Generating MatrixSDKFFI.xcframework framework");
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
    let xcframework_path = generated_dir.join("MatrixSDKFFI.xcframework");
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
    if xcframework_path.exists() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
        remove_dir_all(&xcframework_path)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
    let sh = sh();
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
    let mut cmd = cmd!(sh, "xcodebuild -create-xcframework");
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
    for p in libs {
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
        cmd = cmd.arg("-library").arg(p).arg("-headers").arg(&headers_dir)
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
    cmd.arg("-output").arg(&xcframework_path).run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
    // Copy the Swift package manifest to the SDK root for local development.
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
    copy(apple_dir.join("Debug-Package.swift"), root_dir.join("Package.swift"))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
    // Copy an empty package to folders we want ignored
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
    let ignored_package_folders = ["target"];
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
    for path in ignored_package_folders {
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
        copy(
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
            apple_dir.join("Debug-Empty-Package.swift"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
            root_dir.join(path).join("Package.swift"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
        )?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
    // cleaning up the intermediate data
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
    remove_dir_all(headers_dir.as_path())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
    if let Some(path) = components_path {
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
        println!("-- Copying MatrixSDKFFI.xcframework to {path}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
        let framework_target = path.join("MatrixSDKFFI.xcframework");
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
        let swift_target = path.join("Sources/MatrixRustSDK");
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
        if framework_target.exists() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
            remove_dir_all(&framework_target)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
        if swift_target.exists() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
            remove_dir_all(&swift_target)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
        create_dir_all(&framework_target)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
        create_dir_all(&swift_target)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
        let copy_options = fs_extra::dir::CopyOptions { content_only: true, ..Default::default() };
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
        fs_extra::dir::copy(&xcframework_path, &framework_target, &copy_options)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
        fs_extra::dir::copy(&swift_dir, &swift_target, &copy_options)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
    println!("-- All done and hunky dory. Enjoy!");
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
    Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
/// Builds the SDK for the specified targets and profile.
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
fn build_targets(
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
    targets: Vec<&Target>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
    profile: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC374
    sequentially: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC375
    ios_deployment_target: Option<&str>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC376
    watchos_deployment_target: Option<&str>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC377
) -> Result<HashMap<Platform, Vec<Utf8PathBuf>>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC378
    let sh = sh();
7ff8adafe9e4atagen2026-10-22 12:30 UTC379
7ff8adafe9e4atagen2026-10-22 12:30 UTC380
    // Note: `push_env` stores environment variables and returns a RAII guard that
7ff8adafe9e4atagen2026-10-22 12:30 UTC381
    // will restore the environment variable to its previous value when dropped.
7ff8adafe9e4atagen2026-10-22 12:30 UTC382
    let _env_guard1 =
7ff8adafe9e4atagen2026-10-22 12:30 UTC383
        sh.push_env("CARGO_TARGET_AARCH64_APPLE_IOS_RUSTFLAGS", "-Clinker=/usr/bin/clang");
7ff8adafe9e4atagen2026-10-22 12:30 UTC384
    let _env_guard2 = sh.push_env("AARCH64_APPLE_IOS_CC", "/usr/bin/clang");
7ff8adafe9e4atagen2026-10-22 12:30 UTC385
    let _env_guard3 =
7ff8adafe9e4atagen2026-10-22 12:30 UTC386
        ios_deployment_target.map(|target| sh.push_env("IPHONEOS_DEPLOYMENT_TARGET", target));
7ff8adafe9e4atagen2026-10-22 12:30 UTC387
    let _env_guard4 =
7ff8adafe9e4atagen2026-10-22 12:30 UTC388
        watchos_deployment_target.map(|target| sh.push_env("WATCHOS_DEPLOYMENT_TARGET", target));
7ff8adafe9e4atagen2026-10-22 12:30 UTC389
7ff8adafe9e4atagen2026-10-22 12:30 UTC390
    if sequentially {
7ff8adafe9e4atagen2026-10-22 12:30 UTC391
        for target in &targets {
7ff8adafe9e4atagen2026-10-22 12:30 UTC392
            let triple = target.triple;
7ff8adafe9e4atagen2026-10-22 12:30 UTC393
7ff8adafe9e4atagen2026-10-22 12:30 UTC394
            println!("-- Building for {}", target.description);
7ff8adafe9e4atagen2026-10-22 12:30 UTC395
            if target.status == TargetStatus::TopTier {
7ff8adafe9e4atagen2026-10-22 12:30 UTC396
                cmd!(sh, "rustup run stable cargo build -p matrix-sdk-ffi --target {triple} --profile {profile} --features {FFI_FEATURES}")
7ff8adafe9e4atagen2026-10-22 12:30 UTC397
                    .run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC398
            } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC399
                cmd!(sh, "rustup run nightly cargo build -p matrix-sdk-ffi -Zbuild-std --target {triple} --profile {profile} --features {FFI_FEATURES}")
7ff8adafe9e4atagen2026-10-22 12:30 UTC400
                    .run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC401
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC402
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC403
    } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC404
        let (stable_targets, tier3_targets): (Vec<&Target>, Vec<&Target>) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC405
            targets.iter().partition(|t| t.status == TargetStatus::TopTier);
7ff8adafe9e4atagen2026-10-22 12:30 UTC406
7ff8adafe9e4atagen2026-10-22 12:30 UTC407
        if !stable_targets.is_empty() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC408
            let triples = stable_targets.iter().map(|target| target.triple).collect::<Vec<_>>();
7ff8adafe9e4atagen2026-10-22 12:30 UTC409
            let mut cmd = cmd!(sh, "rustup run stable cargo build -p matrix-sdk-ffi");
7ff8adafe9e4atagen2026-10-22 12:30 UTC410
            for triple in &triples {
7ff8adafe9e4atagen2026-10-22 12:30 UTC411
                cmd = cmd.arg("--target").arg(triple);
7ff8adafe9e4atagen2026-10-22 12:30 UTC412
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC413
            cmd = cmd.arg("--profile").arg(profile).arg("--features").arg(FFI_FEATURES);
7ff8adafe9e4atagen2026-10-22 12:30 UTC414
7ff8adafe9e4atagen2026-10-22 12:30 UTC415
            println!("-- Building for {} targets", triples.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC416
            cmd.run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC417
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC418
7ff8adafe9e4atagen2026-10-22 12:30 UTC419
        if !tier3_targets.is_empty() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC420
            let triples = tier3_targets.iter().map(|target| target.triple).collect::<Vec<_>>();
7ff8adafe9e4atagen2026-10-22 12:30 UTC421
            let mut cmd = cmd!(sh, "rustup run nightly cargo build -p matrix-sdk-ffi -Zbuild-std");
7ff8adafe9e4atagen2026-10-22 12:30 UTC422
            for triple in &triples {
7ff8adafe9e4atagen2026-10-22 12:30 UTC423
                cmd = cmd.arg("--target").arg(triple);
7ff8adafe9e4atagen2026-10-22 12:30 UTC424
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC425
            cmd = cmd.arg("--profile").arg(profile).arg("--features").arg(FFI_FEATURES);
7ff8adafe9e4atagen2026-10-22 12:30 UTC426
7ff8adafe9e4atagen2026-10-22 12:30 UTC427
            println!("-- Building for {} targets with nightly -Zbuild-std", triples.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC428
            cmd.run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC429
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC430
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC431
7ff8adafe9e4atagen2026-10-22 12:30 UTC432
    // a hashmap of platform to array, where each array contains all the paths for
7ff8adafe9e4atagen2026-10-22 12:30 UTC433
    // that platform.
7ff8adafe9e4atagen2026-10-22 12:30 UTC434
    let mut platform_build_paths = HashMap::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC435
    for target in targets {
7ff8adafe9e4atagen2026-10-22 12:30 UTC436
        let path = build_path_for_target(target, profile)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC437
        let paths = platform_build_paths.entry(target.platform.clone()).or_insert_with(Vec::new);
7ff8adafe9e4atagen2026-10-22 12:30 UTC438
        paths.push(path);
7ff8adafe9e4atagen2026-10-22 12:30 UTC439
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC440
7ff8adafe9e4atagen2026-10-22 12:30 UTC441
    Ok(platform_build_paths)
7ff8adafe9e4atagen2026-10-22 12:30 UTC442
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC443
7ff8adafe9e4atagen2026-10-22 12:30 UTC444
/// The path of the built library for a specific target and profile.
7ff8adafe9e4atagen2026-10-22 12:30 UTC445
fn build_path_for_target(target: &Target, profile: &str) -> Result<Utf8PathBuf> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC446
    // The builtin dev profile has its files stored under target/debug, all
7ff8adafe9e4atagen2026-10-22 12:30 UTC447
    // other targets have matching directory names
7ff8adafe9e4atagen2026-10-22 12:30 UTC448
    let profile_dir_name = if profile == "dev" { "debug" } else { profile };
7ff8adafe9e4atagen2026-10-22 12:30 UTC449
    Ok(workspace::target_path()?.join(target.triple).join(profile_dir_name).join(FFI_LIBRARY_NAME))
7ff8adafe9e4atagen2026-10-22 12:30 UTC450
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC451
7ff8adafe9e4atagen2026-10-22 12:30 UTC452
/// Lipo's together the libraries for each platform into a single library.
7ff8adafe9e4atagen2026-10-22 12:30 UTC453
fn lipo_platform_libraries(
7ff8adafe9e4atagen2026-10-22 12:30 UTC454
    platform_build_paths: &HashMap<Platform, Vec<Utf8PathBuf>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC455
    generated_dir: &Utf8Path,
7ff8adafe9e4atagen2026-10-22 12:30 UTC456
) -> Result<Vec<Utf8PathBuf>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC457
    let mut libs = Vec::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC458
    let sh = sh();
7ff8adafe9e4atagen2026-10-22 12:30 UTC459
    for platform in platform_build_paths.keys() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC460
        let paths = platform_build_paths.get(platform).unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC461
7ff8adafe9e4atagen2026-10-22 12:30 UTC462
        if paths.len() == 1 {
7ff8adafe9e4atagen2026-10-22 12:30 UTC463
            libs.push(paths[0].clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC464
            continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC465
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC466
7ff8adafe9e4atagen2026-10-22 12:30 UTC467
        let output_folder = generated_dir.join("lipo").join(platform.lib_folder_name());
7ff8adafe9e4atagen2026-10-22 12:30 UTC468
        create_dir_all(&output_folder)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC469
7ff8adafe9e4atagen2026-10-22 12:30 UTC470
        let output_path = output_folder.join(FFI_LIBRARY_NAME);
7ff8adafe9e4atagen2026-10-22 12:30 UTC471
        let mut cmd = cmd!(sh, "lipo -create");
7ff8adafe9e4atagen2026-10-22 12:30 UTC472
        for path in paths {
7ff8adafe9e4atagen2026-10-22 12:30 UTC473
            cmd = cmd.arg(path);
7ff8adafe9e4atagen2026-10-22 12:30 UTC474
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC475
        cmd = cmd.arg("-output").arg(&output_path);
7ff8adafe9e4atagen2026-10-22 12:30 UTC476
7ff8adafe9e4atagen2026-10-22 12:30 UTC477
        println!("-- Running Lipo for {}", platform.as_str());
7ff8adafe9e4atagen2026-10-22 12:30 UTC478
        cmd.run()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC479
7ff8adafe9e4atagen2026-10-22 12:30 UTC480
        libs.push(output_path);
7ff8adafe9e4atagen2026-10-22 12:30 UTC481
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC482
    Ok(libs)
7ff8adafe9e4atagen2026-10-22 12:30 UTC483
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC484
7ff8adafe9e4atagen2026-10-22 12:30 UTC485
/// Moves all files of the specified file extension from one directory into
7ff8adafe9e4atagen2026-10-22 12:30 UTC486
/// another.
7ff8adafe9e4atagen2026-10-22 12:30 UTC487
fn move_files(extension: &str, source: &Utf8Path, destination: &Utf8Path) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC488
    for entry in source.read_dir_utf8()? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC489
        let entry = entry?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC490
7ff8adafe9e4atagen2026-10-22 12:30 UTC491
        if entry.file_type()?.is_file() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC492
            let path = entry.path();
7ff8adafe9e4atagen2026-10-22 12:30 UTC493
            if path.extension() == Some(extension) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC494
                let file_name = path.file_name().expect("Failed to get file name");
7ff8adafe9e4atagen2026-10-22 12:30 UTC495
                rename(path, destination.join(file_name)).expect("Failed to move swift file");
7ff8adafe9e4atagen2026-10-22 12:30 UTC496
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC497
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC498
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC499
    Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC500
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC501
7ff8adafe9e4atagen2026-10-22 12:30 UTC502
/// Updates all the swift files in the given directory to import the same module
7ff8adafe9e4atagen2026-10-22 12:30 UTC503
/// that gets defined by the `consolidate_modulemap_files` function.
7ff8adafe9e4atagen2026-10-22 12:30 UTC504
fn update_swift_module_imports(directory: &Utf8Path) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC505
    let regex = regex::Regex::new(r"#if canImport\(\w+FFI\)\nimport \w+FFI\n#endif")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC506
7ff8adafe9e4atagen2026-10-22 12:30 UTC507
    for entry in directory.read_dir_utf8()? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC508
        let entry = entry?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC509
        if entry.file_type()?.is_file() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC510
            let path = entry.path();
7ff8adafe9e4atagen2026-10-22 12:30 UTC511
            if path.extension() == Some("swift") {
7ff8adafe9e4atagen2026-10-22 12:30 UTC512
                let contents = std::fs::read_to_string(path)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC513
                let new_contents = regex.replace_all(
7ff8adafe9e4atagen2026-10-22 12:30 UTC514
                    &contents,
7ff8adafe9e4atagen2026-10-22 12:30 UTC515
                    "#if canImport(MatrixSDKFFI)\nimport MatrixSDKFFI\n#endif",
7ff8adafe9e4atagen2026-10-22 12:30 UTC516
                );
7ff8adafe9e4atagen2026-10-22 12:30 UTC517
                if new_contents != contents {
7ff8adafe9e4atagen2026-10-22 12:30 UTC518
                    std::fs::write(path, new_contents.as_ref())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC519
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC520
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC521
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC522
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC523
7ff8adafe9e4atagen2026-10-22 12:30 UTC524
    Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC525
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC526
7ff8adafe9e4atagen2026-10-22 12:30 UTC527
/// Consolidates the modulemap files found in the source directory into a single
7ff8adafe9e4atagen2026-10-22 12:30 UTC528
/// `module.modulemap` file in the destination directory.
7ff8adafe9e4atagen2026-10-22 12:30 UTC529
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC530
/// The first modulemap file found is used as the base and all `header`
7ff8adafe9e4atagen2026-10-22 12:30 UTC531
/// directives from the remaining modulemap files are spliced in after the
7ff8adafe9e4atagen2026-10-22 12:30 UTC532
/// base's own header line. All other content in the base (e.g. `export *`,
7ff8adafe9e4atagen2026-10-22 12:30 UTC533
/// `use` directives, and any future additions) is preserved verbatim.
7ff8adafe9e4atagen2026-10-22 12:30 UTC534
fn consolidate_modulemap_files(source: &Utf8Path, destination: &Utf8Path) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC535
    let mut base_contents: Option<String> = None;
7ff8adafe9e4atagen2026-10-22 12:30 UTC536
    let mut extra_headers: Vec<String> = Vec::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC537
7ff8adafe9e4atagen2026-10-22 12:30 UTC538
    for entry in source.read_dir_utf8()? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC539
        let entry = entry?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC540
7ff8adafe9e4atagen2026-10-22 12:30 UTC541
        if entry.file_type()?.is_file() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC542
            let path = entry.path();
7ff8adafe9e4atagen2026-10-22 12:30 UTC543
            if path.extension() == Some("modulemap") {
7ff8adafe9e4atagen2026-10-22 12:30 UTC544
                let contents = std::fs::read_to_string(path)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC545
                if base_contents.is_none() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC546
                    base_contents = Some(contents);
7ff8adafe9e4atagen2026-10-22 12:30 UTC547
                } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC548
                    for line in contents.lines() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC549
                        if line.trim().starts_with("header ")
7ff8adafe9e4atagen2026-10-22 12:30 UTC550
                            && !extra_headers.contains(&line.to_string())
7ff8adafe9e4atagen2026-10-22 12:30 UTC551
                        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC552
                            extra_headers.push(line.to_string());
7ff8adafe9e4atagen2026-10-22 12:30 UTC553
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC554
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC555
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC556
                remove_file(path)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC557
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC558
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC559
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC560
7ff8adafe9e4atagen2026-10-22 12:30 UTC561
    let base = base_contents.expect("No modulemap files found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC562
7ff8adafe9e4atagen2026-10-22 12:30 UTC563
    // Rebuild the base line-by-line, renaming the module to MatrixSDKFFI and
7ff8adafe9e4atagen2026-10-22 12:30 UTC564
    // inserting the extra headers immediately after the base's own header line.
7ff8adafe9e4atagen2026-10-22 12:30 UTC565
    let mut lines: Vec<String> = Vec::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC566
    let mut last_header_position: Option<usize> = None;
7ff8adafe9e4atagen2026-10-22 12:30 UTC567
7ff8adafe9e4atagen2026-10-22 12:30 UTC568
    for line in base.lines() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC569
        if line.starts_with("module ") {
7ff8adafe9e4atagen2026-10-22 12:30 UTC570
            lines.push("module MatrixSDKFFI {".to_string());
7ff8adafe9e4atagen2026-10-22 12:30 UTC571
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC572
            if line.trim().starts_with("header ") {
7ff8adafe9e4atagen2026-10-22 12:30 UTC573
                last_header_position = Some(lines.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC574
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC575
            lines.push(line.to_string());
7ff8adafe9e4atagen2026-10-22 12:30 UTC576
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC577
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC578
7ff8adafe9e4atagen2026-10-22 12:30 UTC579
    if let Some(last_header_position) = last_header_position {
7ff8adafe9e4atagen2026-10-22 12:30 UTC580
        lines.splice(last_header_position + 1..last_header_position + 1, extra_headers);
7ff8adafe9e4atagen2026-10-22 12:30 UTC581
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC582
7ff8adafe9e4atagen2026-10-22 12:30 UTC583
    let modulemap = lines.join("\n") + "\n";
7ff8adafe9e4atagen2026-10-22 12:30 UTC584
    std::fs::write(destination.join("module.modulemap"), modulemap)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC585
    Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC586
}