Atelier Nix les fondements de nix : store et derivation Nix Langage Base
Atelier Nix
- nix : présentation à partir de la base
- atelier : avec des exemples pratiques bas niveau
Objectifs
- comprendre l’intérêt
- se mettre le pied à l’étrier
- se repérer
nix***
Nix est …
- un gestionnaire de paquetages logiciels, comme dpkg/rpm
- stocke tout dans
/nix/store, appelé le store - commande
nixounix-??????: C++/rust
- stocke tout dans
- un langage permettant de décrire comment bâtir un paquetage à partir des sources
- nixpkgs : un dépot git regroupant 120000 paquetages
- nixos : un OS reposant sur nix
Store
abstraction pour gérer des données immutable et les références entre elles
les données : des paquetages logiciels, des fichiers/répertoire
La réalisation la plus concrète est le store local dans /nix/store
=> la représentation par fichiers est facilement accessible
store-path
Chaque objet stocké dans le store a une référence unique, obtenue quand on insère l’objet dans le store.
Sous forme de chemin de fichiers :
/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1
|--------| |------------------------------| |----------|
store digest name
directory
/nix/store : stocke des store objects
Un objet est composé de :
Des choses qui ressemble à des fichiers :
- un fichier simple, avec un flag pour le bit d’exécution
- un répertoire
- un lien
=> serialisés au format NAR Nix Archive
- un tableau de références (store-path)
ajouter un fichier
❯ echo "just a normal file" > toto ❯ nix store add-file ./toto /nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto STORE_PATH=$(nix store add-file ./toto) ❯ cat /nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto just a normal file ❯ nix store dump-path /nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto nix-archive-1(typeregulacontentsjust a normal file ) nix path-info --json $STORE_PATH |jq . [ { "ca": "fixed:sha256:1sjjyg9yl0vc50shz7isk1qqhmn3n8xfkaknihqj9hh5f84f727p", "narHash": "sha256-d6VAeTwXBMWyN0ae/ax4wgRVp3IsnB7Ty7o/DV5PTYY=", "narSize": 136, "path": "/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto", "references": [], "registrationTime": 1746964972, "valid": true } ]
ajouter un répertoire
❯ mkdir totodir ❯ echo toto > totodir/truc ❯ nix store add-path totodir /nix/store/cbldlwca16l022dpxz37knlhyp22l68p-totodir ❯ tree /nix/store/cbldlwca16l022dpxz37knlhyp22l68p-totodir /nix/store/cbldlwca16l022dpxz37knlhyp22l68p-totodir └── truc 1 directory, 1 file ❯ cat /nix/store/cbldlwca16l022dpxz37knlhyp22l68p-totodir/truc toto
Derivation :
- object du store décrivant comment construire dans le store
- une recette de fabrication, stockée sous la forme d’un fichier
.drv - va contenir des références à d’autres objets !
Créer une dérivation à la main
Je pars d’un objet json vide
{}
J’essaye de l’ajouter au store comme une dérivation, et je suis les erreurs …
nix derivation add < drv.json Expected JSON object to contain key 'name' but it doesn't: {} Expected JSON object to contain key 'outputs' but it doesn't: {"name":"alamano"} Expected JSON value to be of type 'object' but it is of type 'null': null Expected JSON object to contain key 'inputSrcs' but it doesn't: {"name":"alamano","outputs":{}} Expected JSON value to be of type 'array' but it is of type 'null': null Expected JSON object to contain key 'inputDrvs' but it doesn't: {"inputSrcs":[],"name":"alamano","outputs":{}} Expected JSON value to be of type 'object' but it is of type 'null': null Expected JSON object to contain key 'system' but it doesn't: {"inputDrvs":{},"inputSrcs":[],"name":"alamano","outputs":{}} Expected JSON value to be of type 'string' but it is of type 'null': null Expected JSON object to contain key 'builder' but it doesn't: {"inputDrvs":{},"inputSrcs":[],"name":"alamano","outputs":{},"system":""} Expected JSON value to be of type 'string' but it is of type 'null': null Expected JSON object to contain key 'args' but it doesn't: {"builder":"","inputDrvs":{},"inputSrcs":[],"name":"alamano","outputs":{},"system":""} Expected JSON value to be of type 'array' but it is of type 'null': null Expected JSON object to contain key 'env' but it doesn't: {"args":[],"builder":"","inputDrvs":{},"inputSrcs":[],"name":"alamano","outputs":{},"system":""} Expected JSON value to be of type 'object' but it is of type 'null': null must have at least one output error: Expected JSON value to be of type 'object' but it is of type 'null': null
La dérivation minimale acceptée :
{
"name": "alamano",
"outputs": {
"out": {}
},
"inputSrcs" : [],
"inputDrvs" : {},
"system": "",
"builder": "",
"args": [],
"env": {}
}
On regarde le format pour compléter
{
"name": "alamano",
"outputs": {
"out": {
"path": "/nix/store/00000000000000000000000000000000-alamano"
}
},
"inputSrcs" : [],
"inputDrvs" : {},
"system": "x86_64-linux",
"builder": "/bin/sh",
"args": [
"-c",
"echo 'hello world' > $out"
],
"env": {
"out": "/nix/store/-alamano"
}
}
nix derivation add < drv.json
error: derivation '/nix/store/7ggp83qm62y9h36dz0bl0f0v34hg3x1j-alamano.drv' has incorrect output '/nix/store/00000000000000000000000000000000-alamano', should b e '/nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano'
On corrige le hash
{
"name": "alamano",
"outputs": {
"out": {
"path": "/nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano"
}
},
"inputSrcs" : [],
"inputDrvs" : {},
"system": "x86_64-linux",
"builder": "/bin/sh",
"args": [
"-c",
"echo 'hello world' > $out"
],
"env": {
"out": "/nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano"
}
}
nix derivation add < drv2.json
/nix/store/fbxzr0anq9qd8x1m1gvan2zflis6c452-alamano.drv
nix path-info --json $drv | jq .
{
"/nix/store/fbxzr0anq9qd8x1m1gvan2zflis6c452-alamano.drv": {
"ca": "text:sha256:1v35gb6kam0lgalzcisnfb6sfyxfhnd9hs2v6g37dgc81n4jzqln",
"deriver": null,
"narHash": "sha256-bFU5sTRnIUK3FWU94qJkFbWjvyilHEsoQvTVsjKnc10=",
"narSize": 320,
"references": [],
"registrationTime": 1747929825,
"signatures": [],
"ultimate": false
}
}
- On ajoute le fichier
toto/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-totocomme source - on rend visible la valeur par le builder par une variable d’environnement
- on corrige le hash final
{
"name": "alamano",
"outputs": {
"out": {
"path": "/nix/store/6y8zi6npfciizkml15c99hqamiw05r4d-alamano"
}
},
"inputSrcs" : [ "/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto"],
"inputDrvs" : {},
"system": "x86_64-linux",
"builder": "/bin/sh",
"args": [
"-c",
"echo 'hello world'> $out; echo $toto >> $out"
],
"env": {
"out": "/nix/store/6y8zi6npfciizkml15c99hqamiw05r4d-alamano",
"toto": "/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto"
}
}
nix derivation add < drv3.json
/nix/store/ivwizaslbcg7s9rc2in0aq6zp10kh2af-alamano.drv
nix path-info --json $drv | jq .
{
"/nix/store/ivwizaslbcg7s9rc2in0aq6zp10kh2af-alamano.drv": {
"ca": "text:sha256:0fajbhpm4vlbdwii3k9l872z3b98mgbdhll5mvxy4fc3c6l9sf4g",
"deriver": null,
"narHash": "sha256-Qm7VaRLdzHuucJwARxI85+qCHhQ1bETJAPrEtu2M5qY=",
"narSize": 448,
"references": [
"/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto"
],
"registrationTime": 1747929826,
"signatures": [],
"ultimate": false
}
}
=> on a un bien un objet du store qui en référence un autre !
Construire une dérivation
nix-build
- évalue le builder
- génère de nouveaux objets dans le store
- par défaut crée un lien
resultvers le résultat
❯ nix-build /nix/store/fbxzr0anq9qd8x1m1gvan2zflis6c452-alamano.drv this derivation will be built: /nix/store/fbxzr0anq9qd8x1m1gvan2zflis6c452-alamano.drv building '/nix/store/fbxzr0anq9qd8x1m1gvan2zflis6c452-alamano.drv'... /nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano ❯ cat /nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano hello world ❯ cat result hello world
À vous
- Faites marcher l’exemple précédent sur votre machine. En fonction de votre système, il faudra modifier : suivez les messages d’erreur
- ajoutez dans le store un fichier de votre choix, et utiliser le dans le builder. Pour cela, ajoutez une variable d’environnement.
- Construisez la dérivation ainsi obtenue et vérifier le résultat
{
"name": "alamano",
"outputs": {
"out": {
"path": "/nix/store/fy9v0v14x316ylh5jww2d780ppqhwr96-alamano"
}
},
"inputSrcs" : ["/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto"],
"inputDrvs" : {},
"system": "x86_64-linux",
"builder": "/bin/sh",
"args": [
"-c",
"echo $toto > $out"
],
"env": {
"out": "/nix/store/fy9v0v14x316ylh5jww2d780ppqhwr96-alamano",
"toto": "/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto"
}
}
Notion à retenir
- objets du store : des fichiers
- chaque objet a un chemin ~ hash(contenu, depenpances directes, nom)
=> similaire au modèle objet de git
- derivation : un objet enrobant un builder et une liste de dépendances
Bilan store/derivation
Il faut un langage au-dessus du store et des derivations :
- automatiser la gestion des hash
- gérer automatiquement les dépendances
- enrober la création d’une dérivation
- et assurer qu’elles sont correctes
Transition avec la suite : une derivation est l’objet de base du langage nix
instancier une dérivation : créer le drv dans le store
réaliser/bâtir : exécuter le builder défini dans la dérivation, construire dans le store les objets
faire à la main c’est bien, mais un peu fastidieux
nix comme gestionnaire de paquetages
nix ne gère que le contenu de /nix
comment un utilisateur peut-il utiliser des logiciels du store ?
- en utilisant le
PATHcomplet - automatiquement par un profil : un paquetage spécifique qui va contenir des liens vers d’autres du store nix
❯ which bash /home/gamba/.nix-profile/bin/bash ❯ realpath ~/.nix-profile /nix/store/q5p0pjdkzv81prxy6s1vqd65rsqjbcph-profile ❯ ls -d ~/.nix-profile lrwxrwxrwx - gamba 29 août 2022 /home/gamba/.nix-profile -> /nix/var/nix/profiles/per-user/gamba/profile ❯ ls -ld /nix/var/nix/profiles/per-user/gamba/profile lrwxrwxrwx - gamba 18 mai 17:14 /nix/var/nix/profiles/per-user/gamba/profile -> profile-610-link ❯ ls -ld /nix/var/nix/profiles/per-user/gamba/profile-610-link lrwxrwxrwx - gamba 18 mai 17:14 /nix/var/nix/profiles/per-user/gamba/profile-610-link -> /nix/store/q5p0pjdkzv81prxy6s1vqd65rsqjbcph-profile
Modification du profil courant
❯ hello
The program 'hello' is currently not installed. It is provided by
several packages. You can install it by typing one of the following:
nix profile install nixpkgs#haskellPackages.hello.out
nix profile install nixpkgs#mbedtls.out
nix profile install nixpkgs#mbedtls_2.out
nix profile install nixpkgs#hello.out
nix profile install nixpkgs#fyne.out
nix profile install nixpkgs#fltk14.bin
nix profile install nixpkgs#fltk.bin
Or run it once with:
nix shell nixpkgs#haskellPackages.hello.out -c hello ...
nix shell nixpkgs#mbedtls.out -c hello ...
nix shell nixpkgs#mbedtls_2.out -c hello ...
nix shell nixpkgs#hello.out -c hello ...
nix shell nixpkgs#fyne.out -c hello ...
nix shell nixpkgs#fltk14.bin -c hello ...
nix shell nixpkgs#fltk.bin -c hello ...
❯ nix profile install nixpkgs#hello ❯ hello Bonjour, le monde ! ❯ which hello /home/gamba/.nix-profile/bin/hello ❯ realpath $(which hello) /nix/store/lz9gfg6iybsh0hiignpk55w99a3bj4vb-hello-2.12.1/bin/hello
exploration des liens :
~/.nix-profile -> /nix/var/nix/profiles/per-user/gamba/profile # ~/.local/state/nix/profiles/profile -> profile-612-link -> /nix/store/4z8abywmnbi2spz057j266178gw4iscj-profile
❯ nix profile remove hello removing 'flake:nixpkgs#legacyPackages.x86_64-linux.hello' ❯ which hello which: no hello in (/home/gamba/.nix-profile/bin:/home/gamba/.nix-profile/bin:/home/gamba/.nix-profile/bin:/run/wrappers/bin:/home/gamba/.local/share/flatpak/exports/bin:/var/lib/flatpak/exports/bin:/home/gamba/.nix-profile/bin:/home/gamba/.local/state/nix/profile/bin:/home/gamba/.local/state/nix/profile/bin:/etc/profiles/per-user/gamba/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin) ❯ /nix/store/lz9gfg6iybsh0hiignpk55w99a3bj4vb-hello-2.12.1/bin/hello Bonjour, le monde ! # le paquetage n'a pas été supprimé du store
Gestion des versions
Chaque modification du profil est historisée.
nix profile history nix profile rollback nix profile rollback --to N
Tester sans modifier son profil
nix run nixpkgs#hello # un raccourci bien pratique avec un cache nix profile install nixpkgs#comma nix run 'nixpkgs#nix-index' # indexe tous les binaires de nixpkgs ! , hello
Ouvrir un shell temporaire
Pour rendre plusieurs commandes disponibles pour une session de travail, sans modifier son profil.
nix shell nixpkgs#bat nixpkgs#fzf
TP
À vous :
- cherchez un logiciel sur https://search.nixos.org, par ex cowsay
- installer le dans votre profil
- tester le logiciel :
cowsay Nix is good - trouver le vrai chemin complet :
realpath $(which cowsay) - faites un rollback : le logiciel devrait disparaître de votre PATH
- mais il est toujours dans le store !
nix-collect-garbage
nettoie du store les objets qui ne sont plus référencés
nix-collect-garbage # le paquetage installé devrait disparaitre
Les références racines =~ profils utilisateurs
valeur initiale des profils : profil de root
nix comme gestionnaires de paquetages
- opération déléguables à l’utilisateur
- caches binaires : si un objet n’est pas dans le store local, on essaye de le trouver dans le cache, et sinon on le construit
- les manipulations de base ne nécessitent pas de connaissances sur le langage nix
nix : le langage
Le langage nix est un petit langage spécifique, dont les seuls effets possibles sont :
- définir des dérivations
- ajouter des fichiers au store
Nix, les bases
Pour faire l’équivalent de notre dérivation manuelle
{
"name": "alamano",
"outputs": {
"out": {
"path": "/nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano"
}
},
"inputSrcs" : ["/nix/store/w1adwykj2bs1m0h2cy7np5vnh7cyzpsf-toto"],
"inputDrvs" : {},
"system": "x86_64-linux",
"builder": "/bin/sh",
"args": [
"-c",
"echo 'hello world' > $out"
],
"env": {
"out": "/nix/store/51jp26dmb74zjy7sq7kz86zfimbjm9rh-alamano",
}
}
nix repl
On utilise la repl de nix
derivationest une fonction primitive qui prend unSet- cela ajoute un fichier au store contenant la dérivation
- on peut alors réaliser la dérivation avec nix-build ou la repl
# nix repl nix-repl> dset = { name = "alamano"; system = builtins.currentSystem; builder = "/bin/sh"; args = [ "-c" "echo 'hello world' > $out" ]; } nix-repl> d = derivation dset nix-repl> :t d # :t quel est le type de ... ? a set nix-repl> d.type "derivation" nix-repl> d «derivation /nix/store/zcd1ilmz0bnxsn5rjaphj33c6dydcf9c-alamano.drv» nix-repl> :b d # idem nix-build # try do download from cahe 'https://cache.nixos.org/ll8b29blrqzf2ppm9v8z6hgq54wfcslc.narinfo' This derivation produced the following outputs: out -> /nix/store/ll8b29blrqzf2ppm9v8z6hgq54wfcslc-alamano
Set/String/Identifiant
# !! attention au ; { clef = ... ; autre = ... ; } a = 42; # a est un identifiant
builtins contient la librairie des fonctions intégrés à l’interpréteur nix
builtins.currentSystem : la chaîne qui décrit le système local, e.g. "x86_64-linux"
Observer le contenu d’une dérivation
nix-repl> builtins.attrNames d [ "all" "args" "builder" "drvAttrs" "drvPath" "name" "out" "outPath" "outputName" "system" "type" ] nix-repl> :doc builtins.attrNames Synopsis: builtins.attrNames set Return the names of the attributes in the set set in an alphabetically sorted list. For instance, builtins.attrNames { y = 1; x = "foo"; } evaluates to [ "x" "y" ].
Gérer les dépendances
nix-repl> :t <nixpkgs> a path nix-repl> <nixpkgs> /nix/store/sqv0la3aklw5ygfikrn8gicwfgz52jzi-source nix-repl> :t import <nixpkgs> a function nix-repl> pkgs = import <nixpkgs> {} # on évalue la fonction # =(import <nixpkgs>){} nix-repl> :t pkgs a set nix-repl> :t pkgs.bash a set nix-repl> :t pkgs.bash.type a string nix-repl> pkgs.bash.type "derivation" nix-repl> "${pkgs.bash}" "/nix/store/41pi0jiyvlhxgpfhxzvb44w1skc8yp4z-bash-interactive-5.2p37" nix-repl> "${pkgs.bash}/bin/bash" "/nix/store/41pi0jiyvlhxgpfhxzvb44w1skc8yp4z-bash-interactive-5.2p37/bin/bash"
Une «vraie» dérivation
Un script bash sera le builder :
cat <<"EOF" > builder.sh declare -xp echo foo > $out EOF chmod +x builder.sh
pkgs = import <nixpkgs> {} :t ./builder.sh # a path d = derivation { name = "foo"; builder = "${pkgs.bash}/bin/bash"; args = [ ./builder.sh ]; system = builtins.currentSystem; } # /nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv :b d This derivation produced the following outputs: out -> /nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo
❯ nix path-info --json /nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv | jq .
{
"/nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv": {
"ca": "text:sha256:0dlymy9zj7sli8w14500w3abbhsaq4qwcfg4nm2nc6i3df4c02na",
"deriver": null,
"narHash": "sha256-NCOgJZpEejWyLopnVj+wFe1Iabf7egcSHGlizfUBwLY=",
"narSize": 680,
"references": [
"/nix/store/fg8j7klr6mm1ki4qiq5a3fk5swp42b5f-bash-interactive-5.2p37.drv",
"/nix/store/rkss115vqiwnbc19ksgwsn81v5c8c6pw-builder.sh"
],
"registrationTime": 1747795069,
"signatures": [],
"ultimate": false
}
}
❯ nix derivation show /nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv
{
"/nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv": {
"args": [
"/nix/store/rkss115vqiwnbc19ksgwsn81v5c8c6pw-builder.sh"
],
"builder": "/nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37/bin/bash",
"env": {
"builder": "/nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37/bin/bash",
"name": "foo",
"out": "/nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo",
"system": "x86_64-linux"
},
"inputDrvs": {
"/nix/store/fg8j7klr6mm1ki4qiq5a3fk5swp42b5f-bash-interactive-5.2p37.drv": {
"dynamicOutputs": {},
"outputs": [
"out"
]
}
},
"inputSrcs": [
"/nix/store/rkss115vqiwnbc19ksgwsn81v5c8c6pw-builder.sh"
],
"name": "foo",
"outputs": {
"out": {
"path": "/nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo"
}
},
"system": "x86_64-linux"
}
}
Path
le langage nix a un type spécifique pour les chemins de fichiers
- c’est une valeur de base
- en sous-main, le fichier est ajouté au store, et la vraie valeur gérée est le chemin dans le store
nix-repl> :t ./builder.sh a path
Environnement d’exécution
Notre builder nous liste tout l’environnement :
❯ nix log /nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo declare -x HOME="/homeless-shelter" declare -x NIX_BUILD_CORES="0" declare -x NIX_BUILD_TOP="/build" declare -x NIX_LOG_FD="2" declare -x NIX_STORE="/nix/store" declare -x OLDPWD declare -x PATH="/path-not-set" declare -x PWD="/build" declare -x SHLVL="1" declare -x TEMP="/build" declare -x TEMPDIR="/build" declare -x TERM="xterm-256color" declare -x TMP="/build" declare -x TMPDIR="/build" declare -x builder="/nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37/bin/bash" declare -x name="foo" declare -x out="/nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo" declare -x system="x86_64-linux"
Reproductibilité
- build en isolation complète du reste du système
- les sources et les dépendances sont accessibles dans le store par une variable d’environnement
Utiliser un fichier .nix
# default.nix let pkgs = import <nixpkgs> {}; in derivation { name = "foo"; builder = "${pkgs.bash}/bin/bash"; args = [ ./builder.sh ]; system = builtins.currentSystem; }
❯ nix-build default.nix this derivation will be built: /nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv building '/nix/store/l6k4xzf478svf6nl6k67w73x9dydcvgy-foo.drv'... declare -x HOME="/homeless-shelter" declare -x NIX_BUILD_CORES="0" declare -x NIX_BUILD_TOP="/build" declare -x NIX_LOG_FD="2" declare -x NIX_STORE="/nix/store" declare -x OLDPWD declare -x PATH="/path-not-set" declare -x PWD="/build" declare -x SHLVL="1" declare -x TEMP="/build" declare -x TEMPDIR="/build" declare -x TERM="xterm-256color" declare -x TMP="/build" declare -x TMPDIR="/build" declare -x builder="/nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37/bin/bash" declare -x name="foo" declare -x out="/nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo" declare -x system="x86_64-linux" /nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo cat /nix/store/slnmn2kjnf1pkzqjnw34za872l4j34fx-foo foo
let … in
let ... in permet d’introduire des identifiants dans l’expression après le in
Modifier l’environnement du builder
rajouter des valeurs dans le set de la dérivation ajoute une variable d’environnement
On ajoute un répertoire local
# ssg-src.nix let pkgs = import <nixpkgs> {}; in derivation { name = "foo"; builder = "${pkgs.bash}/bin/bash"; args = [ ./builder.sh ]; system = builtins.currentSystem; src = ./content; }
❯ nix-build ssg-src.nix this derivation will be built: /nix/store/1jihnh9afp50vf4pbiq0r3ii4f82dy4v-foo.drv building '/nix/store/1jihnh9afp50vf4pbiq0r3ii4f82dy4v-foo.drv'... declare -x HOME="/homeless-shelter" ... declare -x builder="/nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37/bin/bash" declare -x name="foo" declare -x out="/nix/store/855m8n1si9ddn0v28pm6hpvbz4dhk68v-foo" declare -x src="/nix/store/2w43qdqspr50g24ijz3w1a5d7ccmdisq-content" declare -x system="x86_64-linux"
on voit apparaître la variable d’environnement src
TP
modifiez le builder :
# builder-content.sh declare -xp echo loop over $src txt files for content in $src/*.txt; do cat $content >> $out done
Ajoutez des fichiers txt dans le répertoire ./content
Reconstruisez notre paquetage foo :
nix-build ssg-src.nix declare -x HOME="/homeless-shelter" declare -x PATH="/path-not-set" loop over /nix/store/2w43qdqspr50g24ijz3w1a5d7ccmdisq-content txt files /nix/store/9b7aqryar3mvwyyy6415l728npkb4kvc-builder-content.sh: line 3: cat: command not found /nix/store/9b7aqryar3mvwyyy6415l728npkb4kvc-builder-content.sh: line 3: cat: command not found
build en isolation : tout ce qui n’est pas explicitement listé n’est pas accessible
ajouter des dépendances : on ajoute une variable d’environnement correspondant au paquetage
trouver cat : https://search.nixos.org -> coreutils
# out-from-files.nix let pkgs = import <nixpkgs> {}; in derivation { name = "boo"; builder = "${pkgs.bash}/bin/bash"; args = [ ./builder-content.sh ]; system = builtins.currentSystem; src = ./content; coreutils = pkgs.coreutils; }
declare -xp echo loop over $src txt files for content in $src/*.txt; do $coreutils/bin/cat $content >> $out done
nix-shell
nix-shell crée un shell à partir d’une derivation correcpondant à l’environnement du builder
cela permet de faire à la main les opérations du builder
c’est très utile pour mettre un builder au point
nix-shell --pure out-from-files.nix echo $coreutils /nix/store/87fck6hm17chxjq7badb11mq036zbyv9-coreutils-9.7 echo $builder /nix/store/1q9lw4r2mbap8rsr8cja46nap6wvrw2p-bash-interactive-5.2p37/bin/bash echo $out /nix/store/jvxjqg9hk92pay83la5drkhp1frhd3b2-boo $builder -c ./builder-content.sh # permission denied : seul root et le daemon nix peuvent écrire sur le store export out=/tmp/test-boo $builder -c ./builder-content.sh $coreutils/bin/cat $out content to be displayed
nix : les fonctions
Rajouter des attributs dans la dérivation pour gérer les dépendances revient souvent. On va paramétrer cette pratique.
=> écrire une fonction en nix
nix-repl> x: x*2 «lambda @ «string»:1:1» nix-repl> double = x: x*2 nix-repl> double «lambda @ «string»:1:2» nix-repl> double 21 42
- fonction anonyme à un seul argument
- appel en donnant l’argument sans parenthèses
fonction nix : plusieurs paramètres ?
nix-repl> mul = a: b: a*b # mul = a: (b: a*b) nix-repl> mul «lambda» nix-repl> mul 3 # application partielle «lambda» nix-repl> (mul 3) 4 12
classique en programmation fonctionnelle :
- curryfication : on construit les fonctions à plusieurs arguments à partir des fonctions à un seul argument
- application
Application
# out-from-files.nix let pkgs = import <nixpkgs> {}; in derivation { name = "boo"; builder = "${pkgs.bash}/bin/bash"; args = [ ./builder-content.sh ]; system = builtins.currentSystem; src = ./content; coreutils = pkgs.coreutils; }
# builderFunc.nix pkgs: attrs: let defaultAttrs = { # name = "boo"; builder = "${pkgs.bash}/bin/bash"; args = [ ./builder-content.sh ]; system = builtins.currentSystem; # src = ./content; # coreutils = pkgs.coreutils; }; realAttrs = defaultAttrs // attrs; in derivation realAttrs
set1 // set2 : donne un nouveau set avec le contenu de set1 auquel on ajoute set2
# default.nix let pkgs = import <nixpkgs> { }; mkDerivation = import ./builderFunc.nix pkgs; in mkDerivation { name = "boo"; src = ./content; inherit (pkgs) coreutils; # coreutils = pkgs.coreutils; }
nix-build # default.nix
TP
Notre builder actuel utilise cat.
Changer le pour utiliser banner :
for content in $src/*.txt; do $banner/bin/banner $($coreutils/bin/cat $content) >> $out done
# default.nix let pkgs = import <nixpkgs> { }; mkDerivation = import ./builderFunc.nix pkgs; in mkDerivation { name = "boo"; src = ./content; inherit (pkgs) coreutils banner; }
Rajouter un attribut indiquant le programme à utiliser
# builderFunc.nix pkgs: attrs: let defaultAttrs = { builder = "${pkgs.bash}/bin/bash"; args = [ ./builder-content.sh ]; system = builtins.currentSystem; }; realAttrs = defaultAttrs // attrs; in derivation realAttrs
# default.nix let pkgs = import <nixpkgs> { }; mkDerivation = import ./builderFunc.nix pkgs; in mkDerivation { name = "boo"; src = ./content; inherit (pkgs) coreutils; cmd = "${pkgs.banner}/bin/banner"; }
for content in $src/*.txt; do $cmd $($coreutils/bin/cat $content) >> $out done
# default.nix let pkgs = import <nixpkgs> { }; mkDerivation = import ./builderFunc.nix pkgs; in { bannerboo = mkDerivation { name = "bannerBoo"; src = ./content; inherit (pkgs) coreutils; cmd = "${pkgs.banner}/bin/banner"; }; cowsayboo = mkDerivation { name = "cowsayBoo"; src = ./content; inherit (pkgs) coreutils; cmd = "${pkgs.cowsay}/bin/cowsay"; }; }
nix-build construit tous les attributs
pour restreindre : nix-build -AbannerBoo
nix : environnement standard
On utilise un wrapper au-dessus de derivation :
pkgs.stdenv.mkDerivation bien expliqué dans nix-pills#19
Le builder associé comporte plusieurs phases définies dans un seul script shell setup.sh. De nombreux hooks permettent d’adapter le comportement.
Cet environnement sert de base à des versions spécialisés pour un langage, mais également à des versions «amoindries» appelées trivial builders.
pkgs.writeShellScriptBin
{writeShellScriptBin, banner, cowsay, moo ? false}:
let
cmd = if moo then "${cowsay}/bin/cowsay"
else "${banner}/bin/banner";
name = if moo then "moo" else "boo";
in
writeShellScriptBin name ''
echo do not use ${cowsay}
${cmd} $1
''
let pkgs = import <nixpkgs> { }; in { boo = pkgs.callPackage ./boo.nix { }; moo = pkgs.callPackage ./boo.nix { moo = true;}; }
nix flake
Reproductible, mais …
pkgs = import <nixpkgs> { }; # nixpkgs du système
les flakes sont une réponse à ce problème, et permettent de distribuer du code à partir d’un dépôt git.
Mais :
- commande expérimentale standardisée par défaut
Exemple
# flake.nix { description = "Boo et Moo flake"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; outputs = { self, nixpkgs }: let systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); in { packages = forAllSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; boo = pkgs.callPackage ./boo.nix { }; moo = pkgs.callPackage ./boo.nix { moo = true; }; in { boo = boo; moo = moo; default = boo; } ); }; }
Utilisation
# Pour construire boo nix build .#boo # Pour construire moo nix build .#moo # Pour exécuter boo (en utilisant le chemin du binaire) ./result/bin/boo "texte" # Pour exécuter moo (en utilisant le chemin du binaire) nix build .#moo && ./result/bin/moo "texte"