Build and use the latest Zig using Nix

Hi everyone!

This weekend I started having a look at Zig. It looks very interesting. I like to do my best to keep my global environment as clean as possible and as part of this I like to use Nix.

There’s a Nix package on the global nixpkgs, but that points to version 0.9.1, which is a bit old. Furthermore, the home page of the language itself suggests to use the latest unstable version if you want to dig deep (which I more or less intend to do :smile: ). As such I used the publicly available “derivation” (that’s how package definitions are referred to in Nix) as a reference and wrote a few lines to start a Nix shell that includes a locally available Zig installation freshly fetched from Github and built from source:

with import (fetchTarball {
  url = "https://github.com/nixos/nixpkgs/tarball/b139b6056c8ad4ef7e0cffb81304d59cf077589b";
  sha256 = "0sn9l19ckvdh227n0rfxk1cjnslhb5hr3g8czf3a436zkyfdl3if";
}) {};

let
  zig = stdenv.mkDerivation rec {

    name = "zig";

    src = fetchFromGitHub {
      owner = "ziglang";
      repo = "zig";
      rev = "476bdc8b0b02cbd09f6a856aa7dc548dea565109";
      hash = "sha256-VdMNzvoWNGvVFiblE7vajOetmHa0hyUWw5tWWVZjKEs=";
    };

    nativeBuildInputs = [
      cmake
      llvmPackages_15.llvm.dev
    ];

    buildInputs = [
      libxml2
      zlib
    ] ++ (with llvmPackages_15; [
      libclang
      lld
      llvm
    ]);

    preBuild = ''
      export HOME=$TMPDIR;
    '';

    doCheck = false;
  };
in
mkShell {
  nativeBuildInputs = [
    zig
  ];
}

I took the latest available commit, but that can be adjusted based on your needs. I’ll bump it frequently myself to make sure to keep up with the latest changes. You can see it used in the playground I’m setting up to play around with Zig.

I was thinking that probably some documentation additional guidance for Nix users on how to build the latest Zig from source could be some nice contribution, do you think it would make sense?

2 Likes

I realized I was implicitly depending on nixpkgs-unstable and pinned the dependency, now this should work regardless. While this worked on my MacBook, I tried to use this from my Linux laptop and it ended up producing a zig binary depending on a misplaced dynamic linker, causing a “No such file or directory” error. I used patchelf to fix it by hand but that’s of course not something I’d like to happen. I’ll try to understand what the error might be and perhaps ask help here on the forum if I cannot find a solution. :smiley:

I found the problem and shared it here on StackOverflow as well. In a nutshell, during the build Zig asks for the linker to /usr/bin/env but that doesn’t work in the Nix sandbox (I’m more or less paraphrasing the Nix derivation for the unstable channel, which I just realized wasn’t used on my MacBook because the derivation for 0.10.1 doesn’t work there, leading to falling back to 0.9.1). To solve it, you can import coreutils as a build dependency and use env from there. The following patch got the job done on my Linux laptop (I’ll test it later on my MacBook):

diff --git a/shell.nix b/shell.nix
index f554c8d..bb466de 100644
--- a/shell.nix
+++ b/shell.nix
@@ -21,6 +21,7 @@ let
     ];
 
     buildInputs = [
+      coreutils
       libxml2
       zlib
     ] ++ (with llvmPackages_15; [
@@ -33,7 +34,25 @@ let
       export HOME=$TMPDIR;
     '';
 
-    doCheck = false;
+    postPatch = ''
+      # Zig's build looks at /usr/bin/env to find dynamic linking info. This
+      # doesn't work in Nix' sandbox. Use env from our coreutils instead.
+      substituteInPlace lib/std/zig/system/NativeTargetInfo.zig --replace "/usr/bin/env" "${coreutils}/bin/env"
+    '';
+
+    cmakeFlags = [
+      # file RPATH_CHANGE could not write new RPATH
+      "-DCMAKE_SKIP_BUILD_RPATH=ON"
+
+      # ensure determinism in the compiler build
+      "-DZIG_TARGET_MCPU=baseline"
+    ];
+
+    doCheck = true;
+    installCheckPhase = ''
+      $out/bin/zig test --cache-dir "$TMPDIR" -I $src/test $src/test/behavior.zig
+    '';
+
   };
 in
 mkShell {

Another nice effect of this is that using the latest derivation allowed me to test the build as well before using the freshly compiled zig toolchain. :slight_smile:

1 Like

Sorry I arrived a bit late, Mitchell Hashimoto has a flake for zig:

2 Likes

Looks very nice, I’ll have a look at this, thank you!

Final update: it works on my MacBook too. :slightly_smiling_face:

1 Like