mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-21 14:29:41 +00:00
fix(amm): make flake portable across macOS and Linux
This commit is contained in:
parent
06054770ef
commit
1692581f60
173
apps/amm/MACOS.md
Normal file
173
apps/amm/MACOS.md
Normal file
@ -0,0 +1,173 @@
|
||||
# AMM UI on macOS
|
||||
|
||||
The AMM flake exposes native packages for Apple Silicon and Intel macOS. Nix
|
||||
provides the Rust, C++, Qt, and Apple SDK build dependencies. The only required
|
||||
host toolchain is Apple's Metal compiler, which Apple distributes through
|
||||
Xcode rather than through a redistributable package.
|
||||
|
||||
The standalone build needs no input overrides, temporary source pins, or
|
||||
`DYLD_LIBRARY_PATH`.
|
||||
|
||||
## One-time prerequisites
|
||||
|
||||
### Nix
|
||||
|
||||
Install Nix in multi-user mode, make sure its daemon is available, and enable
|
||||
flakes:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/.config/nix
|
||||
printf '%s\n' 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
|
||||
nix --version
|
||||
nix store info
|
||||
```
|
||||
|
||||
Nix normally has `sandbox = false` on macOS. This build must retain that
|
||||
setting because RISC Zero invokes Apple's host-installed Metal toolchain. The
|
||||
preflight script below reports an actionable error if the daemon is configured
|
||||
otherwise.
|
||||
|
||||
### Xcode and the Metal Toolchain
|
||||
|
||||
Apple Command Line Tools alone do not include `metal` and `metallib`. Install
|
||||
the newest full Xcode release supported by the installed macOS from the App
|
||||
Store or [Apple Developer Downloads](https://developer.apple.com/download/all/).
|
||||
This requires an Apple account and cannot be automated by the repository.
|
||||
|
||||
After installation:
|
||||
|
||||
```sh
|
||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||
sudo xcodebuild -license accept
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
xcodebuild -downloadComponent MetalToolchain
|
||||
xcrun --sdk macosx --find metal
|
||||
xcrun --sdk macosx --find metallib
|
||||
```
|
||||
|
||||
The last two commands must print executable paths. Do not use
|
||||
`RISC0_SKIP_BUILD_KERNELS=1`: the pinned dependency graph requires the real
|
||||
Metal kernels.
|
||||
|
||||
### Disk space and preflight
|
||||
|
||||
Leave at least 60 GiB free before downloading Xcode. Once Xcode is installed,
|
||||
keep at least 40 GiB free for the first Nix build. Later builds reuse the Nix
|
||||
store.
|
||||
|
||||
Run the complete machine check from `apps/amm`:
|
||||
|
||||
```sh
|
||||
bash scripts/check-macos-prerequisites.sh
|
||||
```
|
||||
|
||||
## Build and run
|
||||
|
||||
From `apps/amm`:
|
||||
|
||||
```sh
|
||||
nix build .
|
||||
nix run .
|
||||
```
|
||||
|
||||
The first cold build is large because it includes Qt, the wallet, the AMM
|
||||
client, and the RISC Zero Metal kernels. The first start opens without a
|
||||
wallet; select **Connect** to create or open `~/.lee/wallet/`.
|
||||
|
||||
## Why the flake is structured this way
|
||||
|
||||
Nix 2.25 rejects committed lock entries for unlocked relative flake inputs such
|
||||
as `path:../shared/wallet` and `path:../..`. Newer Nix versions may accept that
|
||||
layout, which is why it can appear to work on one developer's Mac and fail on
|
||||
another.
|
||||
|
||||
The portable layout avoids both relative flake inputs:
|
||||
|
||||
- the shared wallet is passed to CMake as an ordinary Nix source path;
|
||||
- the repository-root AMM client package is imported directly with the same
|
||||
locked `nixpkgs` and Crane inputs;
|
||||
- `flake.lock` contains only immutable upstream sources.
|
||||
|
||||
The app output uses the builder's full-library QML package layout. The current
|
||||
`qt-plugin` layout copies the UI plugin and replica factory but omits sibling
|
||||
external libraries such as `libamm_client`. The module metadata still selects
|
||||
the C++ backend, so this packaging choice does not change process isolation or
|
||||
runtime behavior.
|
||||
|
||||
Do not replace these with a pull-request URL, self-reference, or local
|
||||
`path:` flake input. A normal `nix flake lock`, `nix build`, and `nix run`
|
||||
must work without overrides.
|
||||
|
||||
## Known-good validation host
|
||||
|
||||
This configuration has been exercised on:
|
||||
|
||||
- Apple Silicon (`arm64`)
|
||||
- macOS 26.5.2
|
||||
- Xcode 26.6 (build 17F113)
|
||||
- Metal Toolchain 32023.883
|
||||
- Nix 2.25.3
|
||||
|
||||
The flake also evaluates its complete package and app outputs for
|
||||
`x86_64-darwin`; an Intel Mac is still required for native runtime validation.
|
||||
|
||||
## Recovery
|
||||
|
||||
### Nix is unavailable
|
||||
|
||||
Open a new terminal after installing Nix. For the standard multi-user
|
||||
installation, check the store and daemon before trying to bootstrap it again:
|
||||
|
||||
```sh
|
||||
test -x /nix/var/nix/profiles/default/bin/nix
|
||||
nix store info
|
||||
sudo launchctl print system/org.nixos.nix-daemon
|
||||
```
|
||||
|
||||
`launchctl bootstrap` can report an I/O error when a service is already loaded
|
||||
or in an inconsistent state; that message alone does not prove the daemon is
|
||||
stopped.
|
||||
|
||||
### `xcrun` cannot find `metal`
|
||||
|
||||
The selected developer directory points at Command Line Tools, or Xcode lacks
|
||||
the separately downloaded Metal Toolchain component. Repeat the Xcode setup
|
||||
and verification commands above.
|
||||
|
||||
Apple does not distribute the Metal compiler as a standalone redistributable
|
||||
SDK, so Nix cannot install this host component.
|
||||
|
||||
### The build reports that the macOS sandbox blocks Metal
|
||||
|
||||
Confirm the active setting:
|
||||
|
||||
```sh
|
||||
nix config show sandbox
|
||||
```
|
||||
|
||||
For this native macOS build it must be `false`. If a machine-wide policy set it
|
||||
to `true`, update that Nix installation's daemon configuration and restart the
|
||||
daemon before retrying.
|
||||
|
||||
### The first build runs out of space
|
||||
|
||||
Remove only disposable build outputs, then collect dead Nix store paths:
|
||||
|
||||
```sh
|
||||
rm -rf /private/tmp/amm-build
|
||||
nix-store --gc
|
||||
```
|
||||
|
||||
Do not remove `/nix/store` directly.
|
||||
|
||||
### A lock error mentions an unlocked input
|
||||
|
||||
Restore the committed app lock and verify it without rewriting it:
|
||||
|
||||
```sh
|
||||
git restore flake.lock
|
||||
nix flake metadata --no-update-lock-file .
|
||||
```
|
||||
|
||||
If the error still names `path:../shared/wallet` or `path:../..`, the checkout
|
||||
does not contain the portable flake fix described above.
|
||||
@ -50,6 +50,9 @@ nix profile install 'github:logos-co/logos-package-manager#cli'
|
||||
|
||||
This makes `lgpm` available as a global command.
|
||||
|
||||
For macOS prerequisites, the Metal toolchain requirement, and common Nix
|
||||
recovery steps, see [macOS setup](MACOS.md).
|
||||
|
||||
## Running the UI standalone
|
||||
|
||||
Start the UI with:
|
||||
@ -76,9 +79,11 @@ nix build '.#lgx' --out-link result-lgx
|
||||
# Portable variant (self-contained, works without nix)
|
||||
nix build '.#lgx-portable' --out-link result-lgx-portable
|
||||
|
||||
# The core wallet module it depends on. Use the same rev this app pins as the
|
||||
# `logos_execution_zone` input in flake.nix so the ImageID/ABI match.
|
||||
nix build 'github:logos-blockchain/logos-execution-zone-module?rev=d2e9400ac06c3cdbfc2405b4f153fff9841a453c#lgx' \
|
||||
# The core wallet module it depends on. These are the same immutable upstream
|
||||
# revisions used by this app's flake, including the merged macOS Metal fix.
|
||||
nix build 'github:logos-blockchain/logos-execution-zone-module?rev=d70225ced646934d2294fd9e8f8b03615c104b80#lgx' \
|
||||
--override-input logos-execution-zone \
|
||||
'github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05' \
|
||||
--out-link result-core
|
||||
```
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
alloy-primitives = { version = "1", default-features = false }
|
||||
amm_core = { workspace = true }
|
||||
borsh = { workspace = true }
|
||||
clock_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc6" }
|
||||
clock_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" }
|
||||
hex = "0.4"
|
||||
nssa_core = { workspace = true }
|
||||
risc0-zkvm = { workspace = true }
|
||||
|
||||
920
apps/amm/flake.lock
generated
920
apps/amm/flake.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,12 +3,8 @@
|
||||
|
||||
inputs = {
|
||||
logos-module-builder.url = "github:logos-co/logos-module-builder";
|
||||
|
||||
# Shared C++ wallet access and Logos.Wallet QML sources.
|
||||
shared_wallet = {
|
||||
url = "path:../shared/wallet";
|
||||
flake = false;
|
||||
};
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11-small";
|
||||
crane.url = "github:ipetkov/crane/v0.23.4";
|
||||
|
||||
# Core wallet module (the LEZ wallet FFI Qt plugin). The input name must
|
||||
# match the metadata.json `dependencies` entry so the builder can resolve
|
||||
@ -17,47 +13,67 @@
|
||||
logos_execution_zone = {
|
||||
url = "github:logos-blockchain/logos-execution-zone-module?rev=d70225ced646934d2294fd9e8f8b03615c104b80";
|
||||
|
||||
# The module pins the monorepo at v0.2.0-rc6 (e37876a), which owns the
|
||||
# xcrun wrapper in its flake.nix. Override that transitive input to the
|
||||
# head of PR #629 (fix(macos): nuke xcrun cache) so the Metal/xcrun build
|
||||
# works on macOS. Note: #629 branches off `dev`, so this also pulls dev's
|
||||
# drift from rc6 — if the wallet_ffi ABI mismatches the module build, fall
|
||||
# back to cherry-picking #629's one line onto e37876a and pin that commit.
|
||||
# The module pins the monorepo at v0.2.0-rc6 (e37876a). Override that
|
||||
# transitive input with the merged macOS xcrun-cache fix.
|
||||
inputs.logos-execution-zone.url =
|
||||
"github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05";
|
||||
};
|
||||
|
||||
amm_client.url = "path:../..";
|
||||
};
|
||||
|
||||
outputs = inputs@{ logos-module-builder, shared_wallet, ... }:
|
||||
logos-module-builder.lib.mkLogosQmlModule {
|
||||
src = ./.;
|
||||
configFile = ./metadata.json;
|
||||
flakeInputs = inputs;
|
||||
preConfigure = ''
|
||||
cmakeFlagsArray+=("-DLOGOS_WALLET_SOURCE_DIR=${shared_wallet}")
|
||||
'';
|
||||
externalLibInputs = {
|
||||
amm_client = {
|
||||
input = inputs.amm_client;
|
||||
packages.default = "amm_client";
|
||||
};
|
||||
outputs = inputs@{ logos-module-builder, nixpkgs, crane, ... }:
|
||||
let
|
||||
ammClientInput = (import ../../flake.nix).outputs {
|
||||
inherit nixpkgs crane;
|
||||
};
|
||||
postInstall = ''
|
||||
# The builder installs the view under lib/qml after this hook. Its
|
||||
# import descriptor points back to this compiled shared QML module.
|
||||
test -f ${./qml}/Logos/Wallet/qmldir
|
||||
moduleBuild = logos-module-builder.lib.mkLogosQmlModule {
|
||||
src = ./.;
|
||||
configFile = ./metadata.json;
|
||||
flakeInputs = inputs;
|
||||
preConfigure = ''
|
||||
cmakeFlagsArray+=("-DLOGOS_WALLET_SOURCE_DIR=${../shared/wallet}")
|
||||
'';
|
||||
externalLibInputs = {
|
||||
amm_client = ammClientInput;
|
||||
};
|
||||
postInstall = ''
|
||||
# The builder installs the view under lib/qml after this hook. Its
|
||||
# import descriptor points back to this compiled shared QML module.
|
||||
test -f ${./qml}/Logos/Wallet/qmldir
|
||||
|
||||
walletQmlDir="shared-wallet/qml/Logos/Wallet"
|
||||
if [ ! -d "$walletQmlDir" ]; then
|
||||
echo "Built Logos.Wallet QML module not found"
|
||||
exit 1
|
||||
fi
|
||||
walletQmlInstallDir="$out/lib/Logos/Wallet"
|
||||
mkdir -p "$walletQmlInstallDir"
|
||||
cp -r "$walletQmlDir/." "$walletQmlInstallDir/"
|
||||
test -f "$walletQmlInstallDir/qmldir"
|
||||
'';
|
||||
walletQmlDir="shared-wallet/qml/Logos/Wallet"
|
||||
if [ ! -d "$walletQmlDir" ]; then
|
||||
echo "Built Logos.Wallet QML module not found"
|
||||
exit 1
|
||||
fi
|
||||
walletQmlInstallDir="$out/lib/Logos/Wallet"
|
||||
mkdir -p "$walletQmlInstallDir"
|
||||
cp -r "$walletQmlDir/." "$walletQmlInstallDir/"
|
||||
test -f "$walletQmlInstallDir/qmldir"
|
||||
'';
|
||||
};
|
||||
|
||||
publicApps = logos-module-builder.lib.common.forAllSystems nixpkgs ({ system, pkgs }:
|
||||
{
|
||||
default = logos-module-builder.lib.mkStandaloneApp {
|
||||
inherit pkgs;
|
||||
standalone =
|
||||
logos-module-builder.inputs.logos-standalone-app.packages.${system}.default;
|
||||
plugin = moduleBuild.packages.${system}.default;
|
||||
metadataFile = ./metadata.json;
|
||||
|
||||
# The qt-plugin layout only copies the plugin and replica factory.
|
||||
# Keep the complete lib tree so sibling external libraries such as
|
||||
# libamm_client remain available through the plugin's loader path.
|
||||
format = "qml";
|
||||
|
||||
moduleDeps =
|
||||
logos-module-builder.lib.common.collectAllModuleDeps
|
||||
system inputs moduleBuild.config.dependencies;
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
moduleBuild // {
|
||||
apps = publicApps;
|
||||
};
|
||||
}
|
||||
|
||||
117
apps/amm/scripts/check-macos-prerequisites.sh
Executable file
117
apps/amm/scripts/check-macos-prerequisites.sh
Executable file
@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Verify the host requirements for a supported native macOS AMM build.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
failures=0
|
||||
|
||||
pass() {
|
||||
printf 'ok: %s\n' "$1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf 'missing: %s\n' "$1" >&2
|
||||
failures=$((failures + 1))
|
||||
}
|
||||
|
||||
if [[ "$(uname -s)" != "Darwin" ]]; then
|
||||
printf 'missing: this script must run on macOS\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
architecture="$(uname -m)"
|
||||
case "$architecture" in
|
||||
arm64|x86_64)
|
||||
pass "supported architecture $architecture"
|
||||
;;
|
||||
*)
|
||||
fail "unsupported architecture $architecture"
|
||||
;;
|
||||
esac
|
||||
|
||||
developer_dir="$(xcode-select -p 2>/dev/null || true)"
|
||||
if [[ "$developer_dir" == *.app/Contents/Developer ]]; then
|
||||
pass "full Xcode selected at $developer_dir"
|
||||
required_gib=40
|
||||
else
|
||||
fail "full Xcode is not selected (current developer directory: ${developer_dir:-none})"
|
||||
required_gib=60
|
||||
fi
|
||||
|
||||
if xcode_version="$(xcodebuild -version 2>/dev/null)"; then
|
||||
pass "$(printf '%s' "$xcode_version" | tr '\n' ' ')"
|
||||
else
|
||||
fail "xcodebuild is unavailable; install Xcode.app and select it with xcode-select"
|
||||
fi
|
||||
|
||||
if xcodebuild -checkFirstLaunchStatus >/dev/null 2>&1; then
|
||||
pass "Xcode first-launch setup and licence are complete"
|
||||
else
|
||||
fail "Xcode setup is incomplete; run sudo xcodebuild -license accept and -runFirstLaunch"
|
||||
fi
|
||||
|
||||
for tool in metal metallib; do
|
||||
if tool_path="$(xcrun --sdk macosx --find "$tool" 2>/dev/null)" &&
|
||||
[[ -x "$tool_path" ]]; then
|
||||
pass "$tool at $tool_path"
|
||||
else
|
||||
fail "$tool is unavailable; run xcodebuild -downloadComponent MetalToolchain"
|
||||
fi
|
||||
done
|
||||
|
||||
nix_bin="$(command -v nix 2>/dev/null || true)"
|
||||
if [[ -z "$nix_bin" && -x /nix/var/nix/profiles/default/bin/nix ]]; then
|
||||
nix_bin=/nix/var/nix/profiles/default/bin/nix
|
||||
fi
|
||||
|
||||
if [[ -n "$nix_bin" ]]; then
|
||||
pass "Nix at $nix_bin ($("$nix_bin" --version))"
|
||||
|
||||
configured_features="$(
|
||||
"$nix_bin" config show experimental-features 2>/dev/null ||
|
||||
"$nix_bin" show-config 2>/dev/null |
|
||||
awk -F ' = ' '$1 == "experimental-features" { print $2 }'
|
||||
)"
|
||||
if [[ " $configured_features " == *" nix-command "* &&
|
||||
" $configured_features " == *" flakes "* ]]; then
|
||||
pass "Nix flakes and nix-command are enabled"
|
||||
else
|
||||
fail "enable experimental-features = nix-command flakes in nix.conf"
|
||||
fi
|
||||
|
||||
if "$nix_bin" --extra-experimental-features 'nix-command flakes' \
|
||||
store info >/dev/null 2>&1; then
|
||||
pass "Nix daemon and store are available"
|
||||
else
|
||||
fail "Nix cannot contact its daemon or store"
|
||||
fi
|
||||
|
||||
sandbox_setting="$("$nix_bin" config show sandbox 2>/dev/null || true)"
|
||||
if [[ "$sandbox_setting" == "false" ]]; then
|
||||
pass "Nix macOS sandbox is disabled for the host Metal toolchain"
|
||||
else
|
||||
fail "Nix sandbox must be false for the host Metal toolchain (current: ${sandbox_setting:-unknown})"
|
||||
fi
|
||||
else
|
||||
fail "Nix is unavailable; install multi-user Nix and start its daemon"
|
||||
fi
|
||||
|
||||
if [[ -d /nix ]]; then
|
||||
available_kib="$(df -Pk /nix | awk 'NR == 2 { print $4 }')"
|
||||
required_kib=$((required_gib * 1024 * 1024))
|
||||
if [[ "${available_kib:-0}" -ge "$required_kib" ]]; then
|
||||
pass "at least ${required_gib} GiB free on the Nix volume"
|
||||
else
|
||||
fail "less than ${required_gib} GiB free on the Nix volume"
|
||||
fi
|
||||
else
|
||||
fail "/nix is not mounted"
|
||||
fi
|
||||
|
||||
if (( failures > 0 )); then
|
||||
printf '\nSee MACOS.md for installation and recovery instructions.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '\nAll macOS AMM prerequisites are available.\n'
|
||||
Loading…
x
Reference in New Issue
Block a user