diff --git a/xtask/src/swift/build.rs b/xtask/src/swift/build.rs index 3ad701a0..ac5a5934 100644 --- a/xtask/src/swift/build.rs +++ b/xtask/src/swift/build.rs @@ -18,13 +18,58 @@ const IOS_TARGETS: [&str; 3] = [ "x86_64-apple-ios", ]; +/// Build profile for the native libraries backing the `XCFramework`. Both profiles build +/// the same iOS targets; only optimization level, symbols, and output directory differ. +#[derive(Clone, Copy)] +pub(super) enum Profile { + /// Optimized, dead-stripped, symbols removed. Used for distributed releases. + Release, + /// Unoptimized with full debug symbols, for LLDB source-level debugging. + Debug, +} + +impl Profile { + const fn cargo_profile_flag(self) -> Option<&'static str> { + match self { + Self::Release => Some("--release"), + Self::Debug => None, + } + } + + const fn dir_name(self) -> &'static str { + match self { + Self::Release => "release", + Self::Debug => "debug", + } + } + + const fn rustflags(self) -> &'static str { + match self { + Self::Release => { + "-C link-arg=-Wl,-application_extension \ + -C link-arg=-Wl,-dead_strip \ + -C link-arg=-Wl,-dead_strip_dylibs \ + -C embed-bitcode=no" + } + Self::Debug => { + "-C link-arg=-Wl,-application_extension \ + -C embed-bitcode=no" + } + } + } +} + struct FrameworkSlice<'a> { directory: &'a str, library: PathBuf, platform: &'a str, } -pub(super) fn run(sh: &Shell, output_dir: Option<&Path>) -> Result<()> { +pub(super) fn run( + sh: &Shell, + output_dir: Option<&Path>, + profile: Profile, +) -> Result<()> { ensure_macos()?; ensure_ios_sdks(sh)?; ensure_nargo(sh)?; @@ -34,8 +79,9 @@ pub(super) fn run(sh: &Shell, output_dir: Option<&Path>) -> Result<()> { let framework_output = output_dir.join(FRAMEWORK_NAME); println!( - "Building {FRAMEWORK_NAME} to {}", - framework_output.display() + "Building {FRAMEWORK_NAME} to {} ({})", + framework_output.display(), + profile.dir_name() ); sh.remove_path(INTERMEDIATE_DIR)?; @@ -46,11 +92,11 @@ pub(super) fn run(sh: &Shell, output_dir: Option<&Path>) -> Result<()> { ))?; sh.create_dir(&sources_dir)?; - configure_ios_build(sh); - build_native_libraries(sh)?; - create_universal_simulator_library(sh)?; - generate_bindings(sh, &sources_dir)?; - create_xcframework(sh, &framework_output)?; + configure_ios_build(sh, profile); + build_native_libraries(sh, profile)?; + create_universal_simulator_library(sh, profile)?; + generate_bindings(sh, &sources_dir, profile)?; + create_xcframework(sh, &framework_output, profile)?; sh.remove_path(INTERMEDIATE_DIR)?; @@ -114,7 +160,7 @@ fn resolve_output_dir(output_dir: Option<&Path>) -> PathBuf { ) } -fn configure_ios_build(sh: &Shell) { +fn configure_ios_build(sh: &Shell, profile: Profile) { sh.set_var("IPHONEOS_DEPLOYMENT_TARGET", "13.0"); // aws-lc-sys references Linux-only entropy definitions while compiling an // unreachable iOS code path. Keep this workaround scoped to aws-lc-sys. @@ -122,25 +168,21 @@ fn configure_ios_build(sh: &Shell) { "AWS_LC_SYS_CFLAGS", "-DRNDGETENTCNT=2 -Wno-implicit-function-declaration", ); - sh.set_var( - "RUSTFLAGS", - "-C link-arg=-Wl,-application_extension \ - -C link-arg=-Wl,-dead_strip \ - -C link-arg=-Wl,-dead_strip_dylibs \ - -C embed-bitcode=no", - ); + sh.set_var("RUSTFLAGS", profile.rustflags()); } -fn build_native_libraries(sh: &Shell) -> Result<()> { +fn build_native_libraries(sh: &Shell, profile: Profile) -> Result<()> { let features = cargo_features(sh); + let release_flag = profile.cargo_profile_flag(); println!("Building WalletKit for iOS targets..."); for target in IOS_TARGETS { println!("Building {target}..."); cmd!( sh, - "cargo build --package {PACKAGE_NAME} --target {target} --release --locked --target-dir {TARGET_DIR} --features {features}" + "cargo build --package {PACKAGE_NAME} --target {target} --locked --target-dir {TARGET_DIR} --features {features}" ) + .args(release_flag) .run() .wrap_err_with(|| format!("failed to build WalletKit for {target}"))?; } @@ -155,11 +197,17 @@ fn cargo_features(sh: &Shell) -> String { .unwrap_or_else(|| DEFAULT_CARGO_FEATURES.to_owned()) } -fn create_universal_simulator_library(sh: &Shell) -> Result<()> { - let arm_library = - Path::new(TARGET_DIR).join("aarch64-apple-ios-sim/release/libwalletkit.a"); - let intel_library = - Path::new(TARGET_DIR).join("x86_64-apple-ios/release/libwalletkit.a"); +fn create_universal_simulator_library(sh: &Shell, profile: Profile) -> Result<()> { + let arm_library = Path::new(TARGET_DIR) + .join("aarch64-apple-ios-sim") + .join(profile.dir_name()) + .join("libwalletkit.a"); + + let intel_library = Path::new(TARGET_DIR) + .join("x86_64-apple-ios") + .join(profile.dir_name()) + .join("libwalletkit.a"); + let output = Path::new(INTERMEDIATE_DIR) .join("target/universal-ios-sim/release/libwalletkit.a"); @@ -175,9 +223,12 @@ fn create_universal_simulator_library(sh: &Shell) -> Result<()> { .wrap_err("failed to inspect universal iOS simulator library") } -fn generate_bindings(sh: &Shell, sources_dir: &Path) -> Result<()> { - let library = - Path::new(TARGET_DIR).join("aarch64-apple-ios-sim/release/libwalletkit.dylib"); +fn generate_bindings(sh: &Shell, sources_dir: &Path, profile: Profile) -> Result<()> { + let library = Path::new(TARGET_DIR) + .join("aarch64-apple-ios-sim") + .join(profile.dir_name()) + .join("libwalletkit.dylib"); + let bindings_dir = Path::new(INTERMEDIATE_DIR).join("bindings"); println!("Generating Swift bindings..."); @@ -239,13 +290,19 @@ fn copy_directory_contents( Ok(()) } -fn create_xcframework(sh: &Shell, framework_output: &Path) -> Result<()> { +fn create_xcframework( + sh: &Shell, + framework_output: &Path, + profile: Profile, +) -> Result<()> { let bindings_dir = Path::new(INTERMEDIATE_DIR).join("bindings"); let slices = [ FrameworkSlice { directory: "ios-arm64", library: Path::new(TARGET_DIR) - .join("aarch64-apple-ios/release/libwalletkit.a"), + .join("aarch64-apple-ios") + .join(profile.dir_name()) + .join("libwalletkit.a"), platform: "iPhoneOS", }, FrameworkSlice { diff --git a/xtask/src/swift/local.rs b/xtask/src/swift/local.rs index dd6438c5..131e40b6 100644 --- a/xtask/src/swift/local.rs +++ b/xtask/src/swift/local.rs @@ -5,18 +5,25 @@ use std::path::Path; use eyre::{Result, WrapErr as _}; use xshell::Shell; +use super::build::Profile; use super::{build, package}; const FRAMEWORK_NAME: &str = "WalletKit.xcframework"; const LOCAL_BUILD_DIR: &str = "swift/local_build/walletkit-swift"; const PACKAGE_TEMPLATE: &str = "swift/Package.swift.template"; -pub(super) fn run(sh: &Shell) -> Result<()> { +pub(super) fn run(sh: &Shell, debug: bool) -> Result<()> { + let profile = if debug { + Profile::Debug + } else { + Profile::Release + }; + println!("Building {FRAMEWORK_NAME} for local iOS development..."); sh.remove_path(LOCAL_BUILD_DIR)?; sh.create_dir(LOCAL_BUILD_DIR)?; - build::run(sh, Some(Path::new("local_build/walletkit-swift")))?; + build::run(sh, Some(Path::new("local_build/walletkit-swift")), profile)?; println!("Creating Package.swift for local development..."); let template = sh diff --git a/xtask/src/swift/mod.rs b/xtask/src/swift/mod.rs index 7687d99d..fc1f0e62 100644 --- a/xtask/src/swift/mod.rs +++ b/xtask/src/swift/mod.rs @@ -22,7 +22,7 @@ pub enum Command { Test(TestOptions), /// Build a Swift package for local development. - Local, + Local(LocalOptions), /// Generate the release `Package.swift` manifest. Archive(ArchiveOptions), @@ -38,6 +38,15 @@ pub struct BuildOptions { output_dir: Option, } +/// Options for building a local Swift package. +#[derive(Args)] +pub struct LocalOptions { + /// Build an unoptimized binary with full debug symbols, for LLDB source-level + /// debugging of Rust code. Not suitable for distribution. + #[arg(long)] + debug: bool, +} + /// Options for running Swift foreign-binding tests. #[derive(Args)] pub struct TestOptions { @@ -65,9 +74,11 @@ pub struct ArchiveOptions { /// Runs a Swift/iOS task. pub fn run(sh: &Shell, command: &Command) -> Result<()> { match command { - Command::Build(options) => build::run(sh, options.output_dir.as_deref()), + Command::Build(options) => { + build::run(sh, options.output_dir.as_deref(), build::Profile::Release) + } Command::Test(options) => test::run(sh, options.skip_build), - Command::Local => local::run(sh), + Command::Local(options) => local::run(sh, options.debug), Command::Archive(options) => archive::run( sh, &options.asset_url, diff --git a/xtask/src/swift/test.rs b/xtask/src/swift/test.rs index bef76b71..25e20b49 100644 --- a/xtask/src/swift/test.rs +++ b/xtask/src/swift/test.rs @@ -17,7 +17,7 @@ pub(super) fn run(sh: &Shell, skip_build: bool) -> Result<()> { ensure_macos()?; if !skip_build { - build::run(sh, None)?; + build::run(sh, None, build::Profile::Release)?; } ensure_simulator_sdk(sh)?;