https://lafor.ge/feed.xml

Partie 3 : Première utilisation de Nixpkgs

2023-04-19
Les articles de la série

Bonjour à toutes et tous! 😀

Troisième article sur Nix.

Nixpkgs

Dans la partie 2, nous avons réalisé une dérivation, mais elle ne fait quasiment rien, et surtout pas ce que l'on veut.

C'est à dire compiler du C.

On a besoin de plus! On a besoin d'un compilateur! On a besoin de gcc.

Et donc de sa dérivation ou au moins de son paquet.

Pour cela nous allons appeler repl différemment.

Nous rajoutons un argument --file '<nixpkgs>.

$ nix repl --file '<nixpkgs>'
Welcome to Nix 2.15.0. Type :? for help.

Loading installable ''...
Added 18779 variables.

Ah! Il y a du changement, il nous dit qu'il a chargé plein de variables.

Le '<nixpkgs>' est un alias pour dire d'aller chercher toutes les dérivations de nixpkgs, le repo officiel de Nix.

Donc maintenant dans votre repl, vous avez accès à toutes les dérivations qui ont pu être faites par la communautée.

Exemple, gcc.

Lorsque l'on a fait un

$ nix-shell -p gcc

On a dans les faits déclaré un environnement où le paquet "gcc" était disponible.

Dans le repl, on peut faire de même, la variable qui nous intéresse est pkgs, elle contient toutes les dérivations. Dont celle de gcc.

nix-repl> pkgs.gcc
«derivation /nix/store/wfb8f6nsrnfhpw7ya1dwj3k9d4vx6li1-gcc-wrapper-12.2.0.drv»

On peut d'ailleurs aller voir à quoi elle ressemble.

$ nix derivation show /nix/store/wfb8f6nsrnfhpw7ya1dwj3k9d4vx6li1-gcc-wrapper-12.2.0.drv

{
  "/nix/store/wfb8f6nsrnfhpw7ya1dwj3k9d4vx6li1-gcc-wrapper-12.2.0.drv": {
    "args": [
      "-e",
      "/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh"
    ],
    "name": "gcc-wrapper-12.2.0",
    "builder": "/nix/store/dsn6vl7x1hbn1akgpxync19gpx2dzy8w-bootstrap-tools/bin/bash",
    "env": {
      "out" : "/nix/store/nlgyw2fv0cm8rkz8qm1jyw78vyif1bl9-gcc-wrapper-12.2.0",
      ...
    }
    "system": "x86_64-linux",
    ...
  }
}

( J'ai tronqué plein de champ, sinon c'était trop long )

Mais comme vous pouvez le voir, ça reste une dérivation "classique".

Builder avec gcc

Testons d'appeler gcc dans notre builder.

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    gcc --version > $out         
    ''
  ];
}
error: builder for '/nix/store/bfrw7x8b09r53fkhmif89jslcv0zr0fp-hello-world.drv' failed with exit code 127;
       last 1 log lines:
       > sh: gcc: not found
       For full logs, run 'nix-store -l /nix/store/bfrw7x8b09r53fkhmif89jslcv0zr0fp-hello-world.drv'.

Arf! 😩

Bon petite expérience, où est gcc sur le disque?

$ nix-shell -p gcc
[nix-shell:/dir]$ which gcc
/nix/store/nlgyw2fv0cm8rkz8qm1jyw78vyif1bl9-gcc-wrapper-12.2.0/bin/gcc

Ok, donc c'est du "$out/bin/gcc". Très bien.

Retour au repl.

nix-repl > "${pkgs.gcc}"
[nix-shell:~/Documents/Workshop/nix/hello]$ which gcc
/nix/store/nlgyw2fv0cm8rkz8qm1jyw78vyif1bl9-gcc-wrapper-12.2.0

Ok, donc interpoler une dérivation donne le chemin de sortie de cette dérivation

nix-repl > "${pkgs.gcc}/bin/gcc"
[nix-shell:~/Documents/Workshop/nix/hello]$ which gcc
/nix/store/nlgyw2fv0cm8rkz8qm1jyw78vyif1bl9-gcc-wrapper-12.2.0/bin/gcc

On peut également concaténer des chaîne de caratères. Maintenant le chemin est complet.

Plus qu'à l'introduire dans le builder et l'affaire est dans le sac.

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    ${pkgs.gcc}/bin/gcc --version > $out         
    ''
  ];
}

This derivation produced the following outputs:
  out -> /nix/store/kzwd92if64f115pvg46n7sdc1x2wp6ir-hello-world

nix-repl> :e /nix/store/kzwd92if64f115pvg46n7sdc1x2wp6ir-hello-world
gcc (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Et boom ! on a gcc ! 💥

Modifier le PATH

Oui mais c'est pas top, les scripts de build standards n'auront jamais ${pkgs.gcc}/bin/gcc seulement gcc.

Pour résoudre le problème, on doit ramener gcc dans le PATH du builder

On va introduire une nouvelle règle de la fonction derivation.

Qui dit que l'on peut générer des variables d'environnements comme bon nous semble.

Exemple:

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";
  value = "tata";     
  args = [                 
    "-c"                                                                                                                               
    ''                       
    echo $value > $out         
    ''
  ];
}

This derivation produced the following outputs:
  out -> /nix/store/l0zl8hhcpywsvzzgmyxzxjf61hyqwq4i-hello-world

nix-repl > :e /nix/store/l0zl8hhcpywsvzzgmyxzxjf61hyqwq4i-hello-world
tata

Nous ce que l'on veut définir c'est "$PATH".

Voyons son contenu.

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    echo $PATH > $out         
    ''
  ];
}

This derivation produced the following outputs:
  out -> /nix/store/lvjhj4zgqrvcq2sviwhxii5mw3hdali5-hello-world

nix-repl> :e /nix/store/lvjhj4zgqrvcq2sviwhxii5mw3hdali5-hello-world
/path-not-set

Bon, clairement nada ^^

On peut donc s'amuser.

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    gcc --version > $out         
    ''
  ];
  PATH = "${pkgs.gcc}/bin";
}

This derivation produced the following outputs:
  out -> /nix/store/48vlxdr5bhhcgsz7z4plhaxdsbz35i3m-hello-world

nix-repl> :e /nix/store/48vlxdr5bhhcgsz7z4plhaxdsbz35i3m-hello-world

gcc (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Et re-boom ! on a gcc et pas en format bizare ! 💥💥

Définir des fichiers

Maintenant que nous avons un compilateur, il nous faut des sources.

Nix propose dans sa librairie standard une fonction appelée toFile.

On va faire comme dans l'exemple mais au lieu de déclarer un builder que nous avons déjà, nous allons déclarer le main.c.

nix-repl> builtins.toFile "main.c" ''
  #include "stdio.h"

  void main() {
    printf("Hello World");
  }
  ''
"/nix/store/9vmwahqfcx6909widh6g9xr736in950q-main.c"

$ file /nix/store/9vmwahqfcx6909widh6g9xr736in950q-main.c
/nix/store/9vmwahqfcx6909widh6g9xr736in950q-main.c: C source, ASCII text

$ cat /nix/store/9vmwahqfcx6909widh6g9xr736in950q-main.c
#include "stdio.h"

void main() {
  printf("Hello World");
}

Ok, donc c'est aussi un chemin et dedans nous avons le main.c.

On peut alors définir une variable d'environnement $SOURCE qui contiendra ce chemin.

Puis modifier la commande gcc dans le builder pour compiler.

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    gcc $SOURCE -o $out         
    ''
  ];
  PATH = "${pkgs.gcc}/bin";
  SOURCE = builtins.toFile "main.c" ''
    #include "stdio.h"
  
    void main() {
      printf("Hello World");
    }
    '';
}

This derivation produced the following outputs:
  out -> /nix/store/m6pd92z8rm9a2ia3qxml096cc33z1g2l-hello-world

Bon, ça marche ^^

Voyons le résultat...

$ file /nix/store/m6pd92z8rm9a2ia3qxml096cc33z1g2l-hello-world
/nix/store/m6pd92z8rm9a2ia3qxml096cc33z1g2l-hello-world: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /nix/store/1n2l5law9g3b77hcfyp50vrhhssbrj5g-glibc-2.37-8/lib/ld-linux-x86-64.so.2, for GNU/Linux 3.10.0, not stripped

Ok, ça sent bon, c'est une executable.

Ben éxécutons-le!

$ /nix/store/m6pd92z8rm9a2ia3qxml096cc33z1g2l-hello-world
Hello World$

Cool! 😎

Bon, par contre le "$" attaché c'est pas trop.

Faisons une autre version.

nix-repl> :b derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "/bin/sh";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    gcc $SOURCE -o $out         
    ''
  ];
  PATH = "${pkgs.gcc}/bin";
  SOURCE = builtins.toFile "main.c" ''
    #include "stdio.h"
  
    void main() {
      printf("Hello World\n");
    }
    '';
}

This derivation produced the following outputs:
  out -> /nix/store/807y2c1zj9zz8p8b5ki0a99fkm0lw0w7-hello-world

$ /nix/store/807y2c1zj9zz8p8b5ki0a99fkm0lw0w7-hello-world
Hello World 
$

Ah, beaucoup mieux. 😃

Comme vous pouvez l'observez, une minuscule modification dans le main.c, a eu des répercussions dramatiques sur l'empreinte de la dérivation.

  • v1 : m6pd92z8rm9a2ia3qxml096cc33z1g2l-hello-world
  • v2 : 807y2c1zj9zz8p8b5ki0a99fkm0lw0w7-hello-world

C'est cela qui permet d'être certain de savoir ce que l'on manipule.

Conclusion

Dans cette partie, nous avons appris à créer une dérivation capable de compiler du C.

Cependant, nous n'avons pas tout fixé, seul nos sources sont déterministes.

Dans la partie 4 nous verrons comment rendre déterministe notre dérivation.

Merci de votre lecture ❤️

avatar

Auteur: Akanoa

Je découvre, j'apprends, je comprends et j'explique ce que j'ai compris dans ce blog.

Ce travail est sous licence CC BY-NC-SA 4.0.