field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | LICENSE

commit e15e04f32fcdfe1bb055b47b2dc015ea7ba9d12d
parent a741b7cab63983c0e58ebc99500bb3c81976c1c6
Author: triesap <tyson@radroots.org>
Date:   Mon, 16 Feb 2026 00:40:04 +0000

build: replace ios ffi wrapper with makefile pipeline

Diffstat:
MMakefile | 8++++----
DRadrootsCore/.gitignore | 1-
DRadrootsCore/Cargo.lock | 7-------
DRadrootsCore/Cargo.toml | 14--------------
DRadrootsCore/Makefile | 60------------------------------------------------------------
DRadrootsCore/README.md | 20--------------------
DRadrootsCore/src/lib.rs | 1-
ARadrootsFFI/.gitignore | 2++
ARadrootsFFI/Config/ffi-build.env | 3+++
ARadrootsFFI/Makefile | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ARadrootsFFI/README.md | 32++++++++++++++++++++++++++++++++
RRadrootsCore/rust-toolchain.toml -> RadrootsFFI/rust-toolchain.toml | 0
ARadrootsFFI/source.lock | 4++++
13 files changed, 184 insertions(+), 107 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ -WRAPPER_ROOT := RadrootsCore +FFI_ROOT := RadrootsFFI -.PHONY: all clean build generate package bindings +.PHONY: all clean distclean sync-source build generate package install print-config -all clean build generate package bindings: - $(MAKE) -C $(WRAPPER_ROOT) $@ +all clean distclean sync-source build generate package install print-config: + $(MAKE) -C $(FFI_ROOT) $@ diff --git a/RadrootsCore/.gitignore b/RadrootsCore/.gitignore @@ -1 +0,0 @@ -target/ diff --git a/RadrootsCore/Cargo.lock b/RadrootsCore/Cargo.lock @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "radrootscore-ios" -version = "0.1.0" diff --git a/RadrootsCore/Cargo.toml b/RadrootsCore/Cargo.toml @@ -1,14 +0,0 @@ -[package] -name = "radrootscore-ios" -version = "0.1.0" -edition = "2024" -authors = ["Radroots Authors"] -rust-version = "1.92.0" -license = "AGPL-3.0" -description = "radroots ios rust wrapper manifest" - -[workspace] -resolver = "2" - -[workspace.dependencies] -radroots-app-ffi-swift = { path = "../../../../../../foundation/oss/rs/crates/app-ffi-swift" } diff --git a/RadrootsCore/Makefile b/RadrootsCore/Makefile @@ -1,60 +0,0 @@ -CRATE := radroots-app-ffi-swift -CRATES_ROOT := ../../../../../../foundation/oss/rs/crates -FFI_MANIFEST := $(CRATES_ROOT)/app-ffi-swift/Cargo.toml -RUST_TARGET_DIR := target -OUTDIR := $(RUST_TARGET_DIR)/xcframework -CARGO_FFI := CARGO_TARGET_DIR=$(RUST_TARGET_DIR) cargo - -FRAMEWORK_DEST := ../RadrootsKit/Artifacts -GENERATED_SWIFT := ../RadrootsKit/Sources/RadrootsKit/Generated -CONFIG_PATH := $(CRATES_ROOT)/app-ffi-swift/uniffi.toml - -LIB_DEV := $(RUST_TARGET_DIR)/aarch64-apple-ios/release/libradroots_app_ffi_swift.a -LIB_SIM_ARM64 := $(RUST_TARGET_DIR)/aarch64-apple-ios-sim/release/libradroots_app_ffi_swift.a -HOST_DYLIB := $(RUST_TARGET_DIR)/release/libradroots_app_ffi_swift.dylib - -HEADERS_DIR := $(OUTDIR)/headers - -.PHONY: all clean build generate package bindings - -all: clean build generate package bindings - @echo "done." - @echo " - xcframework: $(FRAMEWORK_DEST)/RadrootsFFI.xcframework" - @echo " - swift bindings: $(GENERATED_SWIFT)/*.swift" - -clean: - @echo "cleaning output dirs..." - rm -rf $(OUTDIR) $(FRAMEWORK_DEST) $(GENERATED_SWIFT) - mkdir -p $(OUTDIR) $(FRAMEWORK_DEST) $(GENERATED_SWIFT) - -build: - @echo "building $(CRATE) for ios device + simulator..." - $(CARGO_FFI) build --manifest-path $(FFI_MANIFEST) --release --target aarch64-apple-ios - $(CARGO_FFI) build --manifest-path $(FFI_MANIFEST) --release --target aarch64-apple-ios-sim - - @echo "building host cdylib for uniffi metadata..." - $(CARGO_FFI) build --manifest-path $(FFI_MANIFEST) --release - -generate: - @echo "generating swift bindings with uniffi..." - $(CARGO_FFI) run --manifest-path $(FFI_MANIFEST) --bin uniffi-bindgen -- \ - generate --library $(HOST_DYLIB) \ - --language swift \ - --out-dir $(OUTDIR)/generated \ - --config $(CONFIG_PATH) - - @echo "preparing headers..." - mkdir -p $(HEADERS_DIR) - cp $(OUTDIR)/generated/RadrootsFFI.h $(HEADERS_DIR)/ - cp $(OUTDIR)/generated/RadrootsFFI.modulemap $(HEADERS_DIR)/module.modulemap - -package: - @echo "packaging RadrootsFFI.xcframework..." - xcodebuild -create-xcframework \ - -library $(LIB_DEV) -headers $(HEADERS_DIR) \ - -library $(LIB_SIM_ARM64) -headers $(HEADERS_DIR) \ - -output $(FRAMEWORK_DEST)/RadrootsFFI.xcframework - -bindings: - @echo "copying swift bindings into RadrootsKit..." - cp $(OUTDIR)/generated/*.swift $(GENERATED_SWIFT)/ diff --git a/RadrootsCore/README.md b/RadrootsCore/README.md @@ -1,20 +0,0 @@ -# RadrootsCore - -`ios/RadrootsCore` is the iOS Rust wrapper for the shared Swift FFI crate. - -## Source of truth -- `../../../../../../foundation/oss/rs/crates/app-ffi-swift` (built via `--manifest-path`) -- the transitive `radroots-*` crate graph resolves through that vendor workspace, where those crates use `workspace = true` - -## Dependency pattern -- Keep `radroots-app-ffi-swift` in `[workspace.dependencies]` with a monorepo-relative `path`. -- Switch this to a crates.io version later without changing the make/build flow. -- Do not link `radroots-app-ffi-swift` as a Rust `[dependencies]` crate in this wrapper. - -## Build flow -Run `make -C ios` (or `make -C ios/RadrootsCore`) to: -- build from `../../../../../../foundation/oss/rs/crates/app-ffi-swift/Cargo.toml` -- write Rust build artifacts under `ios/RadrootsCore/target` -- generate UniFFI Swift bindings -- package `RadrootsFFI.xcframework` into `ios/RadrootsKit/Artifacts` -- copy generated Swift files into `ios/RadrootsKit/Sources/RadrootsKit/Generated` diff --git a/RadrootsCore/src/lib.rs b/RadrootsCore/src/lib.rs @@ -1 +0,0 @@ -#![forbid(unsafe_code)] diff --git a/RadrootsFFI/.gitignore b/RadrootsFFI/.gitignore @@ -0,0 +1,2 @@ +.build/ +target/ diff --git a/RadrootsFFI/Config/ffi-build.env b/RadrootsFFI/Config/ffi-build.env @@ -0,0 +1,3 @@ +# Optional overrides for local development. +# SOURCE_MODE=local +# LOCAL_FFI_MANIFEST=/absolute/path/to/app-ffi-swift/Cargo.toml diff --git a/RadrootsFFI/Makefile b/RadrootsFFI/Makefile @@ -0,0 +1,139 @@ +SHELL := /bin/bash +.SHELLFLAGS := -eu -o pipefail -c + +-include source.lock +-include Config/ffi-build.env + +SOURCE_MODE ?= git +RADROOTS_CRATES_GIT_URL ?= https://github.com/radrootslabs/crates.git +RADROOTS_CRATES_GIT_REV ?= 766c63c07ac7497d75a71aaab010fbc08ee934bc +RADROOTS_APP_FFI_CRATE_VERSION ?= 0.1.0 +LOCAL_FFI_MANIFEST ?= + +IOS_ROOT := $(abspath $(CURDIR)/..) +KIT_ROOT := $(IOS_ROOT)/RadrootsKit +ARTIFACTS_DIR := $(KIT_ROOT)/Artifacts +GENERATED_SWIFT_DIR := $(KIT_ROOT)/Sources/RadrootsKit/Generated + +BUILD_ROOT := $(CURDIR)/.build +SOURCE_ROOT := $(BUILD_ROOT)/source +TARGET_DIR := $(BUILD_ROOT)/target +OUT_DIR := $(BUILD_ROOT)/out +GENERATED_DIR := $(OUT_DIR)/generated +HEADERS_DIR := $(OUT_DIR)/headers + +CRATE_NAME := radroots-app-ffi-swift +FFI_OUTPUT_NAME := RadrootsFFI +LIB_STEM := libradroots_app_ffi_swift + +GIT_CRATES_DIR := $(SOURCE_ROOT)/radroots-crates +CRATE_ARCHIVE := $(SOURCE_ROOT)/$(CRATE_NAME)-$(RADROOTS_APP_FFI_CRATE_VERSION).crate +CRATE_EXTRACT_DIR := $(SOURCE_ROOT)/$(CRATE_NAME)-$(RADROOTS_APP_FFI_CRATE_VERSION) + +ifeq ($(SOURCE_MODE),git) +FFI_MANIFEST := $(GIT_CRATES_DIR)/app-ffi-swift/Cargo.toml +UNIFFI_CONFIG := $(GIT_CRATES_DIR)/app-ffi-swift/uniffi.toml +SOURCE_TARGET := sync-source-git +else ifeq ($(SOURCE_MODE),crates) +FFI_MANIFEST := $(CRATE_EXTRACT_DIR)/Cargo.toml +UNIFFI_CONFIG := $(CRATE_EXTRACT_DIR)/uniffi.toml +SOURCE_TARGET := sync-source-crates +else ifeq ($(SOURCE_MODE),local) +FFI_MANIFEST := $(LOCAL_FFI_MANIFEST) +UNIFFI_CONFIG := $(dir $(LOCAL_FFI_MANIFEST))uniffi.toml +SOURCE_TARGET := sync-source-local +else +$(error SOURCE_MODE must be one of: git, crates, local) +endif + +FFI_CRATE_DIR := $(dir $(FFI_MANIFEST)) + +CARGO := CARGO_TARGET_DIR=$(TARGET_DIR) cargo + +LIB_DEVICE := $(TARGET_DIR)/aarch64-apple-ios/release/$(LIB_STEM).a +LIB_SIMULATOR := $(TARGET_DIR)/aarch64-apple-ios-sim/release/$(LIB_STEM).a +LIB_HOST := $(TARGET_DIR)/release/$(LIB_STEM).dylib + +.PHONY: all clean distclean print-config sync-source sync-source-git sync-source-crates sync-source-local ensure-toolchain build generate package install + +all: sync-source build generate package install + @echo "done" + @echo " - source mode: $(SOURCE_MODE)" + @echo " - xcframework: $(ARTIFACTS_DIR)/$(FFI_OUTPUT_NAME).xcframework" + @echo " - swift bindings: $(GENERATED_SWIFT_DIR)" + +print-config: + @echo "SOURCE_MODE=$(SOURCE_MODE)" + @echo "FFI_MANIFEST=$(FFI_MANIFEST)" + @echo "UNIFFI_CONFIG=$(UNIFFI_CONFIG)" + @echo "ARTIFACTS_DIR=$(ARTIFACTS_DIR)" + @echo "GENERATED_SWIFT_DIR=$(GENERATED_SWIFT_DIR)" + +clean: + rm -rf $(TARGET_DIR) $(OUT_DIR) $(ARTIFACTS_DIR)/$(FFI_OUTPUT_NAME).xcframework + if [ -d $(GENERATED_SWIFT_DIR) ]; then \ + find $(GENERATED_SWIFT_DIR) -maxdepth 1 -type f -name '*.swift' -delete; \ + fi + +distclean: clean + rm -rf $(SOURCE_ROOT) + +sync-source: $(SOURCE_TARGET) + +sync-source-git: + mkdir -p $(SOURCE_ROOT) + if [ ! -d $(GIT_CRATES_DIR)/.git ]; then \ + git clone --filter=blob:none $(RADROOTS_CRATES_GIT_URL) $(GIT_CRATES_DIR); \ + fi + git -C $(GIT_CRATES_DIR) fetch --tags --force origin + git -C $(GIT_CRATES_DIR) checkout --detach $(RADROOTS_CRATES_GIT_REV) + test -f $(FFI_MANIFEST) + +sync-source-crates: + mkdir -p $(SOURCE_ROOT) + rm -rf $(CRATE_EXTRACT_DIR) + curl -fLsS https://crates.io/api/v1/crates/$(CRATE_NAME)/$(RADROOTS_APP_FFI_CRATE_VERSION)/download -o $(CRATE_ARCHIVE) + tar -xzf $(CRATE_ARCHIVE) -C $(SOURCE_ROOT) + test -f $(FFI_MANIFEST) + +sync-source-local: + if [ -z "$(LOCAL_FFI_MANIFEST)" ]; then \ + echo "LOCAL_FFI_MANIFEST is required when SOURCE_MODE=local"; \ + exit 1; \ + fi + test -f $(LOCAL_FFI_MANIFEST) + test -f $(UNIFFI_CONFIG) + +ensure-toolchain: + rustup target add aarch64-apple-ios aarch64-apple-ios-sim + +build: ensure-toolchain + mkdir -p $(TARGET_DIR) $(OUT_DIR) + $(CARGO) build --manifest-path $(FFI_MANIFEST) --release --target aarch64-apple-ios + $(CARGO) build --manifest-path $(FFI_MANIFEST) --release --target aarch64-apple-ios-sim + $(CARGO) build --manifest-path $(FFI_MANIFEST) --release + +generate: + mkdir -p $(GENERATED_DIR) $(HEADERS_DIR) + cd $(FFI_CRATE_DIR) && CARGO_TARGET_DIR=$(TARGET_DIR) cargo run --manifest-path Cargo.toml --bin uniffi-bindgen -- \ + generate --library $(LIB_HOST) \ + --language swift \ + --out-dir $(GENERATED_DIR) \ + --config $(UNIFFI_CONFIG) + cp $(GENERATED_DIR)/$(FFI_OUTPUT_NAME).h $(HEADERS_DIR)/ + cp $(GENERATED_DIR)/$(FFI_OUTPUT_NAME).modulemap $(HEADERS_DIR)/module.modulemap + +package: + mkdir -p $(ARTIFACTS_DIR) + rm -rf $(ARTIFACTS_DIR)/$(FFI_OUTPUT_NAME).xcframework + xcodebuild -create-xcframework \ + -library $(LIB_DEVICE) -headers $(HEADERS_DIR) \ + -library $(LIB_SIMULATOR) -headers $(HEADERS_DIR) \ + -output $(ARTIFACTS_DIR)/$(FFI_OUTPUT_NAME).xcframework + +install: + mkdir -p $(GENERATED_SWIFT_DIR) + if [ -d $(GENERATED_SWIFT_DIR) ]; then \ + find $(GENERATED_SWIFT_DIR) -maxdepth 1 -type f -name '*.swift' -delete; \ + fi + cp $(GENERATED_DIR)/*.swift $(GENERATED_SWIFT_DIR)/ diff --git a/RadrootsFFI/README.md b/RadrootsFFI/README.md @@ -0,0 +1,32 @@ +# RadrootsFFI + +`ios/RadrootsFFI` is the source resolver and build workspace for the Rust FFI +artifact consumed by `ios/RadrootsKit`. + +## Goals +- keep the iOS project openable and buildable in Xcode for OSS developers +- keep `radroots-app-ffi-swift` reusable for other Apple clients +- support three source modes from one Makefile: `git`, `crates`, and `local` + +## Quick start +- build everything: `make -C ios all` +- print current config: `make -C ios print-config` +- rebuild from scratch: `make -C ios distclean all` + +## Source modes +- `SOURCE_MODE=git` (default) + - clones `RADROOTS_CRATES_GIT_URL` at `RADROOTS_CRATES_GIT_REV` + - builds `app-ffi-swift` from the checked out workspace +- `SOURCE_MODE=crates` + - downloads `radroots-app-ffi-swift` from crates.io by version +- `SOURCE_MODE=local` + - requires `LOCAL_FFI_MANIFEST=/absolute/path/to/app-ffi-swift/Cargo.toml` + +## Configuration +Configuration is read from: +- `RadrootsFFI/source.lock` for pinned defaults +- `RadrootsFFI/Config/ffi-build.env` for optional local overrides + +## Outputs +- xcframework: `ios/RadrootsKit/Artifacts/RadrootsFFI.xcframework` +- generated swift bindings: `ios/RadrootsKit/Sources/RadrootsKit/Generated` diff --git a/RadrootsCore/rust-toolchain.toml b/RadrootsFFI/rust-toolchain.toml diff --git a/RadrootsFFI/source.lock b/RadrootsFFI/source.lock @@ -0,0 +1,4 @@ +SOURCE_MODE=git +RADROOTS_CRATES_GIT_URL=https://github.com/radrootslabs/crates.git +RADROOTS_CRATES_GIT_REV=766c63c07ac7497d75a71aaab010fbc08ee934bc +RADROOTS_APP_FFI_CRATE_VERSION=0.1.0