diff --git a/docs/nix.md b/docs/nix.md index 2db47bba..bca4dfd7 100644 --- a/docs/nix.md +++ b/docs/nix.md @@ -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. diff --git a/flake.nix b/flake.nix index 3c73e857..bf3e5fea 100644 --- a/flake.nix +++ b/flake.nix @@ -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 = {