How to use a nix derivation from a local folder?

Suppose have a folder, and inside the folder there is a default.nix file that returns the result of mkDerivation.

As an example, I recently learned how to make a nix derivation containing Node.js: Managing multiple versions of Node.js with nix? - #9 by trusktr

I can run nix-build from inside the folder that contains that default.nix, and it outputs result that is symlinked to the output folder in the nix store. The derivation contains a /bin/node which I can manually run fine.

I haven’t successfully “packaged” it yet, as I haven’t actually installed it to the system or used it in a nix-shell yet. Being able to run ./result/bin/node has been enough to get me back to work on local projects.

To take things further, how would I

  1. run it with the node executable available in a nix-shell?
  2. import it in another derivation directly from the local folder that contains this default.nix?
    • Is it even possible to import from a local directory to begin with?
  3. install it to my system with nix-env?
    • I imagine that if what I ask in question 2. is possible, then another way to install it to my system is by importing it in my configuration.nix file and perhaps adding it to environment.systemPackages.
6 Likes

It sounds like what you might want here is an overlay to extend pkgs. Here’s a simple one that lives in my ~/.config/nixpkgs/overlays that adds a few packages to my system. I can then install these through home.packages (I use home-manager) as I normally would. Works auto-magically.

The motivation for this overlay was similar to what it sounds like you’re driving at: There was a package (kepubify in my case) that I couldn’t get through one of the official channels that I wanted to use in a derivation. Creating an overlay that extended pkgs to include that application let me work with it like I would any other NixOS package.

I’m still learning Nix/NixOS myself, and have wrestled with how to use overlays system-wide. Right now I have a /etc/nixos/overlays folder that contains an additional kernel module my laptop needs. Unlike ~/.config/nixpkgs/overlays, that overlay doesn’t get picked up automatically. Right now I’m importing it with

nixpkgs.overlays = [ (import ./overlays) ];

in my /etc/nixos/configuration.nix, but I feel like there must be a better way.

3 Likes

You could go the overlay way as described by necopinus or a somewhat simpler version that I use in my configuration.nix. Here’s a (shortened) version:

  environment.systemPackages = with pkgs; [
    ...
    which
    wireshark
    xscreensaver
    zip
    (callPackage ./pkgs/go-for-it { })
    (callPackage ./pkgs/nixos-shell { })
    (writeShellScriptBin "afuse-sshfs" ''
      mkdir -p $HOME/sshfs
      exec ${afuse}/bin/afuse -o mount_template='${sshfsFuse}/bin/sshfs %r:/ %m' -o unmount_template='fusermount -u -z %m' $HOME/sshfs
      '')
  ] ;

./pkgs/nixos-shell/default.nix is a common package that looks like this:

{ stdenv, fetchFromGitHub, bash }:

stdenv.mkDerivation rec {
  name = "nixos-shell-${version}";
  version = "b0c0d93c0db4ce5661d0f2e6cbea4d554f9c28de";

  src = fetchFromGitHub {
    owner = "Mic92";
    repo = "nixos-shell";
    rev = "${version}";
    sha256 = "0z2mklcdrf98mm5ywvp4n3y7rdbizjz6wnqzj6c28r0cy1an5xib";
  };

  patches = [ ./nixos-shell.patch ];
  buildInputs = [ bash ];
  preConfigure = ''
    export PREFIX=$out
  '';
}

As you see, I’m also using writeScriptBin directly for very simple scripts that don’t warrant a full package.
Both callPackage and writeShellScriptBin are in the pkgs scope, so you can also refer to them as pkgs.callPackage and pkgs.writeShellScriptBin.

The downside of not using an overlay is that other packages cannot depend on them, unless you include them in that other package through callPackage as well.

2 Likes

Hi,

No need for an overlay, nor touching your NixOS config.
Nix can handle any files containing any Nix expressions.
Once you have an “usual” (a function) default.nix file, you can “instantiate” (see note) it with e.g:

pkgs.callPackage ./default.nix {}

This Nix expression is the result of mkDerivation (which is a derivation) and can be used where a “package” is expected, for instance in mkShell’s inputsFrom or nix-build or whatever.

More specifically, consider those files:

  • derivation.nix
{ writeShellScriptBin }:

# This is eventually evaluated as mkDerivation, thus derivation
writeShellScriptBin "somebin" ''
  echo it works
''
  • default.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.callPackage ./derivation.nix {}
  • shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [ (pkgs.callPackage ./derivation.nix {}) ];
}

Note that the “usual” package file is in derivation.nix because default.nix has a special meaning to nix-build.

You can then:

  1. run it with the executable available in a nix-shell: $ nix-shell
  2. import it in another derivation: you already did it in shell.nix and default.nix :wink: possible in NixOS configuration.nix as well, of course
  3. install it: $ nix-env -if . (or $ nix-env -if default.nix as usual)
  4. as usual, build it: $ nix-build for a result symlink

The file naming is what I found more convenient as it does not require specifying the file name for nix-shell and nix-build, but you do as you want. There’s really, IMHO, only those 2 (default) rules in Nix ;).

Note: this is not the proper word. It’s actually a function application, with a bit of “magic” (introspection) from callPackage.

4 Likes