6.1 KiB
::: header
nix
::: ::: section
nix, the language
Nix is a language. It has numbers. You can add them.
1 + 2
The above expression evaluates to 3
. If you have nix installed, try
running nix repl
and pasting that in. Nix also has strings.
"hello nix!"
"escapes\nand\nnewlines" == "escapes
and
newlines"
''alternative syntax''
Nix has booleans.
!(true && false) || false
Nix has sets, or key value pairs. These are known as records, maps, or objects in other lanugages.
{ foo = 123; }.foo
Sets can be nested.
# There isn't any hard rule on which style to use
{ foo = { bar = "baz"; }; } == { foo.bar = "baz"; }
The let
and in
keyword defines a variable for a given scope.
let
foo = 3;
bar = foo * 3;
in
bar
Nix has pure functions that transform inputs to outputs without side effects.
let
incr = n: n + 1;
in
incr 5
To use multiple parameters, either pass a set or use currying (create a function that returns another function).
let
addSet = input: input.a + input.b;
addDestructure = { a, b }: a + b;
addCurry = a: b: a + b;
in {
foo = addSet { a = 10; b = 20; };
bar = addDestructure { a = 10; b = 20; };
baz = addCurry 30 40;
}
Destructuring optional fields.
let
# this will use b = 5 if b is not defined
addOptional = { a, b ? 5 }: a + b;
in {
foo = addOptional { a = 1; };
bar = addOptional { a = 1; b = 1; };
}
The inherit
keyword.
let
key1 = "something here";
key2 = "something else";
in {
inherit key1 key2;
}
Nix provides many builtin functions in the builtins
set. If you're
using the repl, press tab to get a list of them.
{
system = builtins.currentSystem;
}
Finally, nix has a path type.
{
sourceCode = ./path/to/sources;
}
::: section
nix, the build tool
Nix provides a builtin function to create derivations. These are special descriptions of how to derive build outputs from source code. Build outputs can be anything, such as binaries, shared libraries, or a docker tarball.
derivation {
name = "foobar";
# here is where you'd
# src = ./path/to/source
# the $out variable has the location of where the build outputs should go
# the build outputs can be a file or a folder of any structure
# the $src variable has the location of the sources
builder = "/bin/sh";
args = ["-c" "echo 'Hello, derivation!' > $out"];
system = builtins.currentSystem;
}
This code outputs a hash-based path to a derivation, nix's version of a
build script. Derivations derive build outputs from build inputs. To
build the derivation, use :b (copy-pasted code)
in the repl. It should
print a path to the build outputs. If you cat
the output, you should
get back "Hello, derivation!".
NixOS/nixpkgs is a combination package
repository and standard library, containing thousands of derivations and
functions. Using the derivation
function is almost always unnecessary,
since nixpkgs probably has a helper for whatever language or build system
you want to use. Even if you need to manually make a derivation, there is
mkDerivation
which is a bit higher level than derivation
.
Nix flakes are how derivations and packages are managed, and is what you probably want to be using.
You've probably heard it before, but it's worth repeating: although more difficult to set up, using nix over other build systems gives you reproducability. This means that the same nix expression and source code will always produce the same outputs. If it works on one nix user's machine, it will work every nix user's machine. If a build fails, it fails for everyone.
Nix flakes are a new tool that makes the inputs explicit as well, instead of only the build steps. Anything that isn't explicitly defined as an input, whether it's binaries, paths, or environment variables, is not given to the derivation's builder. Here's an example flake:
{
description = "any useful description here";
inputs = {
# Inputs take in flake references
# https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-flake#flake-references
nixpkgs.url = "github:NixOS/nixpkgs";
};
outputs = { self, nixpkgs }: {
# reuse an existing derivation for simplicity
packages.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.hello;
};
}
This code should be put inside flake.nix
at the root of a git
repository. Running nix flake new hello-world
would create a directory
called hello-world
pre-initialized with a flake like this. From there,
run nix build
to build (the binary will be in result/bin/hello
), or
nix run
to build and run the binary.
Maybe you've noticed already, but the flake isn't really building anything new - it's exporting an already made derivation from nixpkgs. Here is another flake:
{
description = "any useful description here";
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
outputs = { self, nixpkgs }: let
pkgs = import nixpkgs { system = "x86_64-linux"; };
in {
packages.x86_64-linux.default = pkgs.stdenv.mkDerivation {
name = "hello";
src = self;
buildPhase = ''
echo -e '#!/bin/sh\necho hello world' > bin
chmod +x bin
'';
installPhase = ''
mkdir -p $out/bin
mv bin $out/bin/hello
'';
};
};
}
:::