Zig2nix: nix flake for packaging, building and running Zig projects

Hello I created flake for bridging zig to nix.

Features:

  • automatically updated zig compiler builds
  • .zon file conversion to .json format (zon2json binary, fromZON function)
  • build.zig.zon conversion to a lock file describing all zig package manager artifacts and hashes recursively (zon2json-lock binary)
  • lock file conversion to nix derivation (zon2nix binary, deriveLockFile function). Note the zon2nix binary is not the same as nix-community/zon2nix.
  • provides zig-env that can setup a common runtime for you (x11, wayland, opengl, vulkan) where programs dlopen these libraries
  • provides packageForTarget and package functions that fully integrate with build.zig.zon projects. By default they produce binaries compatible with nix environment, but can be used to build for foreign environments as well.
  • tool for dumping external dependencies of a zig project (system libraries, frameworks) nix run github:Cloudef/zig2nix#deps.master
  • zig2nix project templates
  • and maybe more …

Eventually I plan to merge/rewrite the GitHub - Cloudef/nix-zig-stdenv: cross-compile nixpkgs with zig into this project as well, allowing cross-compiling nixpkgs using zig.

A child project also exists called mach-flake, that flakes the Mach Engine https://machengine.org/.
GitHub - Cloudef/mach-flake: Flake that allows you to get started with Mach engine quickly.

13 Likes

Cross compiling and using nixpkgs from binary cache for target is now supported. All flake platforms are tested. At some point I may add support for cross-compiling nixpkgs using zig itself (tracking issue https://github.com/Cloudef/zig2nix/issues/4)

1 Like

I added ability to print external dependencies of a zig project (system libs and frameworks). Zig does not have separation of system/external and local for frameworks, so it may show frameworks that aren’t external. However it’s still good tool if nixing random zig projects.

Introducing experimental feature that allows you to (cross)compile nixpkgs using zig!
nix build github:Cloudef/zig2nix#zigCross.x86_64-windows-gnu.zlib

Note that compiling to macos has some problems right now Cross-compiling to macos or building inside nix sandbox fails to find headers from -isystem path · Issue #18571 · ziglang/zig · GitHub

Hello,
Thanks for this. My flake-foo is still bad, could you provide some sample of such a nix flake where SDL2 is available? I keep getting missing headers with @cImport("SDL2/SDL.h") having this flake:

{
  description = "Zig project flake (Sierpinski triangle with SDL2)";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=23.11";
    zig2nix.url = "github:Cloudef/zig2nix";
  };

  outputs = { zig2nix, nixpkgs, ... }: let
    flake-utils = zig2nix.inputs.flake-utils;
  in (flake-utils.lib.eachDefaultSystem (system: let
      # Zig flake helper
      # Check the flake.nix in zig2nix project for more options:
      # <https://github.com/Cloudef/zig2nix/blob/master/flake.nix>
    env = zig2nix.outputs.zig-env.${system} {
      customRuntimeLibs = [
        #nixpkgs.SDL2  # nixpkgs.SDL2 not available...
      ];
    };
    system-triple = env.lib.zigTripleFromString system;
    in with builtins; with env.lib; with env.pkgs.lib; rec {
      # nix build .#target.{zig-target}
      # e.g. nix build .#target.x86_64-linux-gnu
      packages.target = genAttrs allTargetTriples (target: env.packageForTarget target ({
        src = cleanSource ./.;

        nativeBuildInputs = with env.pkgs; [
          nixpkgs.SDL2
          nixpkgs.SDL2.dev
        ];
        buildInputs = with env.pkgsForTarget target; [
          nixpkgs.SDL2
          nixpkgs.SDL2.dev
        ];

        # Smaller binaries and avoids shipping glibc.
        zigPreferMusl = true;

        # This disables LD_LIBRARY_PATH mangling, binary patching etc...
        # The package won't be usable inside nix.
        zigDisableWrap = true;
      } // optionalAttrs (!pathExists ./build.zig.zon) {
        pname = "sierpinski";
        version = "0.0.1";
      }));

      # nix build .
      packages.default = packages.target.${system-triple}.override {
        # Prefer nix friendly settings.
        zigPreferMusl = false;
        zigDisableWrap = false;
      };

      # For bundling with nix bundle for running outside of nix
      # example: https://github.com/ralismark/nix-appimage
      apps.bundle.target = genAttrs allTargetTriples (target: let
        pkg = packages.target.${target};
      in {
        type = "app";
        program = "${pkg}/bin/default";
      });

      # default bundle
      apps.bundle.default = apps.bundle.target.${system-triple};

      # nix run .
      apps.default = env.app [] "zig build run -- \"$@\"";

      # nix run .#build
      apps.build = env.app [] "zig build \"$@\"";

      # nix run .#test
      apps.test = env.app [] "zig build test -- \"$@\"";

      # nix run .#docs
      apps.docs = env.app [] "zig build docs -- \"$@\"";

      # nix run .#deps
      apps.deps = env.showExternalDeps;

      # nix run .#zon2json
      apps.zon2json = env.app [env.zon2json] "zon2json \"$@\"";

      # nix run .#zon2json-lock
      apps.zon2json-lock = env.app [env.zon2json-lock] "zon2json-lock \"$@\"";

      # nix run .#zon2nix
      apps.zon2nix = env.app [env.zon2nix] "zon2nix \"$@\"";

      # nix develop
      devShells.default = env.mkShell {};
    }));
}

Hello, for packaging add pkg-config to nativeBuildInputs, you need the SDL deps only in buildInputs. Add pkg-config to customRuntimeDeps and SDL to customRuntimeLibs if you want zig build run etc to work in dev shell as well nix run .#build and friends … (general development, not packaging)

E.g. this is from one of my projects

zig-env = zig2nix.outputs.zig-env.${system} {
    zig = zig2nix.outputs.packages.${system}.zig.master.bin;
    customRuntimeDeps = with pkgs; [ pkg-config ];
    customRuntimeLibs = [ donutdb ];
};

FYI to get pkgs you need to do pkgs = nixpkgs.outputs.legacyPackages.${system};

Ah, OK thank you, this works now :slight_smile:

Inspired by GitHub Actions: mlugg/setup-zig
To have good caching on github actions for your zig2nix projects, you can use these 2 actions:

- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main

The first one installs nix, the second one caches the /nix/store.
The action automatically avoids caching anything that’s already cached in the official binary cache of nixpkgs.
All build.zig.zon dependencies will be cached as well if you use zig2nix’s package functions.

The caching can be seen in in action for example in the zig2nix repository itself:
https://github.com/Cloudef/zig2nix/actions/caches

One caveat is this currently caches even small outputs, which I’ve filled a issue:
https://github.com/DeterminateSystems/magic-nix-cache-action/issues/31
PR fixing it https://github.com/DeterminateSystems/magic-nix-cache/pull/69

2 Likes

Example of packaging ghostty using zig2nix

2 Likes

I am curious about whether or not this flake is useful for developing Zig projects as well, I know you mention building and running Zig code through this flake, and I will preface this question with the context that I haven’t yet tried to use this flake in a project of mine and/or read through the code so I am not sure yet of the exact scope and capabilities of it; although I do mean to check it out at some point and I was intrigued due to the tedium of my current process of writing a new flake based on a generic template that pulls in the Zig overlay flake (github:mitchellh/zig-overlay) for the nix-packaged compiler and also the zls flake, which I’ve forked to allow me to use any past version of zls instead of only the master version at the latest commit, and then tries to version match them so I have the right zls version for the specific compiler version I am using for a given project, and also manages the dependencies manually essentially. Which is all rather annoying despite working very well otherwise. I would love to have a more programmatic approach to fetching all the requisite packages and setting up the dev environment.

I guess then my question is this, can I use this flake to automatically fetch dependencies, manage the environments available Zig compiler and zls binary, and also provide a set of commands for building and running, and also maybe an output that provides that project as a Nix package as well?

I am excited to take a look tho at what you’ve made here, looks like good work!! :pink_heart:

shush this isn’t one giant run-on sentence wdymmmm nope couldn’t be me lol. :face_with_hand_over_mouth: