Super n00b question about derivations

Hi all,

I have the most annoyingly basic question ever, but I have been all over the manual and the blogs and I can’t figure it out:

Given a derivation, what controls what gets finally added to the PATH by its installation?

For context: I am trying to package a closed-source application. “Installing” it just means copying anduntaring some things into the right places, and setting some environment variables. I’ve written a custom and observe this working correctly when I run something like nix-shell -p myPackage (I have added it into my global nixpkgs as an overlay). However, nothing in the output directory is accessible for the environment. The directory layout of this package is somewhat arbitrary; what I really want is a way to either add specific subdirectories to the PATH, or to add symlinks to particular files/executables into my profile (I’m also happy to put symlinks within the output directory, if the Nix system simply makes assumptions about file locations).

How do I control this??! I know I am missing something obvious, but all the guides seem to skip over this step. I am guessing it must be something to do with derivation outputs, but every time I set my derivation’s output to anything at all, including directories in the output that exist, it breaks the build.

As a stretch goal, is there also a way to have my derivation set arbitrary environment variables? Say, so that the nix-shell invocation above would see them set. What I’ve done so far is make a default.nix, returning a derivation that has my desired derivation as a buildInput, and then a couple of arbitrary attributes that nix-shell sets onto the environment. But I’d like to be able to do away with this acillary default.nix if possible and just have them set by my main derivation.

Does that make sense?

I’m sure these are stupid questions but I’ve been looking around all day for the answers :confused:


First off: you don’t need to talk yourself down! It’s a valid question :slight_smile:

To answer it: it’s simply the bin directory in the derivation’s output that ends up in $PATH.

You can’t set arbitrary environment variables. You can write a shell hook which gets evaluated in nix-shell, but this won’t affect the result of installing the package into a profile.

EDIT: a number of env vars end up pointing to the nix profile though. For example, on my nixos:

$ env | grep -F .nix-profile
NIX_PROFILES=/run/current-system/sw /nix/var/nix/profiles/default /home/linus/.nix-profile /etc/profiles/per-user/linus

First off: you don’t need to talk yourself down!

You’re very kind @lheckemann! Nix makes me feel entirely stupid on a daily basis, it’s often hard to know whether any question even makes sense, let alone is worth asking.

To answer it: it’s simply the bin directory in the derivation’s output that ends up in $PATH.

WAT! It’s that simple? Ouch. Is it me or is that not really mentioned anywhere in the documentation? Also, is it possible to change that? Or should I just make a /bin directory in my output and make some symlinks to other parts of the output in there?

You can write a shell hook which gets evaluated in nix-shell

Oh, hm, that sounds interesting at least, if calling nix-shell -p myPackage will be sufficient to have the hook executed.

Is this as simple as adding an attribute to my derivation like shellHook = ./;?

Thanks so much!

1 Like

Questions are there to be answered! :wink:

I have to say documentation is not the nix ecosystem’s strongest point. NixOS - Nixpkgs 21.05 manual is the closest I’ve found in a cursory search to documentation on this subject.

For the shell hook, not quite — nix-shell runs the shellHook of the derivation you’re shelling into, so it’s like the default.nix you mentioned in your original post. Additionally, the hook is just verbatim shell code and not a file reference, for example:

$ cat test.nix
with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "test";
  buildCommand = "touch $out";
  shellHook = "echo hello";
$ nix-shell test.nix

1 Like

Ah, thanks for all the info! I’ll have a play and let you know all the things I subsequently fail to figure out :slight_smile:

I have a follow-up question, if you’ll indulge me yet further:

My application comes in a big(, local) tarball (~450MB), which itself contains more tarballs that need to be unpacked in various places. What is the most appropriate way to deal with a such a thing?

Right now, in my derivation, I simply list src = ./package.tgz;, and then tar xzf it manually as a first step in my This does work, but I feel like this is an inefficient way to do this…

I am happy to move the tarball somewhere specific, but would like at least the option t keep it on my machine if possible, and not need to get it over HTTP or whatever.

1 Like

The tarball, once downloaded using fetchurl, will be kept until it is deleted by the garbage collector. You can prevent it from being deleted by making it a GC root, for example by making an indirect root in your downloads directory:

cd ~/Downloads
nix build -o foo.tar.gz -f ~/foo.nix src # or
nix build -o foo.tar.gz  # or similar

This creates a symlink to the store path of the tarball, and a symlink to that symlink in /nix/var/nix/gcroots/auto which allows the garbage-collector to find out that it’s still being referenced and shouldn’t be deleted.

1 Like