GSL dependency cblas in nix flake

Using this flake

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs";
    flake-utils.url = "github:numtide/flake-utils";
    zig-overlay.url = "github:mitchellh/zig-overlay";
  };

  outputs = inputs:
    let
      inherit (inputs) nixpkgs zig-overlay flake-utils gsl;
      systems = [ "x86_64-linux" ];
    in
    flake-utils.lib.eachSystem systems (system:
      let
        pkgs = import nixpkgs { inherit system; };
        zig = zig-overlay.packages.${system}.master;
        gsl = pkgs.gsl;
        blas = pkgs.blas;
      in
      {
        devShells.default = pkgs.mkShell {
          packages = [ zig gsl blas ];
        };
      });
}

I want to compile this C program using the Zig toolchain:

#include <gsl/gsl_cdf.h>
#include <stdio.h>

int main(){
    double bottom_tail = gsl_cdf_gaussian_P(-1.96, 1);
    printf("Area betweeen [-1.96, 1.96]: %g\n", 1 - 2*bottom_tail);
}

via

zig build-exe main_gsl.c -lgsl -lc

but I get a lot of undefined cblas_* from gsl.so

error: ld.lld: undefined reference due to --no-allow-shlib-undefined: cblas_ctrmv
    note: referenced by /nix/store/wh63z2hi25bkiajrg6rzj9pfpyvwp0wy-gsl-2.7.1/lib/libgsl.so
error: ld.lld: undefined reference due to --no-allow-shlib-undefined: cblas_zswap
    note: referenced by /nix/store/wh63z2hi25bkiajrg6rzj9pfpyvwp0wy-gsl-2.7.1/lib/libgsl.so
...

so the actual question, how can I pull in the BLAS dependency for GSL? I tried above with blas package, but initially thought that the dependency would be pulled in with GSL already?

Well, I gave up too early. Got this fixed via

zig build-exe main_gsl.c -lgsl -lc -lblas

and added gsl and blas to the mkShell nativeBuildInputs for good measure.

Edit: looked at the wrong output, still not resolved, but I got closer, now symbol lookup fails with

./main_gsl: symbol lookup error: /nix/store/wh63z2hi25bkiajrg6rzj9pfpyvwp0wy-gsl-2.7.1/lib/libgsl.so.27: undefined symbol: cblas_ctrmv

I tried to investigate a bit, I don’t know why it works with gcc which includes a dynamic link to libgslcblas.so.0 (containing cblas_ctrmv) while zig does not,
even tough pkg-config --cflags --libs gsl does return:

-I/nix/store/yggzrbj04jg5mr34g2v83zmaw6sk2lwd-gsl-2.8-dev/include
-L/nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib
-lgsl -lgslcblas -lm

GCC

$ nix -L build .#test-gcc
[…]
@nix { "action": "setPhase", "phase": "buildPhase" }
++ mkdir -p /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin
+++ pkg-config --cflags --libs gsl
++ gcc main.c -o /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin/test-gcc -I/nix/store/yggzrbj04jg5mr34g2v83zmaw6sk2lwd-gsl-2.8-dev/include -L/nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib -lgsl -lgslcblas -lm
++ set +x
Running phase: installPhase
@nix { "action": "setPhase", "phase": "installPhase" }
no Makefile or custom installPhase, doing nothing
++++ grep RUNPATH
++++ readelf -d /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin/test-gcc
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/lib:/nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib:/nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib:/nix/store/mhd0rk497xm0xnip7262xdw9bylvzh99-gcc-13.3.0-lib/lib]
++++ lddtree /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin/test-gcc
/nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin/test-gcc (interpreter => /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/ld-linux-x86-64.so.2)
    libgsl.so.28 => /nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib/libgsl.so.28
    libgslcblas.so.0 => /nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib/libgslcblas.so.0
    libm.so.6 => /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/libm.so.6
    libc.so.6 => /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/libc.so.6
++++ set +x
Running phase: fixupPhase
@nix { "action": "setPhase", "phase": "fixupPhase" }
shrinking RPATHs of ELF executables and libraries in /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc
shrinking /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin/test-gcc
checking for references to /build/ in /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc...
patching script interpreter paths in /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc
stripping (with command strip and flags -S -p) in  /nix/store/jqg6f90qia0p1nxg0qh664j8018kvfby-test-gcc/bin
$ nix -L run .#test-gcc
Area betweeen [-1.96, 1.96]: 0.950004

Zig

$ nix -L build .#test-zig
[…]
@nix { "action": "setPhase", "phase": "buildPhase" }
++ export ZIG_GLOBAL_CACHE_DIR=/build/69fw0gijinr109dfcwg9zcz7099dil57-source
++ ZIG_GLOBAL_CACHE_DIR=/build/69fw0gijinr109dfcwg9zcz7099dil57-source
++ mkdir -p /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin
+++ pkg-config --cflags --libs gsl
++ zig build-exe main.c -femit-bin=/nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin/test-zig -I/nix/store/yggzrbj04jg5mr34g2v83zmaw6sk2lwd-gsl-2.8-dev/include -L/nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib -lgsl -lgslcblas -lm
++ set +x
Running phase: installPhase
@nix { "action": "setPhase", "phase": "installPhase" }
no Makefile or custom installPhase, doing nothing
++++ grep RUNPATH
++++ readelf -d /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin/test-zig
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib:/nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/lib:/nix/store/lrz0729rrid6p4lsfpj0d1mlamzzrlhr-python3-3.12.8-env/lib]
++++ lddtree /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin/test-zig
/nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin/test-zig (interpreter => /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/ld-linux-x86-64.so.2)
    libgsl.so.28 => /nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib/libgsl.so.28
        libm.so.6 => /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/libm.so.6
    libc.so.6 => /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/libc.so.6
++++ set +x
Running phase: fixupPhase
@nix { "action": "setPhase", "phase": "fixupPhase" }
shrinking RPATHs of ELF executables and libraries in /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig
shrinking /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin/test-zig
checking for references to /build/ in /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig...
patching script interpreter paths in /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig
stripping (with command strip and flags -S -p) in  /nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin
$ nix -L run .#test-zig
/nix/store/6all27vywm3h7256bkfgbcbc3g1hlb4c-test-zig/bin/test-zig:
symbol lookup error: /nix/store/988nciljvk1p8y9byarjxd6ygf3fc7i1-gsl-2.8/lib/libgsl.so.28:
undefined symbol: cblas_ctrmv

flake.nix

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
    flake-utils.url = "github:numtide/flake-utils";
    #zig-overlay.url = "github:mitchellh/zig-overlay";
    #zig2nix.url = "github:Cloudef/zig2nix";
    #zig2nix.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs:
    inputs.flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
      let
        pkgs = import inputs.nixpkgs { inherit system; };
        zig = pkgs.zig;
        #zig = inputs.zig-overlay.packages.${system}.master;
        # Wrap binary package zig on linux so /usr/bin/env can be found inside a sandbox
        zigBin =
          pkgs.writeScriptBin "zig" ''
            args=()
            for d in /*; do
              args+=("--dev-bind" "$d" "$d")
            done
            ${pkgs.bubblewrap}/bin/bwrap "''${args[@]}" \
              --bind ${pkgs.coreutils} /usr \
              -- ${pkgs.lib.getExe zig} "$@"
          '';
        postInstall = ''
          set -x
          readelf -d $out/bin/test-* | grep RUNPATH
          lddtree $out/bin/test-*
          set +x
        '';
      in
      {
        packages.test-zig = pkgs.stdenv.mkDerivation {
          name = "test-zig";
          src = ./.;
          buildInputs = [ zigBin pkgs.gsl pkgs.pkg-config pkgs.llvmPackages.bintools pkgs.pax-utils pkgs.binutils ];
          buildPhase = ''
            set -x
            export ZIG_GLOBAL_CACHE_DIR=$PWD
            mkdir -p $out/bin
            zig build-exe main.c -femit-bin=$out/bin/test-zig $(pkg-config --cflags --libs gsl)
            set +x
          '';
          inherit postInstall;
        };
        packages.test-gcc = pkgs.stdenv.mkDerivation {
          name = "test-gcc";
          src = ./.;
          buildInputs = [ pkgs.gsl pkgs.pkg-config pkgs.pax-utils pkgs.binutils ];
          buildPhase = ''
            set -x
            mkdir -p $out/bin
            gcc main.c -o $out/bin/test-gcc $(pkg-config --cflags --libs gsl)
            set +x
          '';
          inherit postInstall;
        };
        devShells.default = pkgs.mkShell {
          packages = [
            zig
            pkgs.gsl
            #pkgs.blas
            pkgs.pkg-config
          ];
        };
      });
}

main.c

#include <gsl/gsl_cdf.h>
#include <stdio.h>

int main(){
    double bottom_tail = gsl_cdf_gaussian_P(-1.96, 1);
    printf("Area betweeen [-1.96, 1.96]: %g\n", 1 - 2*bottom_tail);
}
1 Like

Follow-up on zig build-exe should not strip indirectly needed libraries · Issue #23215 · ziglang/zig · GitHub

Thanks for investigation, sharing your findings (+ TIL nix dev tricks) and the upstream issue link. Please see Zig cc dynamic C lib runtime error on NixOS - #22 by fleimgruber - Help - Ziggit where I came to the same conclusion with clang.