Partie 5 : Externaliser les sources

Lecture 3 min. ‱


Table des matiĂšres

Les articles de la série

Bonjour à toutes et tous! 😀

CinquiĂšme article sur Nix.

Aujourd’hui nous allons utiliser des sources qui ne sont pas dans la dĂ©rivation, cela rendra notre dĂ©rivation plus souple.

Plus de sources !

Jusqu’à prĂ©sent notre dĂ©rivation ressemble Ă  ceci:

with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz") {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''                       
    gcc $SOURCE -o $out         
    ''
  ];
  PATH = "${pkgs.gcc}/bin";
  SOURCE = builtins.toFile "main.c" ''
    #include "stdio.h"
  
    void main() {
      printf("Hello World\n");
    }
    '';
}

Nous utilisons la varaible d’environnement “$SOURCE” pour stocker un chemin de fichier local que l’on gĂ©nĂšre directement Ă  partir de la fonction toFile.

C’est trĂšs bien si l’on a qu’un seul fichier mais si l’on commence Ă  faire grandir le projet ça va devenir de plus en plus compliquĂ© de suivre la cadence.

Exemple deux fichiers de sources nous donne:

with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz") {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''
    mkdir src
    cd src
    ln -s $SOURCE main.c
    ln -s $SOURCE2 hello.c                       
    gcc main.c -o $out         
    ''
  ];
  PATH = "${pkgs.gcc}/bin:${pkgs.coreutils}/bin";
  SOURCE = builtins.toFile "main.c" ''
    #include "stdio.h"
    #include "hello.c"
  
    void main() {
      printf("%s\n", hello());
    }
    '';
   SOURCE2 = builtins.toFile "hello.c" ''

    const char* hello() {
      return "Hello World";
    }
    '';
}

Et si on veut le faire de maniĂšre cannonique, il faut en plus rajouter un header.

with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz") {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''
    mkdir src
    cd src
    ln -s $SOURCE main.c
    ln -s $SOURCE2 hello.c
    ln -s $HEADER  hello.h                       
    gcc main.c hello.c -o $out         
    ''
  ];
  PATH = "${pkgs.gcc}/bin:${pkgs.coreutils}/bin";
  SOURCE = builtins.toFile "main.c" ''
    #include "stdio.h"
    #include "hello.h"
  
    void main() {
      printf("%s\n", hello());
    }
    '';
  HEADER = builtins.toFile "hello.h" ''
      const char* hello();   
    '';
  SOURCE2 = builtins.toFile "hello.c" ''
    #include "hello.h"

    const char* hello() {
      return "Hello World";
    }
    '';
}

Si en plus, nous voulons respecter les standards, on peut mĂȘme se crĂ©er le Makefile. 😇

Mais pour ce faire nous allons déporter nos sources.

Fetch

Pour tester cela, j’ai créé un projet sur github.

Nous avons appris de notre erreur, il nous faut quelque chose de stable dans le temps, de reproductible.

L’intĂ©rĂȘt de git est de crĂ©er des signatures uniques pour chaque modification. Nous allons utiliser cette propriĂ©tĂ© pour s’assurer de la stabilitĂ© de notre dĂ©rivation.

Sur github, il est possible de voir tous les commits d’une branche.

Et ainsi récupérer le SHA1 de ce commit.

On peut alors rĂ©cupĂ©rer les sources sous la forme d’une archive via une URL formatĂ©e comme suit:

https://github.com/<projet>/archive/<SHA1>.tar.gz

Mon commit est le 639841dcbc59ed24a461a9dadf6234073cdafad0.

Essayons de récupérer les sources, pour cela nous allons réutiliser la commande fetchTarball.

nix-repl> fetchTarball "https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz"
"/nix/store/83z8gfh3lmi8psikgv39fpl67awkg4fg-source"

$ ls -l /nix/store/83z8gfh3lmi8psikgv39fpl67awkg4fg-source
-r--r--r-- 1 root root   70 janv.  1  1970 hello.c
-r--r--r-- 1 root root   20 janv.  1  1970 hello.h
-r--r--r-- 1 root root 1295 janv.  1  1970 LICENSE
-r--r--r-- 1 root root   90 janv.  1  1970 main.c
-r--r--r-- 1 root root   42 janv.  1  1970 Makefile
-r--r--r-- 1 root root   83 janv.  1  1970 README.md

Magnifique, nous avons nos sources. 😃

Pour les construire, nous allons utiliser la méthode standard du Makefile.

Pour constuire les sources, il faut s’y dĂ©placer puis lancer la commande make.

Le Makefile ressemble Ă  ceci:

all: main.c 
	gcc main.c hello.c -o ${out}

On remarque le ${out} qui est directement défini par la dérivation. La sortie de compilation sera au bon endroit.

Nous pouvons créer la dérivation qui va bien.

nix-repl> :b with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz") {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''
    cd $SOURCE                   
    make        
    ''
  ];
  PATH = "${pkgs.gcc}/bin:${pkgs.coreutils}/bin:${pkgs.gnumake}/bin";
  SOURCE = fetchTarball "https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz";
}

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

$ /nix/store/mbx1qs39fgqiyfiikfx98gbs9vvqqxnm-hello-world
Hello world!

De cette maniĂšre les sources de la dĂ©rivations ne sont plus contenues par elle, mais dĂ©portĂ©es tout en conservant l’aspect de reproductibilitĂ©.

En parlant de reproductibilitĂ©, nous pouvons faire mieux. 😃

Checksum

Nix propose un outil nix-prefetch-url pour calculer la somme de contrĂŽle ou checksum de ce que l’on tĂ©lĂ©charge.

$ nix-prefetch-url --unpack https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz
path is '/nix/store/livh28m4dpk96q347s8j17fv2k5drb8g-639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz'
0pvjw2kf0fwv569j2caqc983qk3x78znj52brr1a0av8f2c8rwjl

Attention!

L’argument --unpack est important, car sinon c’est la signature n’est pas correcte, en effet le fetchTarball procĂšde au dĂ©sarchivage du tar.gz aprĂšs son tĂ©lĂ©chargement.

Sans --unpack on calcule la signature de l’archive pas de son contenu

$ nix-prefetch-url https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz
path is '/nix/store/qi5nmqi9np2jbjw4f52zakr10pa6j4pr-639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz'
1wh3vf4ydvvxyiw18wwdyr1xgcqpxnlrl0wl2wcjsysflbrxlhgp

D’ailleurs, si l’on utilise le mauvais checksum:

nix-repl> :b with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz") {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''
    cd $SOURCE                   
    make        
    ''
  ];
  PATH = "${pkgs.gcc}/bin:${pkgs.coreutils}/bin:${pkgs.gnumake}/bin";
  SOURCE = fetchTarball {
    url = "https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz";
    sha256 = "1wh3vf4ydvvxyiw18wwdyr1xgcqpxnlrl0wl2wcjsysflbrxlhgp";
  };
}


error: hash mismatch in file downloaded from 'https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz':
  specified: sha256:1wh3vf4ydvvxyiw18wwdyr1xgcqpxnlrl0wl2wcjsysflbrxlhgp
  got:       sha256:0pvjw2kf0fwv569j2caqc983qk3x78znj52brr1a0av8f2c8rwjl

Le systĂšme signale une incohĂ©rence entre le checksum calculĂ© d’une part et le checksum spĂ©cifiĂ©, d’autre part.

Si le SHA1 correspond, le build est possible.

nix-repl> :b with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz") {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''
    cd $SOURCE                   
    make        
    ''
  ];
  PATH = "${pkgs.gcc}/bin:${pkgs.coreutils}/bin:${pkgs.gnumake}/bin";
  SOURCE = fetchTarball {
    url = "https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz";
    sha256 = "0pvjw2kf0fwv569j2caqc983qk3x78znj52brr1a0av8f2c8rwjl";
  };
}

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

Et on peut faire de de mĂȘme avec le nixpkgs.

$ nix-prefetch-url --unpack https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz
1xk1f62n00z7q5i3pf4c8c4rlv5k4jwpgh0pqgzw1l40vhdkixk9

On peut alors fixé également spécifier le SHA1 du nixpkgs dans la dérivation.

with import (fetchTarball {
  url = "https://github.com/NixOS/nixpkgs/archive/release-21.11.tar.gz";
  sha256 = "1xk1f62n00z7q5i3pf4c8c4rlv5k4jwpgh0pqgzw1l40vhdkixk9";
}) {};
derivation {          
  name = "hello-world";    
  system = "x86_64-linux";
  builder = "${pkgs.bash}/bin/bash";   
  args = [                 
    "-c"                                                                                                                               
    ''
    cd $SOURCE                   
    make        
    ''
  ];
  PATH = "${pkgs.gcc}/bin:${pkgs.coreutils}/bin:${pkgs.gnumake}/bin";
  SOURCE = fetchTarball {
    url = "https://github.com/Akanoa/nix-hello/archive/639841dcbc59ed24a461a9dadf6234073cdafad0.tar.gz";
    sha256 = "0pvjw2kf0fwv569j2caqc983qk3x78znj52brr1a0av8f2c8rwjl";
  };
}

LĂ  on commence Ă  avoir quelque chose qui a de la gueule! đŸ€©

Ainsi, mĂȘme si les donnĂ©es pointĂ©e par l’URL changent, nous sommes capable de le dĂ©tecter et d’annuler la rĂ©alisation de la dĂ©rivation.

Ce mĂ©canisme permet de s’assurer que les sources n’ont pas Ă©tĂ© corrompues d’une maniĂšre ou d’une autre.

Conclusion

Nous commençons Ă  nous rapprocher d’une dĂ©rivation qui est utilisable pour de vrais projets.

Dans la partie 6, nous verrons comment construire des projets plus normalisés via mkDerivation.

Merci de votre lecture ❀