Apptiva Logo

Nix - Die Sprache

Das Editieren von shell.nix oder flake.nix Dateien kann furchteinflössend sein, wenn man mit der Syntax von “Nix” nicht vertraut ist. Deshalb nun ein Versuch, die Sprache etwas näher zu Beleuchten.

Publiziert am von Patrik Stutz

Nix - Die Sprache - ist eigentlich ziemlich simpel. Man könnte schon fast sagen, es handelt sich dabei um JSON mit Funktionen.

Hier ein Beispiel der Basis-Datentypen und Konstrukte:

rec { # "rec" erlaubt uns hier, dass wir innerhalb des objects auf properties verweisen können
aBoolean = true;
aNumber = 1;
aString = "1";
aMultilineString = '' Der whitespace wird bei allen Zeilen getrimmt
a
b
c
'';
anArray = [
aBoolean
aNumber
aString
];
anObject = {
a = 1;
b = 2;
c = anArray;
};
aConditional = if aBoolean then anObject else anArray;
letIn = let
foo = 1;
bar = 2;
in foo+bar;
}

/*

nix-repl> (import ./datatypes.nix).anObject
{ a = 1; b = 2; c = [ ... ]; }

nix-repl> (import ./datatypes.nix).anArray
[ true 1 "1" ]

nix-repl> (import ./datatypes.nix).aMultilineString
"a\nb\nc\n"

nix-repl> (import ./datatypes.nix).aConditional
{ a = 1; b = 2; c = [ ... ]; }

nix-repl> (import ./datatypes.nix).aConditional
{ a = 1; b = 2; c = [ ... ]; }

nix-repl> (import ./datatypes.nix).letIn
3

*/

Und so sehen Funktionen aus:

rec {
# Funktionen haben immer nur ein Argument
sayHello = name: "hello "+name;

# Braucht man mehrere, fasst man diese zu einem Objekt zusammen
plus = {a,b}: a+b;

# Oder gibt eine neue Funktion zurück, der man ein weiteres Argument übergeben kann (currying)
plusCurried = a: b: a+b;

# Es gibt auch eine handvoll eingabaute funktionen im "builtins" objekt
longerThan4Characters = input: (builtins.stringLength input) > 4; # https://nixos.org/manual/nix/stable/language/builtins.html#builtins-stringLength
}

/*

nix-repl> (import ./functions.nix).sayHello "patrik"
"hello patrik"

nix-repl> (import ./functions.nix).plus {a = 1; b = 3;}
4

nix-repl> (import ./functions.nix).plusCurried 1 3
4

nix-repl> (import ./functions.nix).longerThan4Characters "hello world"
true

*/

2-Phasen

Nix-Builds durchlaufen immer 2 Phasen:

  1. Nix-Dateien und die Expressions darin evaluieren zu sogenannten “Derivations”, dies sind statische Daten-Konstrukte, die alle Informationen beinhalten, um ein “Paket” zu builden.
  2. Das Paket mit diesen Informationen in einer Sandbox ohne Internet- oder Dateisystem-Zugriff builden. In diesem Schritt wird die Nix-Sprache nicht mehr verwendet. Man könnte diese “Derivation” konstrukte theoretisch also auch mit einer anderen Sprache generieren und dann builden lassen.

Ein Beispiel:

let
nixpkgs = import <nixpkgs> {};
in nixpkgs.stdenv.mkDerivation {
name = "say-hello";
src = ./say-hello;
nativeBuildInputs = [
nixpkgs.nodePackages.typescript
];
buildInputs = [
nixpkgs.nodejs-18_x
];
buildPhase = ''
tsc --out say-hello.js $src/index.ts
'';
installPhase = ''
mkdir -p $out/bin
cp say-hello.js $out/bin/say-hello2
'';
}

evaluiert zu

Derive(
[
("out","/nix/store/0l5g8w1wmk7j3q6z7ldkfvcp2xfbpr2w-say-hello","","")
],
[
("/nix/store/182qlb4yhgwcalpfixj54p9avgdyyk48-typescript-4.9.4.drv",["out"]),
("/nix/store/66c26vpjxagdr5dirdcg27k0jgakq51k-stdenv-linux.drv",["out"]),
("/nix/store/vqs99ic7qjkjplb7r4qvja3l276avqzq-nodejs-18.13.0.drv",["out"]),
("/nix/store/wx04vp65xza0916zv3w1xprjc92pa7f8-bash-5.2-p15.drv",["out"])
],
[
"/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh",
"/nix/store/707bbyri4cypp9lrdc7j4kwflncd9xgy-say-hello"
],
"x86_64-linux",
"/nix/store/qqa28hmysc23yy081d178jfd9a1yk8aw-bash-5.2-p15/bin/bash",
[
"-e",
"/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh"
],
[
("__structuredAttrs",""),
("buildInputs","/nix/store/xl6461kig4j53p1cm0b3j9nm4l373g6a-nodejs-18.13.0"),
("buildPhase","tsc --out say-hello.js $src/index.ts\n"),
("builder","/nix/store/qqa28hmysc23yy081d178jfd9a1yk8aw-bash-5.2-p15/bin/bash"),
("cmakeFlags",""),
("configureFlags",""),
("depsBuildBuild",""),
("depsBuildBuildPropagated",""),
("depsBuildTarget",""),
("depsBuildTargetPropagated",""),
("depsHostHost",""),
("depsHostHostPropagated",""),
("depsTargetTarget",""),
("depsTargetTargetPropagated",""),
("doCheck",""),
("doInstallCheck",""),
("installPhase","mkdir -p $out/bin\ncp say-hello.js $out/bin/say-hello2\n"),
("mesonFlags",""),
("name","say-hello"),
("nativeBuildInputs","/nix/store/v678yfnj4c0bw8rcy01sf7rgi9j5n29d-typescript-4.9.4"),
("out","/nix/store/0l5g8w1wmk7j3q6z7ldkfvcp2xfbpr2w-say-hello"),
("outputs","out"),
("patches",""),
("propagatedBuildInputs",""),
("propagatedNativeBuildInputs",""),
("src","/nix/store/707bbyri4cypp9lrdc7j4kwflncd9xgy-say-hello"),
("stdenv","/nix/store/3yfs41f4b60jya2gk6xikx4s97zsxjr0-stdenv-linux"),
("strictDeps",""),
("system","x86_64-linux")
]
)