Hello, I'm Raimonster

Human being and software developer

Dynamic libs with Nix+Racket

3 minutes
July 5, 2020

When installing a package with nix-shell, you get a temporary profile that contains everything that was on your parent profile (where you ran nix-shell) plus the installed packages from that nix-shell command.

nixpkgs repo is big and generally ok, but there are hurdles here and there, that you must be able to jump if you want to walk least-walked-paths.

Let’s see what happens (from my dimm understanding still) when you install a dynamic library that is not directly linked from your installed packages.

I was trying to do a testrun of racket’s libgit2 library.

The first attempt failed, because racket couldn’t find the .so.

> (require libgit2)
. . ../../../../nix/store/rlw4h5mibwyavzhr1mscr95ibzl17i52-racket-7.7/share/racket/collects/ffi/unsafe.rkt:131:0: ffi-lib: couldn't open "libgit2.so.0.26.0" (libgit2.so.0.26.0: cannot open shared object file: No such file or directory)

Instant thought was to install the library using nix-shell -p libgit2, but the error was the same.

$ ls $LD_LIBRARY_PATH | grep libgit    # <- no results

So, somehow the library is not added to the LD_LIBRARY_PATH.

Let’s go first for the easy path.

$ nix-env -i libgit2
$ drracket

This just works, so we have a last resort solution. But nix-env is not cool, and we want to learn how to disentangle the whole thing!

Looking at more walked paths, I installed pygit2 to see what python does:

$ nix-shell -p python37Packages.pygit2
$ python -c 'import pygit2'
$ ls $LD_LIBRARY_PATH | grep libgit    # <- no results

And this one worked, but the library is not in the library path either. Let’s see how git proper gets the library…

ldd -v =git | grep libgit # <- no results

Note: =git is a zshism for $(whereis git | cut -f2 -f' ').

Shit. At this point, I’m not sure if the git binary is compiled statically or something. Let’s open the nixpkgs repo and see:

The files we’re mostly concerned about are:

In the first one, the python libgit binding, we see it depends on libgit package.

The second one, the git cli binary, we see no mentions to libgit2, so I imagine it compiles everything statically, and that’s why we didn’t see anything in the ldd command. The only suspicious thing we see is that

  preConfigure = lib.optionalString stdenv.isDarwin ''
    export DYLD_LIBRARY_PATH="${libgit2}/lib"

Third one is the libgit2 package itself. But there’s not much you can get from it.

So time to read a bit more:

So it seems for libraries that are not tied to anything in your environment, you have to manually add them to your LD_LIBRARY_PATH. It’s a bit of a bummer, but it kinda works. $(nix-build '<nixpkgs>' -A libgit2) this builds the library, and links a ‘result’ directory to the built lib. We currently don’t care about the result directory, but we want to add that built lib to the LD_LIBRARY_PATH.

$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(nix-build '<nixpkgs>' -A libgit2)/lib
$ drracket
(require libgit2)    ; WORKSSS!!

A way to build this into your own shell.nix files is like this:

with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "racket";

  buildInputs = [