diff --git a/.github/workflows/release-swift-kotlin.yml b/.github/workflows/release-swift-kotlin.yml index 3e4981765..8ce9b11b4 100644 --- a/.github/workflows/release-swift-kotlin.yml +++ b/.github/workflows/release-swift-kotlin.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Get new version id: version @@ -29,6 +29,8 @@ jobs: echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT build-swift: + # Swift depends on the host macOS/Xcode configuration and intentionally does + # not run inside Nix. runs-on: macos-latest needs: pre-release-checks permissions: @@ -36,7 +38,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ needs.pre-release-checks.outputs.commit_sha }} # to ensure all builds are consistent @@ -57,14 +59,14 @@ jobs: zip -r WalletKit.xcframework.zip swift/WalletKit.xcframework - name: Checkout swift repo - uses: actions/checkout@v6 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: repository: worldcoin/walletkit-swift token: ${{ secrets.WALLETKIT_BOT_TOKEN }} path: target-repo - name: Create draft release in walletkit-swift & upload binaries - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2 id: release with: repository: worldcoin/walletkit-swift @@ -126,11 +128,9 @@ jobs: name: Prepare Kotlin runs-on: arc-public-8xlarge-amd64-runner needs: [pre-release-checks] - env: - CARGO_HOME: /home/runner/_work/_cargo - RUSTUP_HOME: /home/runner/_work/_rustup permissions: - contents: write # to upload artifacts + contents: read + actions: write # to save Rust cache # Each target is built separately to run in parallel. Running sequentially takes vastly more time. strategy: @@ -143,31 +143,36 @@ jobs: - target: i686-linux-android steps: - - name: Add Cargo to PATH - run: echo "$CARGO_HOME/bin" >> $GITHUB_PATH - - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ needs.pre-release-checks.outputs.commit_sha }} # to ensure all builds are consistent - - name: Set up Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.92.0 - targets: ${{ matrix.settings.target }} - components: rustfmt + - name: Install Nix + uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31.10.6 - - name: Install Cross - run: | - cargo install cross --git https://github.com/cross-rs/cross --locked + # TODO: magic-nix-cache is best-effort; builds still work without it. + - name: Enable Nix cache + uses: DeterminateSystems/magic-nix-cache-action@565684385bcd71bad329742eefe8d12f2e765b39 # v13 + + - name: Cache Rust build artifacts + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target/release + target/${{ matrix.settings.target }} + key: ${{ runner.os }}-cargo-android-${{ matrix.settings.target }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml', 'flake.lock', 'flake.nix', 'nix/**/*.nix') }} + restore-keys: | + ${{ runner.os }}-cargo-android-${{ matrix.settings.target }}- + ${{ runner.os }}-cargo-android- - name: Build for target - run: | - CROSS_NO_WARNINGS=0 cross build -p walletkit --target ${{ matrix.settings.target }} --release --locked --features "$WALLETKIT_CARGO_FEATURES" + run: ./nix/build-android.sh --target ${{ matrix.settings.target }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: android-${{ matrix.settings.target }} path: ./target/${{ matrix.settings.target }}/release/libwalletkit.so @@ -183,40 +188,30 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ needs.pre-release-checks.outputs.commit_sha }} # to ensure all builds are consistent - - name: Set up Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.92.0 - targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android - components: rustfmt + - name: Install Nix + uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31.10.6 + + # TODO: magic-nix-cache is best-effort; builds still work without it. + - name: Enable Nix cache + uses: DeterminateSystems/magic-nix-cache-action@565684385bcd71bad329742eefe8d12f2e765b39 # v13 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@17f84c3641ba7b8f6deff6309fc4c864478f5d62 # v3.14.1 with: distribution: temurin java-version: 17 - name: Download Artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: - path: . + path: artifacts - - name: Move artifacts - run: | - mkdir -p kotlin/walletkit/src/main/jniLibs && cd kotlin/walletkit/src/main/jniLibs - mkdir armeabi-v7a arm64-v8a x86 x86_64 - mv /home/runner/work/walletkit/walletkit/android-armv7-linux-androideabi/libwalletkit.so ./armeabi-v7a/libwalletkit.so - mv /home/runner/work/walletkit/walletkit/android-aarch64-linux-android/libwalletkit.so ./arm64-v8a/libwalletkit.so - mv /home/runner/work/walletkit/walletkit/android-i686-linux-android/libwalletkit.so ./x86/libwalletkit.so - mv /home/runner/work/walletkit/walletkit/android-x86_64-linux-android/libwalletkit.so ./x86_64/libwalletkit.so - - - name: Generate bindings - working-directory: kotlin - run: cargo run -p uniffi-bindgen generate ./walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so --library --language kotlin --no-format --out-dir walletkit/src/main/java + - name: Assemble jniLibs & generate bindings + run: nix develop . --command ./kotlin/build_kotlin.sh --artifacts-dir artifacts - name: Publish working-directory: kotlin diff --git a/Cross.toml b/Cross.toml deleted file mode 100644 index d65322e09..000000000 --- a/Cross.toml +++ /dev/null @@ -1,24 +0,0 @@ -[build.env] -passthrough = [ - "CARGO_NET_GIT_FETCH_WITH_CLI=true", # to force cargo to use git cli with config --global in homedir - "HOME", # set $HOME to shared dir where we put .gitconfig - "RUSTUP_HOME", # override rustup home to prevent host permission issues - "CARGO_HOME", # override cargo home to prevent host permission issues -] - -# max-page-size=16384: -# Android 15 (API 35) introduces support for 16KB page sizes to improve performance on devices with larger RAM. -# Apps with native libraries MUST be compiled with 16KB ELF alignment or they will crash on startup -# on devices configured with 16KB page sizes. This flag ensures proper alignment for both 32-bit and 64-bit targets. -# Reference: https://developer.android.com/guide/practices/page-sizes -[target.aarch64-linux-android] -rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] - -[target.armv7-linux-androideabi] -rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] - -[target.x86_64-linux-android] -rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] - -[target.i686-linux-android] -rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] diff --git a/README.md b/README.md index 886ea5554..a9e86ca09 100644 --- a/README.md +++ b/README.md @@ -40,13 +40,9 @@ Replace `VERSION` with the desired WalletKit version. ### Prerequisites -1. **Docker Desktop**: Required for cross-compilation - - The build uses [`cross`](https://github.com/cross-rs/cross) which runs builds in Docker containers with all necessary toolchains - - Install via Homebrew: - ```bash - brew install --cask docker - ``` - - Launch Docker Desktop and ensure it's running before building +1. **Nix**: The Android cross-compilation toolchain (Rust, NDK, linkers) is provided + by the Nix devshell — see [`nix/README.md`](nix/README.md). Without Nix, you + must provision the equivalent dependencies manually. 2. **Android SDK + NDK**: Required for Gradle Android tasks - Install via Android Studio > Settings > Android SDK (ensure the NDK is installed) @@ -57,7 +53,7 @@ Replace `VERSION` with the desired WalletKit version. To test local changes before publishing a release, use the build script to compile the Rust library, generate UniFFI bindings, and publish a SNAPSHOT to Maven Local: ```bash -./kotlin/local_kotlin.sh 0.3.1 +nix develop .#android --command ./kotlin/local_kotlin.sh 0.3.1 ``` Example with custom Rust locations: @@ -65,7 +61,7 @@ Example with custom Rust locations: RUSTUP_HOME=~/.rustup CARGO_HOME=~/.cargo ./kotlin/local_kotlin.sh 0.1.0-SNAPSHOT ``` -> **Note**: The script can be run from any working directory (it resolves its own location). It sets `RUSTUP_HOME` and `CARGO_HOME` to `/tmp` by default to avoid Docker permission issues when using `cross`. You can override them by exporting your own values. +> **Note**: The script resolves its own location, but does not provision or enter the build environment. Run it from the Nix `android` devshell or with the required dependencies configured manually. This will: 1. Build the Rust library for all Android architectures (arm64-v8a, armeabi-v7a, x86_64, x86) diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..cfb7a8b68 --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1777268161, + "narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777605393, + "narHash": "sha256-Hjp0VOOHgHcTrX23iVvnfAudPcuCmfkfpQNFwv2v/ks=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ff88db34cfa486fc4964a6991cab1678d82eee8c", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..49b3ac9d0 --- /dev/null +++ b/flake.nix @@ -0,0 +1,51 @@ +{ + description = "WalletKit development shells"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay.url = "github:oxalica/rust-overlay"; + rust-overlay.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { nixpkgs, flake-utils, rust-overlay, ... }: + flake-utils.lib.eachSystem [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + ] (system: + let + overlays = [ + (import rust-overlay) + (final: prev: { nargo = final.callPackage ./nix/nargo.nix { }; }) + ]; + pkgs = import nixpkgs { + inherit system overlays; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + }; + + rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + in + { + devShells = { + default = pkgs.mkShell { + packages = [ + rustToolchain + pkgs.curl + pkgs.git + pkgs.nargo + ]; + }; + + wasm = import ./nix/wasm.nix { inherit pkgs; }; + } // pkgs.lib.optionalAttrs (!(pkgs.stdenv.isLinux && pkgs.stdenv.isAarch64)) { + # The Android NDK has no aarch64-linux prebuilt toolchain + # (on aarch64 Docker hosts, run the container as linux/amd64). + android = import ./nix/android.nix { inherit pkgs; }; + }; + }); +} diff --git a/kotlin/build_kotlin.sh b/kotlin/build_kotlin.sh index ffa2057c7..f13b322a0 100755 --- a/kotlin/build_kotlin.sh +++ b/kotlin/build_kotlin.sh @@ -1,42 +1,103 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -euo pipefail -## This script is only used for local builds. For production releases, the code is in the CI workflow. +# Build Android native libraries and assemble the Kotlin JNI/UniFFI bindings. +# Usage: ./kotlin/build_kotlin.sh [--artifacts-dir ] +# See nix/README.md for build environment setup. -echo "Building WalletKit Android SDK..." +cd "$(dirname "${BASH_SOURCE[0]}")/.." -CARGO_FEATURES="compress-zkeys,embed-zkeys,v3" +readonly TARGETS=( + "aarch64-linux-android" + "armv7-linux-androideabi" + "x86_64-linux-android" + "i686-linux-android" +) -# Create jniLibs directories -mkdir -p ./walletkit/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86_64,x86} +abi_for_target() { + case "$1" in + aarch64-linux-android) echo "arm64-v8a" ;; + armv7-linux-androideabi) echo "armeabi-v7a" ;; + x86_64-linux-android) echo "x86_64" ;; + i686-linux-android) echo "x86" ;; + esac +} -# Build for all Android architectures -echo "Building for aarch64-linux-android..." -cross build -p walletkit --release --target=aarch64-linux-android --features "$CARGO_FEATURES" +usage() { + cat <] -echo "Building for armv7-linux-androideabi..." -cross build -p walletkit --release --target=armv7-linux-androideabi --features "$CARGO_FEATURES" +Options: + --artifacts-dir Skip the cargo builds and take prebuilt libraries + from /android-/libwalletkit.so + (the layout produced by the CI build matrix). -echo "Building for x86_64-linux-android..." -cross build -p walletkit --release --target=x86_64-linux-android --features "$CARGO_FEATURES" +EOF +} -echo "Building for i686-linux-android..." -cross build -p walletkit --release --target=i686-linux-android --features "$CARGO_FEATURES" +ARTIFACTS_DIR="" -# Move .so files to jniLibs -echo "Moving native libraries..." -mv ../target/aarch64-linux-android/release/libwalletkit.so ./walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so -mv ../target/armv7-linux-androideabi/release/libwalletkit.so ./walletkit/src/main/jniLibs/armeabi-v7a/libwalletkit.so -mv ../target/x86_64-linux-android/release/libwalletkit.so ./walletkit/src/main/jniLibs/x86_64/libwalletkit.so -mv ../target/i686-linux-android/release/libwalletkit.so ./walletkit/src/main/jniLibs/x86/libwalletkit.so +while [[ $# -gt 0 ]]; do + case "$1" in + --artifacts-dir) + if [[ $# -lt 2 ]]; then + echo "error: --artifacts-dir requires a value" >&2 + usage >&2 + exit 1 + fi + ARTIFACTS_DIR="$2" + shift 2 + ;; + --help | -h) + usage + exit 0 + ;; + *) + echo "error: unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "${ARTIFACTS_DIR}" ]]; then + if [[ -z "${CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER:-}" ]]; then + echo "error: Android cross-compilation environment not configured." >&2 + echo "Run inside the Nix devshell: nix develop .#android --command $0" >&2 + echo "or via Docker without Nix: nix/docker.sh develop .#android --command $0" >&2 + exit 1 + fi + + # Defaults to the release feature set; CI overrides via WALLETKIT_CARGO_FEATURES. + CARGO_FEATURES="${WALLETKIT_CARGO_FEATURES:-compress-zkeys,embed-zkeys,v3}" + + echo "Building WalletKit Android SDK..." + for target in "${TARGETS[@]}"; do + echo "Building for ${target}..." + cargo build -p walletkit --release --locked --target "${target}" --features "$CARGO_FEATURES" + done +fi + +# Copy .so files into the jniLibs layout +echo "Copying native libraries..." +for target in "${TARGETS[@]}"; do + abi="$(abi_for_target "${target}")" + if [[ -n "${ARTIFACTS_DIR}" ]]; then + src="${ARTIFACTS_DIR}/android-${target}/libwalletkit.so" + else + src="target/${target}/release/libwalletkit.so" + fi + mkdir -p "kotlin/walletkit/src/main/jniLibs/${abi}" + cp "${src}" "kotlin/walletkit/src/main/jniLibs/${abi}/libwalletkit.so" +done # Generate Kotlin bindings echo "Generating Kotlin bindings..." -cargo run -p uniffi-bindgen generate \ - ./walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so \ +cargo run -p uniffi-bindgen --locked -- generate \ + kotlin/walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so \ --library \ --language kotlin \ --no-format \ - --out-dir walletkit/src/main/java + --out-dir kotlin/walletkit/src/main/java echo "✅ Build complete!" diff --git a/kotlin/local_kotlin.sh b/kotlin/local_kotlin.sh index a119a2b29..e4b9f3a67 100755 --- a/kotlin/local_kotlin.sh +++ b/kotlin/local_kotlin.sh @@ -1,11 +1,11 @@ #!/bin/bash set -e -echo "Building WalletKit Android SDK for local development..." +# Build the Kotlin bindings and publish them to Maven Local. +# Usage: ./kotlin/local_kotlin.sh +# See nix/README.md for build environment setup. -# Set rustup and cargo home to /tmp to prevent Docker permission issues -export RUSTUP_HOME="${RUSTUP_HOME:-/tmp/.rustup}" -export CARGO_HOME="${CARGO_HOME:-/tmp/.cargo}" +echo "Building WalletKit Android SDK for local development..." # Version is required if [ -z "$1" ]; then @@ -18,7 +18,6 @@ fi VERSION="$1" echo "Using version: $VERSION" -# Build using kotlin/build.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" diff --git a/kotlin/walletkit/src/main/jniLibs/.gitignore b/kotlin/walletkit/src/main/jniLibs/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/kotlin/walletkit/src/main/jniLibs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 000000000..6d4b1433a --- /dev/null +++ b/nix/README.md @@ -0,0 +1,77 @@ +# Nix development environments + +This directory contains the Nix devshells and helper scripts used to +cross-compile WalletKit for Android and wasm, both locally and in CI. +Swift/iOS builds intentionally continue to use their existing host Xcode setup. + +The flake provides **devshells, not packages**: the shells pin the Rust +toolchain (from `rust-toolchain.toml`), the Android NDK, nargo and every +env var cargo needs for cross-compilation — the builds themselves are +ordinary `cargo build` invocations run inside a shell. + +## Shells + +| Shell | Purpose | Systems | +|---|---|---| +| `default` | Host builds, uniffi-bindgen, nargo | all | +| `android` | Cross-compile the 4 Android targets (NDK, linkers, API 23) | linux/darwin x86_64, darwin aarch64 | +| `wasm` | `wasm32-unknown-unknown` with a wasm-safe clang | all | + +```bash +nix develop .#android # enter a shell +nix develop .#android --command cargo build ... # or run one command +``` + +Convenience wrappers (they enter the right shell for you): + +```bash +nix/build-android.sh --target aarch64-linux-android +nix/build-wasm.sh +nix develop .#android --command ./kotlin/build_kotlin.sh # full Android jniLibs + bindings +nix develop .#android --command ./kotlin/local_kotlin.sh 0.3.1 # publish to Maven Local +``` + +## No Nix installed? Use Docker + +`nix/docker.sh` proxies Nix commands into a `nixos/nix` container — the only +host dependency is Docker. Its arguments intentionally match the native Nix CLI: + +```bash +nix/docker.sh develop .#android --command ./kotlin/build_kotlin.sh +nix/docker.sh develop .#wasm --command cargo build -p walletkit --release --locked --target wasm32-unknown-unknown +nix/docker.sh develop .#default # interactive shell +nix/docker.sh flake show # other Nix commands work too +``` + +Native Nix is recommended when available. The Docker wrapper always uses a +`linux/amd64` container because the Android NDK does not ship an aarch64-linux +host toolchain. Consequently, it runs under emulation on ARM hosts such as Apple +Silicon and is slower than `nix develop` directly. + +Notes: + +- The Nix store is kept in the `walletkit-nix-store-amd64` Docker volume, so + toolchains download only on the first run. Remove it with `docker volume rm + walletkit-nix-store-amd64` to reclaim the space. Cargo uses the corresponding + `walletkit-cargo-home-amd64` volume. +- On Apple Silicon, enable Rosetta emulation in Docker Desktop settings (on by + default in recent versions). +- The flake does not provide a Swift shell. Swift builds depend on the host + macOS/Xcode configuration; see [`swift/README.md`](../swift/README.md). +- On Linux hosts, files created by the container (e.g. `target/`) are + root-owned. + +## Building without Nix at all + +Possible, if you bring the dependencies yourself: + +- **Host / wasm**: `rustup` picks the toolchain and targets up from + `rust-toolchain.toml`, and `.cargo/config.toml` carries the target + rustflags. wasm needs clang ≥ 18 exported as `CC_wasm32_unknown_unknown` + (see `nix/wasm.nix`). +- **Android**: you need NDK r27 and the `CC_*`/`AR_*`/ + `CARGO_TARGET_*_LINKER` env vars pointing into it — exactly what + `nix/android.nix` sets. There is no script for the manual setup; the + Nix shell (or its Docker wrapper) is the supported path. +- **nargo**: install the version matching the `provekit_*` crates in + `Cargo.lock` (see `nix/nargo.nix`). diff --git a/nix/android.nix b/nix/android.nix new file mode 100644 index 000000000..d04127e55 --- /dev/null +++ b/nix/android.nix @@ -0,0 +1,76 @@ +{ pkgs }: +let + androidApiLevel = "23"; + androidPackages = pkgs.androidenv.composeAndroidPackages { + platformVersions = [ androidApiLevel ]; + includeNDK = true; + ndkVersion = "27.2.12479018"; + }; + androidNdkPackage = androidPackages.ndk-bundle; + # These paths reach into the internal layout of the nixpkgs androidenv derivation + # (there is no stable API for them). Pinned via flake.lock, but expect them to be + # the first thing to break on a `nix flake update`. + androidNdkHome = "${androidNdkPackage}/libexec/android-sdk/ndk-bundle"; + androidToolchainPlatform = if pkgs.stdenv.isDarwin then "darwin-x86_64" else "linux-x86_64"; + androidToolchainBin = "${androidNdkHome}/toolchains/llvm/prebuilt/${androidToolchainPlatform}/bin"; + + androidClang = "${androidToolchainBin}/aarch64-linux-android${androidApiLevel}-clang"; + androidClangxx = "${androidToolchainBin}/aarch64-linux-android${androidApiLevel}-clang++"; + androidI686Clang = "${androidToolchainBin}/i686-linux-android${androidApiLevel}-clang"; + androidI686Clangxx = "${androidToolchainBin}/i686-linux-android${androidApiLevel}-clang++"; + androidArmv7Clang = "${androidToolchainBin}/armv7a-linux-androideabi${androidApiLevel}-clang"; + androidArmv7Clangxx = "${androidToolchainBin}/armv7a-linux-androideabi${androidApiLevel}-clang++"; + androidX8664Clang = "${androidToolchainBin}/x86_64-linux-android${androidApiLevel}-clang"; + androidX8664Clangxx = "${androidToolchainBin}/x86_64-linux-android${androidApiLevel}-clang++"; + + rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ../rust-toolchain.toml; +in +pkgs.mkShell { + packages = [ + rustToolchain + androidNdkPackage + pkgs.curl + pkgs.git + pkgs.jdk17 + pkgs.nargo + ]; + + ANDROID_API_LEVEL = androidApiLevel; + ANDROID_NDK_HOME = androidNdkHome; + ANDROID_NDK_ROOT = androidNdkHome; + NDK_HOME = androidNdkHome; + + # Target rustflags live in .cargo/config.toml — a CARGO_TARGET_*_RUSTFLAGS + # env var here would replace them, not merge. + CC_aarch64_linux_android = androidClang; + CXX_aarch64_linux_android = androidClangxx; + AR_aarch64_linux_android = "${androidToolchainBin}/llvm-ar"; + RANLIB_aarch64_linux_android = "${androidToolchainBin}/llvm-ranlib"; + CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER = androidClang; + + CC_i686_linux_android = androidI686Clang; + CXX_i686_linux_android = androidI686Clangxx; + AR_i686_linux_android = "${androidToolchainBin}/llvm-ar"; + RANLIB_i686_linux_android = "${androidToolchainBin}/llvm-ranlib"; + CARGO_TARGET_I686_LINUX_ANDROID_LINKER = androidI686Clang; + + CC_armv7_linux_androideabi = androidArmv7Clang; + CXX_armv7_linux_androideabi = androidArmv7Clangxx; + AR_armv7_linux_androideabi = "${androidToolchainBin}/llvm-ar"; + RANLIB_armv7_linux_androideabi = "${androidToolchainBin}/llvm-ranlib"; + CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER = androidArmv7Clang; + + CC_x86_64_linux_android = androidX8664Clang; + CXX_x86_64_linux_android = androidX8664Clangxx; + AR_x86_64_linux_android = "${androidToolchainBin}/llvm-ar"; + RANLIB_x86_64_linux_android = "${androidToolchainBin}/llvm-ranlib"; + CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER = androidX8664Clang; + + shellHook = '' + echo "WalletKit Android dev shell" + echo " targets: aarch64-linux-android, armv7-linux-androideabi, i686-linux-android, x86_64-linux-android (API $ANDROID_API_LEVEL)" + echo " ndk: $ANDROID_NDK_HOME" + echo "" + echo "Build with: cargo build -p walletkit --release --target aarch64-linux-android --features compress-zkeys,embed-zkeys,v3" + ''; +} diff --git a/nix/build-android.sh b/nix/build-android.sh new file mode 100755 index 000000000..4bb027a79 --- /dev/null +++ b/nix/build-android.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +readonly TARGETS=( + "aarch64-linux-android" + "armv7-linux-androideabi" + "x86_64-linux-android" + "i686-linux-android" +) + +usage() { + cat < + +Targets: +$(printf ' %s\n' "${TARGETS[@]}") + +EOF +} + +TARGET="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --target | -t) + if [[ $# -lt 2 ]]; then + echo "error: --target requires a value" >&2 + usage >&2 + exit 1 + fi + TARGET="$2" + shift 2 + ;; + --help | -h) + usage + exit 0 + ;; + *) + echo "error: unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "${TARGET}" ]]; then + echo "error: --target is required" >&2 + usage >&2 + exit 1 +fi + +valid_target=false +for candidate in "${TARGETS[@]}"; do + if [[ "${TARGET}" == "${candidate}" ]]; then + valid_target=true + break + fi +done + +if [[ "${valid_target}" != true ]]; then + echo "error: unsupported target: ${TARGET}" >&2 + usage >&2 + exit 1 +fi + +# Defaults to the release feature set; CI overrides via WALLETKIT_CARGO_FEATURES. +exec nix develop .#android --command cargo build \ + -p walletkit \ + --release \ + --locked \ + --target "${TARGET}" \ + --features "${WALLETKIT_CARGO_FEATURES:-compress-zkeys,embed-zkeys,v3}" diff --git a/nix/build-wasm.sh b/nix/build-wasm.sh new file mode 100755 index 000000000..2a00ab7e3 --- /dev/null +++ b/nix/build-wasm.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +exec nix develop .#wasm --command cargo build \ + -p walletkit \ + --release \ + --locked \ + --target wasm32-unknown-unknown \ + "$@" diff --git a/nix/docker.sh b/nix/docker.sh new file mode 100755 index 000000000..02545e810 --- /dev/null +++ b/nix/docker.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run Nix commands in a Linux/amd64 Docker container for hosts without Nix. +# This mirrors the Nix CLI; only the container boundary is added. +# +# Usage: +# nix/docker.sh develop .#android +# nix/docker.sh develop .#android --command ./kotlin/build_kotlin.sh +# nix/docker.sh develop .#wasm --command cargo build --release --target wasm32-unknown-unknown +# nix/docker.sh flake show +# +# Swift builds are not provided by the flake because they depend on macOS and +# the host Xcode configuration; use the existing Swift build flow instead. + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +TTY_FLAGS=(-i) +if [[ -t 0 && -t 1 ]]; then + TTY_FLAGS=(-it) +fi + +# Always use linux/amd64 because the Android NDK has no aarch64-linux host +# toolchain. This is emulated on ARM hosts. Keep the Nix store and Cargo home +# architecture-specific so they cannot be contaminated by native ARM binaries. +# Nix's syscall filter cannot be installed under Rosetta emulation; Docker still +# provides the outer container sandbox. +# +# Note: the container runs as root, so on Linux hosts files created in target/ +# will be root-owned. +exec docker run --rm "${TTY_FLAGS[@]}" --platform linux/amd64 \ + --volume "$PWD:/src" \ + --volume walletkit-nix-store-amd64:/nix \ + --volume walletkit-cargo-home-amd64:/root/.cargo \ + --workdir /src \ + nixos/nix:2.34.8 \ + nix --extra-experimental-features 'nix-command flakes' \ + --option filter-syscalls false \ + "$@" diff --git a/nix/nargo.nix b/nix/nargo.nix new file mode 100644 index 000000000..c07c15260 --- /dev/null +++ b/nix/nargo.nix @@ -0,0 +1,56 @@ +# Noir is not packaged in nixpkgs, so we repackage the official prebuilt +# release binaries. The version must match what the provekit crates expect +# (see the provekit_* entries in Cargo.lock, versioned `-alpha.N`). +{ stdenv, lib, fetchurl, autoPatchelfHook }: + +let + artifacts = { + aarch64-darwin = { + target = "aarch64-apple-darwin"; + hash = "sha256-XAYTKQvnKPyzFKSAV4pg4KEc0ZLSSdCN7US+58XuoUI="; + }; + x86_64-darwin = { + target = "x86_64-apple-darwin"; + hash = "sha256-q/qd+hpYsQoQBt0uq/2xnrpWl99com4XB+qhsK36j/c="; + }; + x86_64-linux = { + target = "x86_64-unknown-linux-gnu"; + hash = "sha256-iaAU/TBYoFnRv6+ZzZiR65SKCT7DExSpzwRzSI9Ud5U="; + }; + aarch64-linux = { + target = "aarch64-unknown-linux-gnu"; + hash = "sha256-v25Eqc0dqMnERdIcaYtAZ4o9bveda3txjCSdZp0HCWY="; + }; + }; + artifact = + artifacts.${stdenv.hostPlatform.system} + or (throw "nargo: no prebuilt binary for ${stdenv.hostPlatform.system}"); +in stdenv.mkDerivation rec { + pname = "nargo"; + version = "1.0.0-beta.11"; + + src = fetchurl { + url = "https://github.com/noir-lang/noir/releases/download/v${version}/nargo-${artifact.target}.tar.gz"; + hash = artifact.hash; + }; + + sourceRoot = "."; + + nativeBuildInputs = lib.optionals stdenv.isLinux [ autoPatchelfHook ]; + buildInputs = lib.optionals stdenv.isLinux [ stdenv.cc.cc.lib ]; + + installPhase = '' + runHook preInstall + install -Dm755 nargo $out/bin/nargo + runHook postInstall + ''; + + meta = { + description = "Noir compiler and package manager"; + homepage = "https://noir-lang.org"; + license = with lib.licenses; [ mit asl20 ]; + sourceProvenance = [ lib.sourceTypes.binaryNativeCode ]; + platforms = lib.attrNames artifacts; + mainProgram = "nargo"; + }; +} diff --git a/nix/wasm.nix b/nix/wasm.nix new file mode 100644 index 000000000..e5d911b88 --- /dev/null +++ b/nix/wasm.nix @@ -0,0 +1,27 @@ +{ pkgs }: +let + rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ../rust-toolchain.toml; + llvm = pkgs.llvmPackages; +in +pkgs.mkShell { + packages = [ + rustToolchain + llvm.clang-unwrapped + llvm.bintools-unwrapped + pkgs.curl + pkgs.git + pkgs.nargo + ]; + + # Use unwrapped clang: cc-wrapper injects host hardening flags that are invalid for wasm. + CC_wasm32_unknown_unknown = "${llvm.clang-unwrapped}/bin/clang"; + AR_wasm32_unknown_unknown = "${llvm.bintools-unwrapped}/bin/llvm-ar"; + + shellHook = '' + echo "WalletKit wasm dev shell" + echo " target: wasm32-unknown-unknown" + echo " clang: $CC_wasm32_unknown_unknown" + echo "" + echo "Build with: cargo build -p walletkit --target wasm32-unknown-unknown" + ''; +}