In a previous article, I described how to build a Nix package. In this article, I will discuss how to package scripts: specifically, how to handle their runtime dependencies.
Let's begin with the example of a simple script:
#!/usr/bin/env xonsh
echo "2 + 2" | bc
This is a xonsh script, which calls echo
and bc
.
This program should print the number 4
.
Let's look at a basic package definition:
{ stdenv, lib, xonsh, toybox, bc, makeWrapper }:
stdenv.mkDerivation {
pname = "example";
version = "0.0.1";
src = ./.;
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
mkdir -p $out/bin
install -m 755 example.xonsh $out/bin/example
wrapProgram $out/bin/example --set PATH ${lib.makeBinPath [
bc
toybox
xonsh
]}
'';
}
Let's explore this line-by-line:
{ stdenv, lib, xonsh, toybox, bc, makeWrapper }:
For most scripts we will need stdenv
, lib
, and makeWrapper
.
makeWrapper
provides wrapProgram
, which we will use below.
This particular program needs xonsh
, toybox
, and bc
.
xonsh
does not provide echo
, so we get it from toybox
.
stdenv.mkDerivation {
pname = "example";
version = "0.0.1";
src = ./.;
Here we are getting basic information about our program.
Normally, src =
would be followed by some fetcher like
fetchFromGithub
.
nativeBuildInputs = [ makeWrapper ];
Here we specify what we need to build the program.
Since it is a script, we just need makeWrapper
.
installPhase = ''
mkdir -p $out/bin
install -m 755 example.xonsh $out/bin/example
Here we explain how to actually extract the relevant files
from the source. install -m 755
moves the file and marks
it as executable.
wrapProgram $out/bin/example --set PATH ${lib.makeBinPath [
bc
toybox
xonsh
]}
'';
}
This is where we specify our runtime dependencies.
The --set PATH
argument is important. You may find other documents
saying to use --prefix
or --suffix
, but I recommend --set
because
it is the easiest to reason about: only what you specify in the following
list will be available to the script.
Hopefully this helps show how easy and convienient it is to use Nix to package scripts and make them distributable, for example, in a custom channel.