mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-22 14:59:38 +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.
|
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
|
## Running the UI standalone
|
||||||
|
|
||||||
Start the UI with:
|
Start the UI with:
|
||||||
@ -76,9 +79,11 @@ nix build '.#lgx' --out-link result-lgx
|
|||||||
# Portable variant (self-contained, works without nix)
|
# Portable variant (self-contained, works without nix)
|
||||||
nix build '.#lgx-portable' --out-link result-lgx-portable
|
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
|
# The core wallet module it depends on. These are the same immutable upstream
|
||||||
# `logos_execution_zone` input in flake.nix so the ImageID/ABI match.
|
# revisions used by this app's flake, including the merged macOS Metal fix.
|
||||||
nix build 'github:logos-blockchain/logos-execution-zone-module?rev=d2e9400ac06c3cdbfc2405b4f153fff9841a453c#lgx' \
|
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
|
--out-link result-core
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
alloy-primitives = { version = "1", default-features = false }
|
alloy-primitives = { version = "1", default-features = false }
|
||||||
amm_core = { workspace = true }
|
amm_core = { workspace = true }
|
||||||
borsh = { 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"
|
hex = "0.4"
|
||||||
nssa_core = { workspace = true }
|
nssa_core = { workspace = true }
|
||||||
risc0-zkvm = { 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 = {
|
inputs = {
|
||||||
logos-module-builder.url = "github:logos-co/logos-module-builder";
|
logos-module-builder.url = "github:logos-co/logos-module-builder";
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11-small";
|
||||||
# Shared C++ wallet access and Logos.Wallet QML sources.
|
crane.url = "github:ipetkov/crane/v0.23.4";
|
||||||
shared_wallet = {
|
|
||||||
url = "path:../shared/wallet";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Core wallet module (the LEZ wallet FFI Qt plugin). The input name must
|
# Core wallet module (the LEZ wallet FFI Qt plugin). The input name must
|
||||||
# match the metadata.json `dependencies` entry so the builder can resolve
|
# match the metadata.json `dependencies` entry so the builder can resolve
|
||||||
@ -17,47 +13,67 @@
|
|||||||
logos_execution_zone = {
|
logos_execution_zone = {
|
||||||
url = "github:logos-blockchain/logos-execution-zone-module?rev=d70225ced646934d2294fd9e8f8b03615c104b80";
|
url = "github:logos-blockchain/logos-execution-zone-module?rev=d70225ced646934d2294fd9e8f8b03615c104b80";
|
||||||
|
|
||||||
# The module pins the monorepo at v0.2.0-rc6 (e37876a), which owns the
|
# The module pins the monorepo at v0.2.0-rc6 (e37876a). Override that
|
||||||
# xcrun wrapper in its flake.nix. Override that transitive input to the
|
# transitive input with the merged macOS xcrun-cache fix.
|
||||||
# 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.
|
|
||||||
inputs.logos-execution-zone.url =
|
inputs.logos-execution-zone.url =
|
||||||
"github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05";
|
"github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05";
|
||||||
};
|
};
|
||||||
|
|
||||||
amm_client.url = "path:../..";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs@{ logos-module-builder, shared_wallet, ... }:
|
outputs = inputs@{ logos-module-builder, nixpkgs, crane, ... }:
|
||||||
logos-module-builder.lib.mkLogosQmlModule {
|
let
|
||||||
src = ./.;
|
ammClientInput = (import ../../flake.nix).outputs {
|
||||||
configFile = ./metadata.json;
|
inherit nixpkgs crane;
|
||||||
flakeInputs = inputs;
|
|
||||||
preConfigure = ''
|
|
||||||
cmakeFlagsArray+=("-DLOGOS_WALLET_SOURCE_DIR=${shared_wallet}")
|
|
||||||
'';
|
|
||||||
externalLibInputs = {
|
|
||||||
amm_client = {
|
|
||||||
input = inputs.amm_client;
|
|
||||||
packages.default = "amm_client";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
postInstall = ''
|
moduleBuild = logos-module-builder.lib.mkLogosQmlModule {
|
||||||
# The builder installs the view under lib/qml after this hook. Its
|
src = ./.;
|
||||||
# import descriptor points back to this compiled shared QML module.
|
configFile = ./metadata.json;
|
||||||
test -f ${./qml}/Logos/Wallet/qmldir
|
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"
|
walletQmlDir="shared-wallet/qml/Logos/Wallet"
|
||||||
if [ ! -d "$walletQmlDir" ]; then
|
if [ ! -d "$walletQmlDir" ]; then
|
||||||
echo "Built Logos.Wallet QML module not found"
|
echo "Built Logos.Wallet QML module not found"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
walletQmlInstallDir="$out/lib/Logos/Wallet"
|
walletQmlInstallDir="$out/lib/Logos/Wallet"
|
||||||
mkdir -p "$walletQmlInstallDir"
|
mkdir -p "$walletQmlInstallDir"
|
||||||
cp -r "$walletQmlDir/." "$walletQmlInstallDir/"
|
cp -r "$walletQmlDir/." "$walletQmlInstallDir/"
|
||||||
test -f "$walletQmlInstallDir/qmldir"
|
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