How does NixOS link built packages (firmware)?

If my understanding is correct, stdenv.mkDerivation will unpack and build in a temporary location and then install my package into the store. Putting the result of the derivation into environment.systemPackages or even hardware.firmware should actually create symbolic links in my system to the store, it should not?

How does environment.systemPackages know which files need to be symlinked to the system?

Also, I have proprietary firmware (which I extracted from a different OS) that I need to install under NixOS. The package is precompiled. Learning from I can just use the buildCommand to do so:

  hardware.firmware = [
    (pkgs.stdenvNoCC.mkDerivation { # NoCC, as we don't build anything here
      name = "brcm-firmware";
      # buildCommand will bypass all phases:
      buildCommand = ''
        cp -r ${./brcm}/* "$out" # just copying into the store

Now, the firmware is copied into the store, but /lib/firmware/brcm doesn’t link to it.

Where can I learn how Nix links after a package has been build in the store?

Where can I learn how Nix links after a package has been build in the store?

This doesn’t happen automatically, but is done by several modules in NixOS. On a non-NixOS system, you’d typically need to do this yourself.

How does environment.systemPackages know which files need to be symlinked to the system?

There’s an option environment.pathsToLink that defines this. There’s a whole bunch of paths that are added to it by default, see here:

For the firmware, you need to add the firmware packages to hardware.firmware, after which files from lib/firmware will be collected and used to build the firmware tree that you see in the NixOS generations, see here:


Thanks for taking the time explaining this. I still wonder why it won’t symlink /lib/firmware, but I got some better understanding about the process now.

In regards of the pathsToLink, it’s not listing /usr paths, but I also want to compile a package that wants a library to be in /usr/include/. On top of that the relative path of the dependency in the store starts with include/ right away, so even if it was linked to /usr/include it wouldn’t work.

I definitely realize there is no shortcut and I have to study the nixpkgs manual in detail to really know what I’m doing. I think most of my questions will be answered in doing so.

I assume you are using NixOS, right?

So, do you have lib/firmware/brcm in the out path of the derivation? And did you add the package to hardware.firmware and rebuild your config? If so, then you should see the brcm file in /run/current-system/firmware.

For what concerns the include file, NixOS simply doesn’t work this way. There is no FHS, and no system-wide libraries that are installed in /usr. Doing these things differently is exaclty what enables NixOS to work differently.
Typically, you would set the right flags for whatever build system or compiler you are using so that it would find such libraries in the nix store instead.

Thanks a lot!

First of all, I have to check /run/current-system/(lib/)firmware, not /lib/firmware, which was my first error. The outPath will probably be wrong, as my brcm will be at the top of the store package, when it actually should be in lib/firmware/brcm. (I need to check later when I’m in NixOS again, so yes, I’m using NixOS)

Secondly, I was never paying attention to the outputs attribute that gives me the opportunity to define bash env variables for the actual store paths to my buildInputs, as I was thinking libraries will be linked to my system (which would prevent NixOS from handling multiple versions, so that was limited thinking). This is the reason why I can’t get my other package to work.

If I get things correct from the manual now, I will provide my dependency in buildInputs and define a variable name for it in outputs and then I will have the variable name available to use in my builder script, which is the path to the dependency in the store, thus I can adjust the compiler flags to really look into the right path, right?

But, I thought this is why we have the PATH magic in the, but now it falls off the top of my head, this magic cannot work for absolute paths. Something like LDFLAGS=-I/usr/include/... cannot work in NixOS. I have to replace /usr with an output variable that points to the dependency in the store that has the include directory in its root.

I hope I got this right now.

One more thing though: Is there also nativeOutputs for nativeBuildInputs or how is are the natives handled in that regard?

You are confusing many concepts here.

outputs is for derivations to split themselves, mostly for size concerns. Some package might ship huge amounts of data, so splitting that into a separate output allows users to only substitute/keep the binaries without the data if they don’t need it. It bears no meaning in this case; it’d just mean you’d have to use yourpackage.outputname instead of just yourpackage. You only have one thing you want to output and therefore don’t need multiple outputs.

buildInputs is for libraries that provide some standardised way of accessing them. Their lib/s or includes will appear in the CFLAGs and LDFLAGs passed to the compiler and tools like pkg-config will be able to discover them.

nativeBuildInputs is for binaries that are run during the build and therefore need to be built for the buildPlatform. This is an important distinction for cross-compilation but that’s not relevant here.
Never heard of nativeOutputs and that doesn’t sound like something that’d make sense.

What you actually want here is something along these lines:

pkgs.runCommand "brcm-firmware" { } ''
  mkdir -p $out/lib/firmware/
  cp -r ${./brcm} "$out"/lib/firmware/

I’d perhaps even use this:

pkgs.runCommand "brcm-firmware" { } ''
  mkdir -p $out/lib/firmware/
  ln -s ${./brcm} "$out"/lib/firmware/

You don’t need to deal with the complexities of mkDerivation to just run some basic commands in a derivation.