Skip to content

Instantly share code, notes, and snippets.

@CMCDragonkai
Last active August 12, 2023 11:49
Show Gist options
  • Save CMCDragonkai/8b5cc041cea4a7e45a9cb89f849eaaf8 to your computer and use it in GitHub Desktop.
Save CMCDragonkai/8b5cc041cea4a7e45a9cb89f849eaaf8 to your computer and use it in GitHub Desktop.
An Illustration of a Nix C Package #nix #c

An Illustration of a Nix C Package

The pkgs.nix:

import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/4df3426f5a5e78cef4835897a43abd9e2a092b74.tar.gz) {}

The default.nix:

{
  pkgs ? import ./pkgs.nix,
  linuxPath ? "linuxPackages_4_17"
}:
  with pkgs;
  let
    linux = lib.getAttrFromPath (lib.splitString "." linuxPath) pkgs;
  in
    stdenv.mkDerivation {
      name = "awesome-package";
      version = "0.0.1";
      src = lib.cleanSource ./.;
      buildInputs = [
        linuxHeaders
      ];
      propagatedBuildInputs = [
        linux.bcc
      ];
    }

The shell.nix:

{
  pkgs ? import ./pkgs.nix,
  linuxPath ? "linuxPackages_4_17"
}:
  with pkgs;
  let
    linux = lib.getAttrFromPath (lib.splitString "." linuxPath) pkgs;
    drv = import ./default.nix { inherit pkgs linuxPath; };
  in
    drv.overrideAttrs (attrs: {
      src = null;
      shellHook = ''
        echo 'Entering ${attrs.name}'

        set -v

        # include headers (this works even if the derivation doesn't have a dev output)
        C_INCLUDE_PATH="${lib.makeSearchPathOutput "dev" "include" drv.buildInputs}"

        # static linking
        LIBRARY_PATH="${lib.makeLibraryPath drv.buildInputs}"

        # dynamic linking
        LD_LIBRARY_PATH="${lib.makeLibraryPath drv.propagatedBuildInputs}";

        set +v
      '';
    })

In this particular shell.nix we fully specified the LD_LIBRARY_PATH. This works as long as your program doesn't depend on some global dynamic libraries. On NixOS, these are usually the video card drivers. In those cases, you want to instead use something like:

LD_LIBRARY_PATH="${lib.makeLibraryPath drv.propagatedBuildInputs}''${$LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

This will append the external :$LD_LIBRARY_PATH but only if it exists.


Note that CPLUS_INCLUDE_PATH is for C++.

See: https://gist.github.com/CMCDragonkai/8d91e90c47d810cffe7e65af15a6824c for more information on lib.cleanSource.

It's also important to read: https://nixos.org/nixpkgs/manual/#ssec-setup-hooks and https://nixos.org/nixos/nix-pills/basic-dependencies-and-hooks.html

Usually these kinds of variables would be managed via setup hook. Like for example python manages a setup hook for the PYTHONPATH. But not every packaging system has this setup. Note it is the Python derivation itself that provides this setup hook which looks for every other buildInput or propagatedBuildInput.

@CMCDragonkai
Copy link
Author

Something that I didn't mention above is that you need to also consider nativeBuildInputs not just buildInputs and not just propagatedBuildInputs. Also most of the time any C based libraries will be linked in a dynamically shared way. Thus the compiled outputs will always be using LD_LIBRARY_PATH and not LIBRARY_PATH, necessitating the upstream dependencies to be propagated on anyway. I'm not entirely sure how this works.

@CMCDragonkai
Copy link
Author

Should also incorporate pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/

Also see https://github.com/edolstra/nix-repl/blob/master/default.nix as inspiration for how to bind into more complex C/C++ libraries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment