Came across the patchShebangs
command while looking at packages in the Nixpkgs repo, and saw it used in various phases of the standard environment's generic builder, but not sure what it is for or why it is needed in the first place.
What is the `patchShebangs` command in Nix build expressions?
1k Views Asked by toraritte At
1
In short: shell scripts used during a Nix build won't work out of the box because Nix clears the environment, and so the interpreter directive (shebang), on the first line of the script determining the program to use to evaluate the script body, will not find it.
patchShebangs
looks up the interpreter in the Nix store, and amends the script shebang.0. Introduction
patchShebangs
is indirectly mentioned in the Nixpkgs manual when describing the phases of the generic builder of the Nixpkgs standard environment, stating that the fixup phase at one pointIt is important to note that (paraphrasing @jonringer's comment), "the
patchShebangs
command is only available during the build if you source the$stdenv/setup
setup hook" (more on that below) "provided bystdenv
's (the Nixpkgs standard environment's) default builder (you get this by default when usingstdenv.mkDerivation
), which is why the starting point of almost all nix expressions isimport <nixpkgs> {}, stdenv.mkDerivation
, or something similar."1. Where is
patchShebangs
definedThe file
patch-shebangs.sh
in the Nixpkgs repo (also documented at 6.7.4. patch-shebangs.sh) defines thepatchShebangs
function, which in turn is used to implementpatchShebangsAuto
, the setup hook that is registered to run during the fixup phase.2. Why are shebang rewrites needed when building Nix packages?
According to the comment at the top of
patch-shebangs.sh
:The line in a shell script starting with
#!
is called shebang (among others), and it is an interpreter directive to the executing shell as for what program to use to decipher the text below; the characters after#!
has to consitute an absolute path that points to this executable. For example,#!/usr/bin/python3
will expect to find thepython3
program there to carry out the commands in the body of the shell script written in the Python programming language.Using shell scripts during package build phases becomes problematic though because
The quote above is from the Nix manual but the builder, that is shown there as an example, uses
$stdenv/setup
- a shell script that sets up a pristine sandbox environment for the build process, unsetting most (all?) environment variables from the calling shell, and only including a small number of utilities. (This is done to make builds reproducible, as much as possible.)1$stdenv/setup
is usually called implicitly when usingstdenv.mkDerivation
with the generic builder (i.e., when thebuilder
attribute is left undeclared) but one can write their own builders and invoke it explicitly during the build process.TIP: This answer shows one way to find where a certain Nix function is defined (although it is not infallible).
As a corollary, the programs pointed to by the shebang directives won't be at those locations (or unavailable to reach from the sandbox), but they are actually around (or will be) in the Nix store so the paths will need to be re-pointed to their location in there.
3. How to use
3.1 Implicitly
As mentioned above,
patchShebangs
is automatically invoked by thepatchShebangsAuto
setup hook during the fixup phase whenever a package is built - unless one opts out of this by setting thedontPatchShebangs
variable (or thedontFixup
variable for that matter) (see Variables controlling the fixup phase in the Nixpkgs manual).Reminder to self: 6.4 Bash Conditional Expressions.
3.1.0 What scripts is
patchShebangs
used on when invoked automatically?Usually on scripts installed by packages (for example to
$out/bin
).Or the ones provided default by the Nixpkgs standard library? I presume that these have to be generic enough to run on different platforms so that (1) the template is built, and (2) scripts shebangs are patched in the end. (@jtojnar confirmed this conjecture, but this section needs references, hence the small case.)
3.1.1 How to use the variables controlling a build phase?
Pass it to
mkDerivation
like any other variable controlling the builder.3.2 Explicitly
Historical note: Originally,
patchShebangs
was not externally callable, but it was later extracted to make its functionality re-usable in other build phases as well.Again, from the comments in the implementation:
It needs to be run on scripts that are to be executed directly (shell scripts included) during build time. These may be
Specific examples from around the web:
... and quoting @jtojnar:
Footnotes
[1]: TODO: Find out more about how the sandbox(es) are built exactly and what are barred and what are allowed. Quoting @jtojnar to bring one example:
[2]: @jtojnar's comment: "Right, you will not need to use it explicitly for scripts that are only executed at run time, since those will be handled by the implicit call."
All links in this thread have (hopefully) been saved to the Internet Archive. (The soundtrack of the thread is this gem.)