build: macOS support for the Nix flake

Add aarch64-darwin to the flake systems so packages, apps and both
devShells are exposed on Apple Silicon Macs. The Wayland/X11/EGL
runtime libraries, the /run/opengl-driver search paths, the freedesktop
packaging and the wrapProgram post-fixup are Linux-only and stay that
way: on macOS winit talks to AppKit, wgpu renders through Metal, and
the GPU drivers ship with the OS, so the darwin package needs no
wrapper. x86_64-darwin is deliberately not added: current nixpkgs
unstable no longer evaluates rustc for that platform (26.11
deprecation), so the package would fail on toolchain evaluation.

docs/nix.md: platform list updated, macOS paragraph in the graphics
section, desktop-entry paragraph qualified as Linux-only.
This commit is contained in:
Levon Kirakosyan authored and bherbruck committed 2026-10-12 10:06:03 -04:00
1 parent 874315db8e
commit c308a1ba7a
2 files changed
+63 -45

No files matched your search

+8 -3
View File
@@ -1,6 +1,6 @@
# SolidCraft with Nix
The flake supports `x86_64-linux` and `aarch64-linux`. Enable Nix's `nix-command`
The flake supports `x86_64-linux`, `aarch64-linux` and `aarch64-darwin`. Enable Nix's `nix-command`
and `flakes` experimental features. The committed `flake.lock` pins nixpkgs and
its Rust toolchain; package evaluation checks that compiler against the Rust
floor in `Cargo.toml`.
@@ -35,8 +35,9 @@ nix profile install github:bherbruck/solidcraft#solidcraft
solidcraft --sample
```
This installs the SolidCraft desktop entry, MIME definition and generated icon
sizes alongside the commands. NixOS/Home Manager can instead put the flake's
On Linux, this installs the SolidCraft desktop entry, MIME definition and generated icon
sizes alongside the commands. On macOS the package contains the binaries only; for a bundled
`SolveCraft.app`, use the release `.dmg`. NixOS/Home Manager can instead put the flake's
`packages.${system}.solidcraft` in `environment.systemPackages`/`home.packages`.
If a non-NixOS desktop does not discover profile-installed launchers, ensure that
its session includes `~/.nix-profile/share` in `XDG_DATA_DIRS`, then log out and
@@ -64,6 +65,10 @@ discoverable. Enable `hardware.graphics.enable = true` on NixOS and keep the
system's driver stack consistent with the compositor. File pickers also need a
working session D-Bus and the desktop's `xdg-desktop-portal` service.
On macOS (`aarch64-darwin`), none of this applies: winit talks to AppKit, wgpu renders through
Metal, and the GPU drivers ship with the operating system, so the darwin package carries no
library wrapper and no extra search paths.
On another distribution, graphics drivers outside the Nix store may need a
matching [nixGL wrapper](https://github.com/nix-community/nixGL). Follow its
GPU-specific instructions; a wrapper for a different driver is not a substitute.
+55 -42
View File
@@ -1,5 +1,5 @@
{
description = "SolidCraft — native app development shell and package for Linux";
description = "SolidCraft — native app development shell and package for Linux and macOS";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@@ -9,10 +9,11 @@
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
# Why this shell exists. winit opens the window by dlopen'ing the Wayland client libraries,
# Linux only. Why this shell exists. winit opens the window by dlopen'ing the Wayland client libraries,
# wgpu renders through the EGL loader (libEGL.so.1), and rfd's file dialogs reach
# xdg-desktop-portal through libdbus-1; a plain `cargo run` under NixOS finds none of them,
# because there is no /usr/lib and nothing puts them on LD_LIBRARY_PATH:
@@ -29,34 +30,44 @@
# `xvfb-run` on a machine with no display at all.
runtimeLibs =
pkgs:
(with pkgs; [
wayland
libxkbcommon
libglvnd
vulkan-loader
dbus
])
++ (with pkgs; [
# xorg.libX11 and friends are deprecated aliases in current nixpkgs.
libx11
libxcursor
libxi
libxrandr
libxrender
libxext
libxcb
]);
driverLibDir = "/run/opengl-driver/lib";
driverShareDir = "/run/opengl-driver/share";
libraryPath = pkgs: "${pkgs.lib.makeLibraryPath (runtimeLibs pkgs)}:${driverLibDir}";
pkgs.lib.optionals pkgs.stdenv.isLinux (
(with pkgs; [
wayland
libxkbcommon
libglvnd
vulkan-loader
dbus
])
++ (with pkgs; [
# xorg.libX11 and friends are deprecated aliases in current nixpkgs.
libx11
libxcursor
libxi
libxrandr
libxrender
libxext
libxcb
])
);
# macOS needs none of the above: winit talks to AppKit, wgpu renders through Metal, and the
# OS ships its GPU drivers, so the darwin package and shells skip the wrapper and paths.
driverLibDir = pkgs: if pkgs.stdenv.isLinux then "/run/opengl-driver/lib" else "";
driverShareDir = pkgs: if pkgs.stdenv.isLinux then "/run/opengl-driver/share" else "";
libraryPath =
pkgs:
if pkgs.stdenv.isLinux then "${pkgs.lib.makeLibraryPath (runtimeLibs pkgs)}:${driverLibDir pkgs}" else "";
shellHook = ''
if [ ! -d ${driverLibDir} ]; then
echo "SolidCraft: ${driverLibDir} is missing — on NixOS enable hardware.graphics; on other distributions use a matching nixGL driver wrapper." >&2
fi
shellHook = pkgs: ''
${pkgs.lib.optionalString pkgs.stdenv.isLinux ''
if [ ! -d ${driverLibDir pkgs} ]; then
echo "SolidCraft: ${driverLibDir pkgs} is missing — on NixOS enable hardware.graphics; on other distributions use a matching nixGL driver wrapper." >&2
fi
''}
command -v cc >/dev/null || echo "solvecraft: no C linker on PATH, cargo cannot link — use nix develop .#rust" >&2
echo "solvecraft: cargo run --release -p solvecraft -- --sample"
export XDG_DATA_DIRS="${driverShareDir}:''${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
${pkgs.lib.optionalString pkgs.stdenv.isLinux ''
export XDG_DATA_DIRS="${driverShareDir pkgs}:''${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
''}
'';
in
{
@@ -68,7 +79,7 @@
default = pkgs.mkShellNoCC {
name = "solidcraft";
LD_LIBRARY_PATH = libraryPath pkgs;
inherit shellHook;
shellHook = shellHook pkgs;
};
# For a machine with no Rust at all: `nix develop .#rust`. Its own target directory, so
@@ -85,7 +96,7 @@
# Under target/, which .gitignore already covers.
CARGO_TARGET_DIR = "target/nix";
shellHook = ''
${shellHook}
${shellHook pkgs}
echo "solvecraft: $(rustc --version), building into target/nix"
'';
};
@@ -123,25 +134,27 @@
buildInputs = runtimeLibs pkgs;
postInstall = ''
install -Dm644 packaging/linux/ai.storyteller.solvecraft.desktop \
$out/share/applications/ai.storyteller.solvecraft.desktop
install -Dm644 packaging/linux/ai.storyteller.solvecraft.mime.xml \
$out/share/mime/packages/ai.storyteller.solvecraft.xml
for size in 16 24 32 48 64 128 256 512; do
icon=$out/share/icons/hicolor/''${size}x''${size}/apps/ai.storyteller.solvecraft.png
mkdir -p "$(dirname "$icon")"
$out/bin/solvecraft-cli icon --size "$size" --out "$icon" >/dev/null
done
ln -s solvecraft $out/bin/solidcraft
${pkgs.lib.optionalString pkgs.stdenv.isLinux ''
install -Dm644 packaging/linux/ai.storyteller.solvecraft.desktop \
$out/share/applications/ai.storyteller.solvecraft.desktop
install -Dm644 packaging/linux/ai.storyteller.solvecraft.mime.xml \
$out/share/mime/packages/ai.storyteller.solvecraft.xml
for size in 16 24 32 48 64 128 256 512; do
icon=$out/share/icons/hicolor/''${size}x''${size}/apps/ai.storyteller.solvecraft.png
mkdir -p "$(dirname "$icon")"
$out/bin/solvecraft-cli icon --size "$size" --out "$icon" >/dev/null
done
''}
'';
# The app reaches the window through dlopen'd libraries, so the packaged binary needs the
# same LD_LIBRARY_PATH the shell exports.
postFixup = ''
# Linux only: the app reaches the window through dlopen'd libraries, so the packaged
# binary needs the same LD_LIBRARY_PATH the shell exports. macOS needs no wrapper.
postFixup = pkgs.lib.optionalString pkgs.stdenv.isLinux ''
wrapProgram $out/bin/solvecraft \
--prefix LD_LIBRARY_PATH : "${libraryPath pkgs}" \
--set-default XDG_DATA_DIRS /usr/local/share:/usr/share \
--prefix XDG_DATA_DIRS : "$out/share:${driverShareDir}"
--prefix XDG_DATA_DIRS : "$out/share:${driverShareDir pkgs}"
'';
meta = {