7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | use std::fs::create_dir_all; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | use camino::{Utf8Path, Utf8PathBuf}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | use clap::{Args, Subcommand, ValueEnum}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | use uniffi_bindgen::bindings::{GenerateOptions, TargetLanguage, generate}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | use xshell::cmd; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | use crate::{Result, sh, workspace}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | struct PackageValues { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | name: &'static str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | features: &'static str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | #[derive(ValueEnum, Clone)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | enum Package { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | CryptoSDK, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | FullSDK, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | impl Package { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | fn values(self) -> PackageValues { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | match self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | Package::CryptoSDK => PackageValues { name: "matrix-sdk-crypto-ffi", features: "" }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | Package::FullSDK => PackageValues { name: "matrix-sdk-ffi", features: "sentry" }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | #[derive(Args)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | pub struct KotlinArgs { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | #[clap(subcommand)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | cmd: KotlinCommand, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | #[derive(Subcommand)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | enum KotlinCommand { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | /// Builds the SDK for Android as an AAR. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | BuildAndroidLibrary { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | #[clap(value_enum, long)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | package: Package, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | /// Build with the release profile |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | #[clap(long)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | release: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | /// Build with a custom profile, takes precedence over `--release` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | #[clap(long)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | profile: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | /// Build the given target only |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | #[clap(long)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | only_target: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | /// Move the generated files into the given src directory |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | #[clap(long)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | src_dir: Utf8PathBuf, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | impl KotlinArgs { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | pub fn run(self) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | let sh = sh(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | let _p = sh.push_dir(workspace::root_path()?); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | match self.cmd { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | KotlinCommand::BuildAndroidLibrary { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | release, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | profile, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | src_dir, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | only_target, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | package, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | } => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | let profile = profile.as_deref().unwrap_or(if release { "release" } else { "dev" }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | build_android_library(profile, only_target, &src_dir, package) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | fn build_android_library( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | profile: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | only_target: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | src_dir: &Utf8Path, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | package: Package, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | ) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | let package_values = package.values(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | let package_name = package_values.name; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | let package_features = package_values.features; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | let jni_libs_dir = src_dir.join("jniLibs"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | let jni_libs_dir_str = jni_libs_dir.as_str(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | let kotlin_generated_dir = src_dir.join("kotlin"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | create_dir_all(kotlin_generated_dir.clone())?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | let uniffi_lib_path = if let Some(target) = only_target { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | println!("-- Building for {target} [1/1]"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | build_for_android_target( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | target.as_str(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | profile, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | jni_libs_dir_str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | package_name, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | package_features, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | )? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | println!("-- Building for x86_64-linux-android[1/4]"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | build_for_android_target( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | "x86_64-linux-android", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | profile, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | jni_libs_dir_str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | package_name, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | package_features, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | )?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | println!("-- Building for aarch64-linux-android[2/4]"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | build_for_android_target( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | "aarch64-linux-android", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | profile, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | jni_libs_dir_str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | package_name, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | package_features, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | )?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | println!("-- Building for armv7-linux-androideabi[3/4]"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | build_for_android_target( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | "armv7-linux-androideabi", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | profile, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | jni_libs_dir_str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | package_name, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | package_features, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | )?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | println!("-- Building for i686-linux-android[4/4]"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | build_for_android_target( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | "i686-linux-android", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | profile, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | jni_libs_dir_str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | package_name, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | package_features, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | )? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | println!("-- Generate uniffi files"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | generate_uniffi_bindings(&uniffi_lib_path, &kotlin_generated_dir)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | println!("-- All done and hunky dory. Enjoy!"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | fn generate_uniffi_bindings(library_path: &Utf8Path, ffi_generated_dir: &Utf8Path) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | println!("-- library_path = {library_path}"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | generate(GenerateOptions { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | languages: vec![TargetLanguage::Kotlin], |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | source: library_path.to_path_buf(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | out_dir: ffi_generated_dir.to_path_buf(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | ..GenerateOptions::default() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | })?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | fn build_for_android_target( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | target: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | profile: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | dest_dir: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | package_name: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | features: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | ) -> Result<Utf8PathBuf> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | let sh = sh(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | cmd!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | sh, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | "cargo ndk --target {target} -o {dest_dir} build --profile {profile} --package {package_name} --features {features}" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | .run()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | // The builtin dev profile has its files stored under target/debug, all |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | // other targets have matching directory names |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | let profile_dir_name = if profile == "dev" { "debug" } else { profile }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | let package_camel = package_name.replace('-', "_"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | let lib_name = format!("lib{package_camel}.so"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | Ok(workspace::target_path()?.join(target).join(profile_dir_name).join(lib_name)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | } |