The problem is that rustc emits synthetic libraries (symbols.o and lib.rmeta) which Apple's ld then chokes on when targetting aarch64-apple-ios-macabi, as they lack an LC_BUILD_VERSION load command to identify them as being intended for use with Catalyst.
The errors produced look like:
ld: in /Users/matthew/workspace/matrix-rust-sdk/target/aarch64-apple-ios-macabi/dbg/deps/libstatic_assertions-fdafb4b8ba800a8a.rlib(lib.rmeta), building for Mac Catalyst, but linking in object file built for , file '/Users/matthew/workspace/matrix-rust-sdk/target/aarch64-apple-ios-macabi/dbg/deps/libstatic_assertions-fdafb4b8ba800a8a.rlib' for architecture arm64
The key thing being that the lib.rmeta file here within the .rlib archive has a blank platform, which ld refuses to link in with the other Catalyst platform objects & libraries.
rustc generates object files using https://github.com/gimli-rs/object, which doesn't expose an API to add an LC_BUILD_VERSION on Mach-O objects. So I added one:
diff --git a/Cargo.toml b/Cargo.toml
index b08eec8..c840cf1 100755
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "object"
-version = "0.30.0"
+version = "0.29.0"
edition = "2018"
exclude = ["/.github", "/testfiles"]
keywords = ["object", "elf", "mach-o", "pe", "coff"]
diff --git a/src/lib.rs b/src/lib.rs
index 40f17c0..968496e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -56,6 +56,10 @@
#![deny(missing_debug_implementations)]
#![no_std]
// Style.
+
+// let us be built directly by rustc as a path dependency
+#![allow(elided_lifetimes_in_paths, rustc::default_hash_types, explicit_outlives_requirements)]
+
#![allow(clippy::collapsible_if)]
#![allow(clippy::comparison_chain)]
#![allow(clippy::match_like_matches_macro)]
diff --git a/src/write/macho.rs b/src/write/macho.rs
index 8ef722f..628a05d 100644
--- a/src/write/macho.rs
+++ b/src/write/macho.rs
@@ -1,4 +1,5 @@
use core::mem;
+use core::convert::TryInto;
use crate::endian::*;
use crate::macho;
@@ -211,6 +212,15 @@ impl<'a> Object<'a> {
let mut ncmds = 0;
let command_offset = offset;
+ // Calculate size of build version
+ let build_version_offset = offset;
+ let mut build_version_len = 0;
+ if self.platform > 0 {
+ build_version_len = mem::size_of::<macho::BuildVersionCommand<Endianness>>();
+ offset += build_version_len;
+ ncmds += 1;
+ }
+
// Calculate size of segment command and section headers.
let segment_command_offset = offset;
let segment_command_len =
@@ -358,6 +368,18 @@ impl<'a> Object<'a> {
},
);
+ if self.platform > 0 {
+ debug_assert_eq!(build_version_offset, buffer.len());
+ buffer.write(&macho::BuildVersionCommand {
+ cmd: U32::new(endian, macho::LC_BUILD_VERSION),
+ cmdsize: U32::new(endian, build_version_len.try_into().unwrap()),
+ platform: U32::new(endian, self.platform),
+ minos: U32::new(endian, self.minos),
+ sdk: U32::new(endian, self.sdk),
+ ntools: U32::new(endian, 0),
+ });
+ }
+
// Write segment command.
debug_assert_eq!(segment_command_offset, buffer.len());
macho.write_segment_command(
diff --git a/src/write/mod.rs b/src/write/mod.rs
index aa4980b..ebdf7d7 100644
--- a/src/write/mod.rs
+++ b/src/write/mod.rs
@@ -70,6 +70,10 @@ pub struct Object<'a> {
pub mangling: Mangling,
/// Mach-O "_tlv_bootstrap" symbol.
tlv_bootstrap: Option<SymbolId>,
+ /// Mach-O platform details
+ platform: u32,
+ minos: u32,
+ sdk: u32,
}
impl<'a> Object<'a> {
@@ -88,6 +92,9 @@ impl<'a> Object<'a> {
flags: FileFlags::None,
mangling: Mangling::default(format, architecture),
tlv_bootstrap: None,
+ platform: 0,
+ minos: 0,
+ sdk: 0,
}
}
@@ -298,6 +305,13 @@ impl<'a> Object<'a> {
comdat_id
}
+ /// Add a build version load command (Mach-O only); needed for macabi to link.
+ pub fn set_build_version(&mut self, platform: u32, minos: u32, sdk: u32) {
+ self.platform = platform;
+ self.minos = minos;
+ self.sdk = sdk;
+ }
+
/// Get the `SymbolId` of the symbol with the given name.
pub fn symbol_id(&self, name: &[u8]) -> Option<SymbolId> {
self.symbol_map.get(name).cloned()Then, you need to build a custom rustc toolchain against the local object dependency:
diff --git a/Cargo.toml b/Cargo.toml
index 000c10a1f90..23a5753c1b1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -105,6 +105,7 @@ object.debug = 0
# See comments in `src/tools/rustc-workspace-hack/README.md` for what's going on
# here
rustc-workspace-hack = { path = 'src/tools/rustc-workspace-hack' }
+object = { path = '/Users/matthew/workspace/object' }
# See comments in `library/rustc-std-workspace-core/README.md` for what's going on
# here
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 51c5c375d51..953a48a0d9d 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -133,6 +133,16 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
};
let mut file = write::Object::new(binary_format, architecture, endianness);
+
+ // macOS ld won't link macabi ABIs which are missing a build_version
+ if sess.target.llvm_target.ends_with("-macabi") {
+ file.set_build_version(
+ object::macho::PLATFORM_MACCATALYST,
+ 0x000E0000, // minOS 14.0
+ 0x00100200, // SDK 16.2
+ );
+ }
+
let e_flags = match architecture {
Architecture::Mips => {
let arch = match sess.target.options.cpu.as_ref() {
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
index 0f3f8519963..5d307ac4f3a 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
@@ -2,7 +2,7 @@
use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
- let llvm_target = "x86_64-apple-ios13.0-macabi";
+ let llvm_target = "x86_64-apple-ios15.0-macabi";
let arch = Arch::X86_64_macabi;
let mut base = opts("ios", arch);N.B. needing to downgrade the local object checkout to 0.29.0 to persuade rustc to use it, as well as tweaking
its lint rules to let it build in rustc's strict environment.
To build rustc, you just ./x.py build && ./x.py install
To link the result as a custom toolchain, you rustup toolchain link rust-catalyst /usr/local/rust or wherever.
Then to actually build the library under Catalyst (matrix-rust-sdk in this instance), you have to tell cargo
to use the right toolchain (if it's not the default) and build with -Z build-std as there's no prebuilt
standard library for our custom toolchain.
For matrix-rust-sdk this poses another problem, because ruma-common depends on indexmap which chokes by default
if there's no std. So you have to run a custom ruma with std as an explicit feature:
diff --git a/crates/ruma-common/Cargo.toml b/crates/ruma-common/Cargo.toml
index 83f22461..2aa36f8a 100644
--- a/crates/ruma-common/Cargo.toml
+++ b/crates/ruma-common/Cargo.toml
@@ -54,7 +54,7 @@ form_urlencoded = "1.0.0"
getrandom = { version = "0.2.6", optional = true }
html5ever = { version = "0.25.2", optional = true }
http = { workspace = true, optional = true }
-indexmap = { version = "1.9.1", features = ["serde"] }
+indexmap = { version = "1.9.1", features = ["serde", "std"] }
itoa = "1.0.1"
js_int = { workspace = true, features = ["serde"] }
js_option = "0.1.0"...and then link that in from matrix-rust-sdk with the tweaked framework build script like so:
diff --git a/Cargo.toml b/Cargo.toml
index 6d2b68fe..4f96eee9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,8 +19,10 @@ resolver = "2"
rust-version = "1.65"
[workspace.dependencies]
-ruma = { git = "https://github.com/ruma/ruma", rev = "284b797e0513daf56859b64b8c7a506856fb11ec", features = ["client-api-c"] }
-ruma-common = { git = "https://github.com/ruma/ruma", rev = "284b797e0513daf56859b64b8c7a506856fb11ec" }
+#ruma = { git = "https://github.com/ruma/ruma", rev = "284b797e0513daf56859b64b8c7a506856fb11ec", features = ["client-api-c"] }
+ruma = { path = "/Users/matthew/workspace/ruma/crates/ruma", features = ["client-api-c"] }
+#ruma-common = { git = "https://github.com/ruma/ruma", rev = "284b797e0513daf56859b64b8c7a506856fb11ec" }
+ruma-common = { path = "/Users/matthew/workspace/ruma/crates/ruma-common" }
tracing = { version = "0.1.36", default-features = false, features = ["std"] }
uniffi = { git = "https://github.com/mozilla/uniffi-rs", rev = "249a78b6f3f35661f1530e53811134e1bf012608" }
uniffi_macros = { git = "https://github.com/mozilla/uniffi-rs", rev = "249a78b6f3f35661f1530e53811134e1bf012608" }
diff --git a/bindings/apple/README.md b/bindings/apple/README.md
index 710e891b..778db18a 100644
--- a/bindings/apple/README.md
+++ b/bindings/apple/README.md
@@ -27,6 +27,10 @@ For development purposes, it will additionally generate a `Package.swift` file i
When building the SDK for release you should pass the `--release` argument to the task, which will strip away any symbols and optimise the created binary.
+You can also pass in --only-target to speed things up and build a single target (e.g. aarch64-apple-ios)
+
+The resulting framework can then be added from xcode via the Package.swift found at the root of the repository.
+
## Building only the Crypto SDK
diff --git a/xtask/src/swift.rs b/xtask/src/swift.rs
index 31dbdb6b..14cf80c1 100644
--- a/xtask/src/swift.rs
+++ b/xtask/src/swift.rs
@@ -115,7 +115,7 @@ fn generate_uniffi(library_file: &Path, ffi_directory: &Path) -> Result<()> {
}
fn build_for_target(target: &str, profile: &str) -> Result<PathBuf> {
- cmd!("cargo build -p matrix-sdk-ffi --target {target} --profile {profile}").run()?;
+ cmd!("cargo +rust-catalyst build -p matrix-sdk-ffi -Z build-std --target {target} --profile {profile}").run()?;
// The builtin dev profile has its files stored under target/debug, all
// other targets have matching directory namesYou can then build the catalyst framework with:
cargo xtask swift build-framework --only-target aarch64-apple-ios-macabi
Having imported the matrix-rust-sdk repo as a swift package into XCode, the final tweak needed to build Element X is to switch to using OIDExternalUserAgentCatalyst rather than OIDExternalUserAgentiOS.
Just FYI, the Mac Catalyst targets have recently begun shipping with
rustup(rust-lang/rust#126450), so the-Zbuild-stdshould now be unnecessary, and once 1.82.0 lands, you don't even neednightly.