diff --git a/bindings/swift/OuisyncLib/.gitignore b/bindings/swift/OuisyncLib/.gitignore
index 72917e5fd..878cfd1fb 100644
--- a/bindings/swift/OuisyncLib/.gitignore
+++ b/bindings/swift/OuisyncLib/.gitignore
@@ -1,4 +1,5 @@
-/output
+/OuisyncLibFFI.xcframework
+/config.sh
.DS_Store
/.build
/Packages
diff --git a/bindings/swift/OuisyncLib/Package.swift b/bindings/swift/OuisyncLib/Package.swift
index 592ff50cf..49a169653 100644
--- a/bindings/swift/OuisyncLib/Package.swift
+++ b/bindings/swift/OuisyncLib/Package.swift
@@ -1,7 +1,6 @@
// swift-tools-version: 5.9
import PackageDescription
-
let package = Package(
name: "OuisyncLib",
platforms: [.macOS(.v13), .iOS(.v16)],
@@ -25,7 +24,7 @@ let package = Package(
path: "Tests"),
// FIXME: move this to a separate package / framework
.binaryTarget(name: "OuisyncLibFFI",
- path: "output/OuisyncLibFFI.xcframework"),
+ path: "OuisyncLibFFI.xcframework"),
.plugin(name: "FFIBuilder",
capability: .buildTool(),
path: "Plugins/Builder"),
@@ -34,8 +33,7 @@ let package = Package(
description: "Update rust dependencies"),
permissions: [
.allowNetworkConnections(scope: .all(),
- reason: "Downloads dependencies defined by Cargo.toml"),
- .writeToPackageDirectory(reason: "These are not the droids you are looking for")]),
+ reason: "Downloads dependencies defined by Cargo.toml")]),
path: "Plugins/Updater"),
]
)
diff --git a/bindings/swift/OuisyncLib/Plugins/Updater/updater.swift b/bindings/swift/OuisyncLib/Plugins/Updater/updater.swift
index a32fb6d60..af74e20e6 100644
--- a/bindings/swift/OuisyncLib/Plugins/Updater/updater.swift
+++ b/bindings/swift/OuisyncLib/Plugins/Updater/updater.swift
@@ -18,18 +18,11 @@ import PackagePlugin
func performCommand(context: PackagePlugin.PluginContext,
arguments: [String] = []) async throws {
- let update = context.pluginWorkDirectory
-
- // FIXME: this path is very unstable; we might need to search the tree instead
- let build = update
- .removingLastComponent()
- .appending(["\(context.package.id).output", "OuisyncLib", "FFIBuilder"])
-
let task = Process()
let exe = context.package.directory.appending(["Plugins", "update.sh"]).string
task.standardInput = nil
task.executableURL = URL(fileURLWithPath: exe)
- task.arguments = [update.string, build.string]
+ task.arguments = [context.pluginWorkDirectory.string]
do { try task.run() } catch { panic("Unable to start \(exe): \(error)") }
task.waitUntilExit()
diff --git a/bindings/swift/OuisyncLib/Plugins/build.sh b/bindings/swift/OuisyncLib/Plugins/build.sh
index a08e07b3c..1e616d649 100755
--- a/bindings/swift/OuisyncLib/Plugins/build.sh
+++ b/bindings/swift/OuisyncLib/Plugins/build.sh
@@ -1,26 +1,19 @@
#!/usr/bin/env zsh
# Command line tool which produces a `OuisyncLibFFI` framework for all configured llvm triples from
-# OuisyncLib/config.sh
-#
-# This tool runs in a sandboxed process that can only write to a `output` folder and cannot access
-# the network, so it relies on the `Updater` companion plugin to download the required dependencies
-# before hand. Unfortunately, this does not work 100% of the time since both rust and especially
-# cargo like to touch the lockfiles or the network for various reasons even when told not to.
-#
-# Called by the builder plugin which passes its own environment as well as the input, dependency
-# and output paths. The builder checks that the dependency folder exists, but does not otherwise
-# FIXME: validate that it contains all necessary dependencies as defined in Cargo.toml
-#
-# Hic sunt dracones! These might be of interest to anyone thinking they can do better than this mess:
+# OuisyncLib/config.sh (currently generated in the ouisync-app repository)
#
+# This tool runs in a sandboxed process that cannot access the network, so it relies on the updater
+# companion plugin to download the required dependencies ahead of time. Called by the builder plugin
+# which passes both plugins' output paths as arguments. Hic sunt dracones! These may be of interest:
# [1] https://forums.developer.apple.com/forums/thread/666335
# [2] https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/Plugins.md#build-tool-target-dependencies
# [3] https://www.amyspark.me/blog/posts/2024/01/10/stripping-rust-libraries.html
+fatal() { echo "Error $@" && exit $1 }
PROJECT_HOME=$(realpath "$(dirname "$0")/../../../../")
-PACKAGE_HOME=$(realpath "$PROJECT_HOME/bindings/swift/OuisyncLib")
-export CARGO_HOME="$1"
+export CARGO_HOME=$(realpath "$1")
+export PATH="$CARGO_HOME/bin:$PATH"
export RUSTUP_HOME="$CARGO_HOME/.rustup"
-BUILD_OUTPUT="$2"
+BUILD_OUTPUT=$(realpath "$2")
# cargo builds some things that confuse xcode such as fingerprints and depfiles which cannot be
# (easily) disabled; additionally, xcode does pick up the xcframework and reports it as a duplicate
@@ -29,7 +22,7 @@ BUILD_OUTPUT="$2"
mkdir -p "$BUILD_OUTPUT/dummy"
# read config and prepare to build
-source "$PACKAGE_HOME/config.sh"
+source "$PROJECT_HOME/bindings/swift/OuisyncLib/config.sh"
if [ $SKIP ] && [ $SKIP -gt 0 ]; then
exit 0
fi
@@ -49,12 +42,12 @@ for TARGET in $LIST[@]; do TARGETS[$TARGET]="" done
# build configured targets
cd $PROJECT_HOME
for TARGET in ${(k)TARGETS}; do
- "$CARGO_HOME/bin/cross" build \
+ cross build \
--frozen \
--package ouisync-ffi \
--target $TARGET \
--target-dir "$BUILD_OUTPUT" \
- $FLAGS || exit 1
+ $FLAGS || fatal 1 "Unable to compile for $TARGET"
done
# generate include files
@@ -64,11 +57,7 @@ echo "module OuisyncLibFFI {
header \"bindings.h\"
export *
}" > "$INCLUDE/module.modulemap"
-"$CARGO_HOME/bin/cbindgen" --lang C --crate ouisync-ffi > "$INCLUDE/bindings.h" || exit 2
-
-# delete previous framework (possibly a stub) and replace with new one that contains the archive
-# TODO: some symlinks would be lovely here instead, cargo already create two copies
-rm -Rf $BUILD_OUTPUT/output/OuisyncLibFFI.xcframework
+cbindgen --lang C --crate ouisync-ffi > "$INCLUDE/bindings.h" || fatal 2 "Unable to generate bindings.h"
# xcodebuild refuses multiple architectures per platform, instead expecting fat libraries when the
# destination operating system supports multiple architectures; apple also explicitly rejects any
@@ -96,11 +85,17 @@ for PLATFORM OUTPUTS in ${(kv)TREE}; do
else # at least two architectures; run lipo on all matches and link the output instead
LIBRARY="$BUILD_OUTPUT/$PLATFORM/libouisync_ffi.a"
mkdir -p "$(dirname "$LIBRARY")"
- lipo -create $MATCHED[@] -output $LIBRARY || exit 3
+ lipo -create $MATCHED[@] -output $LIBRARY || fatal 3 "Unable to run lipo for ${MATCHED[@]}"
fi
PARAMS+=("-library" "$LIBRARY" "-headers" "$INCLUDE")
done
-echo ${PARAMS[@]}
+
+# TODO: skip xcodebuild and manually create symlinks instead (faster but Info.plist would be tricky)
+rm -Rf "$BUILD_OUTPUT/temp.xcframework"
+find "$BUILD_OUTPUT/OuisyncLibFFI.xcframework" -mindepth 1 -delete
xcodebuild \
-create-xcframework ${PARAMS[@]} \
- -output "$BUILD_OUTPUT/output/OuisyncLibFFI.xcframework" || exit 4
+ -output "$BUILD_OUTPUT/temp.xcframework" || fatal 4 "Unable to build xcframework"
+for FILE in $(ls "$BUILD_OUTPUT/temp.xcframework"); do
+ mv "$BUILD_OUTPUT/temp.xcframework/$FILE" "$BUILD_OUTPUT/OuisyncLibFFI.xcframework/$FILE"
+done
diff --git a/bindings/swift/OuisyncLib/Plugins/update.sh b/bindings/swift/OuisyncLib/Plugins/update.sh
index 570fe5859..445ea331c 100755
--- a/bindings/swift/OuisyncLib/Plugins/update.sh
+++ b/bindings/swift/OuisyncLib/Plugins/update.sh
@@ -1,39 +1,28 @@
#!/usr/bin/env zsh
# Command line tool which pulls all dependencies needed to build the rust core library.
-#
-# Assumes that `cargo` and `rustup` are installed and available in REAL_PATH and it is run with the
-# two plugin output paths (update and build)
+fatal() { echo "Error $@" >&2 && exit $1 }
PROJECT_HOME=$(realpath "$(dirname "$0")/../../../../")
-PACKAGE_HOME=$(realpath "$PROJECT_HOME/bindings/swift/OuisyncLib")
-export CARGO_HOME="$1"
-export CARGO_HTTP_CHECK_REVOKE="false" # unclear why this fails, but it does
-export RUSTUP_USE_CURL=1 # https://github.com/rust-lang/rustup/issues/1856
-
-# download all possible toolchains: they only take up about 100MiB in total
-mkdir -p .rustup
+export CARGO_HOME=$(realpath "$1")
+export PATH="$CARGO_HOME/bin:$PATH"
export RUSTUP_HOME="$CARGO_HOME/.rustup"
-rustup default stable
-rustup target install aarch64-apple-darwin aarch64-apple-ios aarch64-apple-ios-sim \
- x86_64-apple-darwin x86_64-apple-ios
-
-cd "$PROJECT_HOME"
-cargo fetch --locked || exit 1 # this is currently only fixable by moving the plugin location
-cargo install cbindgen cross || exit 2 # build.sh also needs `cbindgen` and `cross`
-# as part of the updater, we also perform the xcode symlink hack: we replace the existing
-# $PACKAGE_HOME/output folder (either stub checked out by git or symlink to a previous build) with
-# a link to the $BUILD_OUTPUT/output folder which will eventually contain an actual framework
-BUILD_OUTPUT="$2"
-mkdir -p "$BUILD_OUTPUT"
-cd "$BUILD_OUTPUT" > /dev/null
-# if this is the first time we build at this location, generate a new stub library to keep xcode
-# happy in case the build process fails later down the line
-if ! [ -d "output/OuisyncLibFFI.xcframework" ]; then
- "$PACKAGE_HOME/reset-output.sh"
+# install rust or update to latest version
+export RUSTUP_USE_CURL=1 # https://github.com/rust-lang/rustup/issues/1856
+if [ -f "$CARGO_HOME/bin/rustup" ]; then
+ rustup update || fatal 1 "Unable to update rust"
+else
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path \
+ || fatal 1 "Unable to install rust"
fi
-# we can now replace the local stub (or prior link) with a link to the most recent build location
-rm -rf "$PACKAGE_HOME/output"
-ln -s "$BUILD_OUTPUT/output" "$PACKAGE_HOME/output"
+# also install all possible toolchains since they only take up about 100MiB in total
+export CARGO_HTTP_CHECK_REVOKE="false" # unclear it fails without this, but it does
+rustup target install aarch64-apple-darwin aarch64-apple-ios \
+ aarch64-apple-ios-sim x86_64-apple-darwin x86_64-apple-ios || fatal 2 "Unable to install rust via rustup"
+
+# build.sh needs `cbindgen` and `cross` to build as a multiplatform framework
+cargo install cbindgen cross || fatal 3 "Unable to install header generator or cross compiler"
-# unfortunately, we can't trigger a build from here because `build.sh` runs in a different sandbox
+# fetch all up to date package dependencies for the next build (which must run offline)
+cd "$PROJECT_HOME"
+cargo fetch --locked || fatal 4 "Unable to fetch library dependencies"
diff --git a/bindings/swift/OuisyncLib/config.sh b/bindings/swift/OuisyncLib/config.sh
deleted file mode 100644
index 32dc0832a..000000000
--- a/bindings/swift/OuisyncLib/config.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-# this file is sourced by `build.sh`; keep as many options enabled as you have patience for
-SKIP=1
-#DEBUG=1 # set to 0 to generate release builds (much faster)
-TARGETS=(
- aarch64-apple-darwin # mac on apple silicon
- x86_64-apple-darwin # mac on intel
- aarch64-apple-ios # all real devices (ios 11+ are 64 bit only)
- aarch64-apple-ios-sim # simulators when running on M chips
- x86_64-apple-ios # simulator running on intel chips
-)
-# make sure to re-run "Update rust dependencies" after making changes here
diff --git a/bindings/swift/OuisyncLib/output/OuisyncLibFFI.xcframework/Info.plist.sample b/bindings/swift/OuisyncLib/output/OuisyncLibFFI.xcframework/Info.plist.sample
deleted file mode 100644
index 670c55dbb..000000000
--- a/bindings/swift/OuisyncLib/output/OuisyncLibFFI.xcframework/Info.plist.sample
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- AvailableLibraries
-
-
- CFBundlePackageType
- XFWK
- XCFrameworkFormatVersion
- 1.0
-
-
diff --git a/bindings/swift/OuisyncLib/reset-output.sh b/bindings/swift/OuisyncLib/reset-output.sh
deleted file mode 100755
index 2d3489aed..000000000
--- a/bindings/swift/OuisyncLib/reset-output.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env sh
-# Command line tool which produces a stub `OuisyncLibFFI` framework in the current directory
-#
-# Xcode expects a valid binary target to be available at package resolution time at the location
-# specified in Package.swift, otherwise it refuses to register any plugins. To work around this
-# limitation, we include a gitignored stub in the repository that is then then first replaced by the
-# the updater plugin with a link to the same stub in the build folder (the only folder writable
-# within the build plugin sandbox), then replaced with a real framework by the build plugin.
-#
-# While the process seems to work, we may run into edge cases where the framework gets corrupted,
-# resulting in the inability to run the updater script that would fix it. If and when that happens,
-# run this script to reset the framework to its original stub version from git.
-#
-echo d - ./output
-rm -Rf ./output
-#
-# Generated by shar $(find output -print)
-#
-# This is a shell archive. Save it in a file, remove anything before
-# this line, and then unpack it by entering "sh file". Note, it may
-# create directories; files and directories will be owned by you and
-# have default permissions.
-#
-# This archive contains:
-#
-# .
-# ./output
-# ./output/OuisyncLibFFI.xcframework
-# ./output/OuisyncLibFFI.xcframework/macos-x86_64
-# ./output/OuisyncLibFFI.xcframework/macos-x86_64/Headers
-# ./output/OuisyncLibFFI.xcframework/macos-x86_64/Headers/module.modulemap
-# ./output/OuisyncLibFFI.xcframework/macos-x86_64/libouisync_ffi.a
-# ./output/OuisyncLibFFI.xcframework/Info.plist
-#
-echo c - .
-mkdir -p . > /dev/null 2>&1
-echo c - ./output
-mkdir -p ./output > /dev/null 2>&1
-echo c - ./output/OuisyncLibFFI.xcframework
-mkdir -p ./output/OuisyncLibFFI.xcframework > /dev/null 2>&1
-echo c - ./output/OuisyncLibFFI.xcframework/macos-x86_64
-mkdir -p ./output/OuisyncLibFFI.xcframework/macos-x86_64 > /dev/null 2>&1
-echo c - ./output/OuisyncLibFFI.xcframework/macos-x86_64/Headers
-mkdir -p ./output/OuisyncLibFFI.xcframework/macos-x86_64/Headers > /dev/null 2>&1
-echo x - ./output/OuisyncLibFFI.xcframework/macos-x86_64/Headers/module.modulemap
-sed 's/^X//' >./output/OuisyncLibFFI.xcframework/macos-x86_64/Headers/module.modulemap << '27ba995dcca9d28af9ee52fafa7cdc12'
-Xmodule OuisyncLibFFI {
-X export *
-X}
-27ba995dcca9d28af9ee52fafa7cdc12
-echo x - ./output/OuisyncLibFFI.xcframework/macos-x86_64/libouisync_ffi.a
-sed 's/^X//' >./output/OuisyncLibFFI.xcframework/macos-x86_64/libouisync_ffi.a << '452e73dffcd1e38b0e076852cbd16868'
-452e73dffcd1e38b0e076852cbd16868
-echo x - ./output/OuisyncLibFFI.xcframework/Info.plist
-sed 's/^X//' >./output/OuisyncLibFFI.xcframework/Info.plist << '176f576dd1dba006b62db63408ad24c2'
-X
-X
-X
-X
-X AvailableLibraries
-X
-X
-X BinaryPath
-X libouisync_ffi.a
-X HeadersPath
-X Headers
-X LibraryIdentifier
-X macos-x86_64
-X LibraryPath
-X libouisync_ffi.a
-X SupportedArchitectures
-X
-X x86_64
-X
-X SupportedPlatform
-X macos
-X
-X
-X CFBundlePackageType
-X XFWK
-X XCFrameworkFormatVersion
-X 1.0
-X
-X
-176f576dd1dba006b62db63408ad24c2
-exit