logs.gokuls.in

2 pull requests merged across 1 repo

Offline-Protocol/offline-protocol-sdk

What

The iOS static library held 25 objects stamped for the newest iOS the release machine's Xcode knew. Every application linking the pod got one linker warning for each:

ld: warning: object file (...curve25519.o) was built for newer 'iOS-simulator' version (26.5) than being linked (14.0)

The build now hands the pod's own deployment target to every compiler, and refuses to package an archive that holds an object built for anything newer.

Why it happened

Two compilers read IPHONEOS_DEPLOYMENT_TARGET, and they fall back differently when it is unset.

CompilerBuildsFalls back to
rustcthe Rust cratesthe Rust target's floor, iOS 10.0
cc-rsthe C and assembly in ring and oslogthe version of the installed SDK

build-uniffi-ios.sh never set the variable.

What changes

  • The target is read from the podspec, not written again. MeshSdk.podspec stays the one place it lives.
  • A gate reads every object in every slice after the build and before packaging.
  • The arm64 simulator slice is held to the target or to 14.0, whichever is newer. No arm64 simulator exists before 14.0, and the toolchain raises the slice to it.
  • An archive the gate cannot read fails. That covers an archive with no stamp in it, and one otool read part of before giving up.
  • The SDK's own Rust code is now compiled for iOS 13.0, where it was compiled for 10.0. This is the one effect beyond the warning.

Measured

Minimum OS declared by the objects in each archive, before and after:

SliceBeforeAfter
Device, arm64591 at 10.0, 25 at 26.5390 at 10.0, 229 at 13.0
Simulator, arm64594 at 14.0, 25 at 26.5619 at 14.0
Simulator, x86_64not measured295 at 10.0, 237 at 13.0

Verification

  • bash scripts/tests/test-ios-min-os.sh: 50 assertions, green under BSD awk, gawk and mawk.
  • 14 mutants of the gate and the build script, run in scratch copies: all killed.
  • The real gate against the real archives: all three pass. Against the archive built before this change: 25 offenders reported.
  • build-uniffi-ios.sh run end to end: builds, passes the gate, packages, regenerates the bindings with no diff.
  • shellcheck at --severity=warning on every script touched.

Not covered

  • The gate needs otool, so CI runs it only in a release. The first real run is the next tag.
  • No application was built against the new library. The claim that the warnings are gone rests on the stamps, and on a SwiftPM test bundle that linked the new simulator slice with none.

Review

A fresh-context review found no blocker and six things worth fixing. All six are in this version: the gate checks otool's own exit status, the simulator ceiling is computed, the number is read from the podspec instead of mirrored, the docs name both compilers and both crates, and the test covers the cases the first draft missed.

Why

offline-protocol-leaf imported alloc::sync::Arc, which exists only where the processor has pointer-width compare-and-swap. The crate therefore failed to build for ESP32-C3, ESP32-C2 and every Cortex-M0, although all of its dependencies, mls-rs included, already built there. CI gated the Cortex-M33 only, so nothing noticed.

What changes

  • One module chooses the store handle. src/shared.rs selects alloc::sync::Arc where atomics exist and portable_atomic_util::Arc where they do not, the same choice mls-rs makes for its own handles. The crate exports SharedStore and a shared_store constructor. Where atomics exist SharedStore is Arc<dyn LeafStore>, so existing callers compile unchanged.
  • No atomics backend is chosen by the library. On a target without compare-and-swap the firmware links a critical-section implementation.
  • A host-side source guard refuses alloc::sync anywhere but shared.rs, because a host build always has atomics and cannot notice the import. A second test pins each import shape it must catch.
  • CI: embedded-core builds and lints core, sealed and leaf for riscv32imac-unknown-none-elf (ESP32-C6, H2), riscv32imc-unknown-none-elf (ESP32-C3, C2) and thumbv6m-none-eabi (Cortex-M0). The job's display name is now Embedded core (bare metal); no ruleset requires a check by name.
  • Docs: a support table in the leaf README, a section in ADR 0021, a rule in CLAUDE.md, a note in the footprint README, a CHANGELOG entry.

What this does not do

  • Nothing is linked or run on a board. This is a compile result.
  • ESP32-C3 and ESP32-C2 still cannot be built on esp-hal. mls-rs 0.56.0 enables portable-atomic's critical-section backend on those targets, esp-hal 1.2.2 enables unsafe-assume-single-core, and portable-atomic refuses the pair at compile time. ESP32-C6 and H2 type-check inside an esp-hal graph. Both results were checked by hand and are not in CI.
  • The Xtensa chips were not tried. The footprint harness still measures a Cortex-M33 only.

Verification

CheckResult
Build and clippy -D warnings, core, sealed and leaf, on four targetsPass
The new CI step, extracted from the workflow and run as written9 of 9 pass
Leaf tests, engine leaf_pairing tests, workspace clippy, rustdoc -D warnings, fmtPass
cargo publish --dry-run -p offline-protocol-leafClean
Leaf image on the Cortex-M33449.5 KiB, unchanged
An alloc::sync import put back, in two shapesSource guard and the ESP32-C3 lint both fail

Both lockfiles gain one line each (portable-atomic-util under the leaf); the crate was already in the graph through mls-rs.