From 8f67c4c4b639c61c1bc2c141f4ce6b042ddabbc9 Mon Sep 17 00:00:00 2001 From: Andrea Franz Date: Thu, 16 Jul 2026 14:14:20 +0200 Subject: [PATCH] build(amm): load amm_client_ffi via absolute store-path id + DYLD fallback on macOS --- flake.nix | 28 ++++++++++++++++++++++++++-- programs/amm/client-ffi/Cargo.toml | 7 +++++++ programs/amm/client-ffi/build.rs | 2 ++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 33adb78..b0a9e2d 100644 --- a/flake.nix +++ b/flake.nix @@ -71,8 +71,14 @@ cp programs/amm/client-ffi/amm_client_ffi.h $out/include/ '' + pkgs.lib.optionalString pkgs.stdenv.isDarwin '' + # Set the dylib's install-name to its ABSOLUTE store path (NOT + # @rpath): the logos module builder links this lib into the plugin + # but never stages it into the plugin's runtime rpath, so an + # @rpath id fails to dlopen at launch. An absolute /nix/store id + # is recorded in the plugin's LC_LOAD_DYLIB, kept in the closure + # by Nix, and resolved directly at runtime — no rpath needed. if [ -f $out/lib/libamm_client_ffi.dylib ]; then - install_name_tool -id @rpath/libamm_client_ffi.dylib $out/lib/libamm_client_ffi.dylib + install_name_tool -id "$out/lib/libamm_client_ffi.dylib" $out/lib/libamm_client_ffi.dylib fi ''; } @@ -108,9 +114,27 @@ appApps = appOutputs.apps or { }; appPkgs = appOutputs.packages or { }; + # Wrap the app launcher to export DYLD_FALLBACK_LIBRARY_PATH pointing at the + # amm_client_ffi lib. The logos module builder links the plugin against + # @rpath/libamm_client_ffi.dylib but does NOT stage that dylib into the + # plugin-dir it loads at runtime, so dlopen fails with "Failed to load UI + # plugin". Adding the crate's store lib dir to DYLD's fallback search path + # lets the loader find it (the store path stays in the closure). + wrapWithDyld = system: app: + let + pkgs = import nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; }; + ammFfi = crateOutputs.packages.${system}.amm_client_ffi; + in + app // { + program = "${pkgs.writeShellScript "run-amm-ui" '' + export DYLD_FALLBACK_LIBRARY_PATH="${ammFfi}/lib''${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}" + exec ${app.program} "$@" + ''}"; + }; + renamedApps = builtins.mapAttrs ( system: attrs: - (builtins.removeAttrs attrs [ "default" ]) // (if attrs ? default then { amm-ui = attrs.default; } else { }) + (builtins.removeAttrs attrs [ "default" ]) // (if attrs ? default then { amm-ui = wrapWithDyld system attrs.default; } else { }) ) appApps; mergedPackages = builtins.mapAttrs ( diff --git a/programs/amm/client-ffi/Cargo.toml b/programs/amm/client-ffi/Cargo.toml index 8211b87..5015f5e 100644 --- a/programs/amm/client-ffi/Cargo.toml +++ b/programs/amm/client-ffi/Cargo.toml @@ -1,10 +1,17 @@ [package] name = "amm_client_ffi" version = "0.1.0" +# 2021 (not the repo's 2024): matches amm_core and keeps plain #[no_mangle] +# (edition 2024 requires #[unsafe(no_mangle)]). edition = "2021" [lib] name = "amm_client_ffi" +# The logos module builder's macOS find_library only locates SHARED libs +# (lib.dylib), never a static .a — so we must ship the cdylib. The flake +# gives the dylib an ABSOLUTE store-path install-name (not @rpath), so the plugin +# links it by its /nix/store path (kept in the closure) and dlopen resolves it at +# runtime without any rpath staging. staticlib/rlib kept for other consumers/tests. crate-type = ["staticlib", "cdylib", "rlib"] [lints] diff --git a/programs/amm/client-ffi/build.rs b/programs/amm/client-ffi/build.rs index 32946d2..5c7aa76 100644 --- a/programs/amm/client-ffi/build.rs +++ b/programs/amm/client-ffi/build.rs @@ -5,4 +5,6 @@ fn main() { .expect("cbindgen") .write_to_file(format!("{crate_dir}/amm_client_ffi.h")); println!("cargo:rerun-if-changed=src/lib.rs"); + println!("cargo:rerun-if-changed=src"); + println!("cargo:rerun-if-changed=cbindgen.toml"); }