Alire, le package manager d'Ada

28
1
juin
2022
Ada

Dans un précédent journal, il m’a été demandé de présenter Alire sous la forme d’une dépêche. Voilà, j’ai fini par prendre le temps et j’ai choisi la façon de le présenter.
Je vous propose donc une sorte de pas-à-pas plutôt qu’une explication un peu brutale

Le but

Nous allons donc créer des crates Alire (les Rustistes auront reconnu le terme).
J’ai bien dit des car cela va permettre de voir les deux types, lib ou bin que l’on peut créer.

Le projet

Le but n’étant pas d’écrire du code Ada, on va prendre un de mes projets préférés qui n’a, au moment de l’écriture de ces lignes, pas de crate associée.
J’ai nommé le Corporate Bullshit Generator, logiciel ô combien indispensable, source inépuisable d’études en scalabilité (autopromotion).

Ce magnifique projet est l’œuvre de Gautier de Montmollin qui produit aussi des choses beaucoup plus sérieuses comme Zip-Ada, une lib pour gérer les archives zip, Ada PDF Writer ou HAC, un compilateur Ada partiel.
Évidemment, tout est codé en pur Ada.

Bon assez causé, on y va direct car c’est NOTRE PROJEEEEETTTT :)

Sommaire

C’est parti avec les prérequis

Installation d’Alire

Je ne vais pas paraphraser totalement la doc mais il vous faut installer un compilateur Ada sur votre distribution, histoire d’avoir les bibliothèques du langage dans votre système.
Ensuite, on télécharge le binaire et on le met dans son PATH pour être tranquille.
Les courageux pourront construire à partir des sources.

Premiers pas

Là, encore, je vais faire plus court que la doc officielle.
On va juste vérifier que notre installation fonctionne bien.

fred@tatooine:~/Dev/Ada$ alr get hello
ⓘ Deploying release hello=1.0.1...
-=O=-        #       #         #         #
ⓘ Deploying release libhello=1.0.0...
############################################################################################################################################################ 100,0%

hello=1.0.1 successfully retrieved.
Dependencies were solved as follows:

   + libhello 1.0.0 (new)

On va ensuite dans le répertoire créé (aujourd’hui, hello_1.0.1_dcc36a2f) et on exécute :

fred@tatooine:~/Dev/Ada$ alr run
Welcome to the toolchain selection assistant

In this assistant you can set up the default toolchain to be used with any crate
that does not specify its own top-level dependency on a version of gnat or
gprbuild.

If you choose "None", Alire will use whatever version is found in the
environment.

ⓘ gnat is currently not configured. (alr will use the version found in the environment.)

Please select the gnat version for use with this configuration
  1. gnat_native=11.2.4
  2. None
  3. gnat_external=2021.0.0 [Detected at /opt/gnat/2021/bin/gnat]
  4. gnat_arm_elf=11.2.4
  5. gnat_avr_elf=11.2.4
  6. gnat_riscv64_elf=11.2.4
  7. gnat_arm_elf=11.2.3
  8. gnat_native=11.2.3
  9. gnat_riscv64_elf=11.2.3
  0. gnat_arm_elf=11.2.2
  a. (See more choices...)
Enter your choice index (first is default):
> 3
ⓘ Selected tool version gnat_external=2021.0.0

ⓘ Choices for the following tool are narrowed down to releases compatible with just selected gnat_external=2021.0.0

ⓘ gprbuild is currently not configured. (alr will use the version found in the environment.)

Please select the gprbuild version for use with this configuration
  1. gprbuild=2021.0.0 [Detected at /opt/gnat/2021/bin/gprbuild]
  2. None
Enter your choice index (first is default):
> 1
ⓘ Selected tool version gprbuild=2021.0.0
Setup
   [mkdir]        object directory for project Libhello
   [mkdir]        library directory for project Libhello
   [mkdir]        object directory for project Hello
Compile
   [Ada]          hello.adb
   [Ada]          libhello.adb
Build Libraries
   [gprlib]       hello.lexch
   [archive]      libhello.a
   [index]        libhello.a
Bind
   [gprbind]      hello.bexch
   [Ada]          hello.ali
Link
   [link]         hello.adb
Hello, world!

Mais alors, il s’est passé quoi ?
Normalement, si c’est votre première installation, comme ci-dessus, Alire va vous demander de choisir votre toolchain en fonction de ce qui se trouve dans votre système.
Personnellement, j’ai, entre autres, la version 2021 du compilateur fourni par Adacore dans /opt/gnat.
Ensuite, le binaire n’étant pas encore créé, on passe par un build puis une exécution.

Qu’on se rassure, le processus de choix de la toolchain ne se fait qu’une fois, mais il est possible de changer en cours de route via les commandes alr toolchain.

De manière générale, à l’instar de beaucoup d’autres outils, on peut utiliser l’aide en ligne via alr help comme alr help toolchain.

Maintenant que l’on voit que cela fonctionne, on passe aux choses sérieuses.

Récupération du code du CBSG sur SF

Le code du Corporate Bullshit Generator (CBSG pour les intimes) est disponible sur Sourceforge et possède un miroir sur Github

On récupère soit via

svn checkout https://svn.code.sf.net/p/cbsg/code/ cbsg-code

soit via

git clone https://github.com/zertovitch/cbsg.git cbsg-code

L’un ou l’autre ne pose aucun problème, cela ne servira qu’à piocher dans le code source.

Préparons maintenant notre première crate, une lib.

Notre première crate, une bibliothèque

On commence par créer un patron de crate

fred@tatooine:~/Dev/Ada$ alr init --lib cbsg

Cela donne la hiérarchie suivante:

cbsg
├── alire.toml
├── cbsg.gpr
├── config
│   ├── cbsg_config.ads
│   ├── cbsg_config.gpr
│   └── cbsg_config.h
└── src
    └── cbsg.ads

Quand on analyse le code du Corporate Bullshit Generator, on voit qu’un package fournit la fonctionnalité que l’on veut exposer.
En effet, corporate_bullshit est le package générique donnant les fonctions retournant les phrases tant attendues.

generic
   Paragraph_Mark     : String;
   Paragraph_End_Mark : String;
   Dialog_Mark        : String;
package Corporate_Bullshit is

   function Sentence return String;
   function Workshop return String;
   function Short_Workshop return String;
   function Short_Meeting return String;
   function Financial_Report return String;

end Corporate_Bullshit;

On incorporera donc le fichier de spécification corporate_bullshit.ads ainsi que le corps du package, corporate_bullshit.adb.

Il manque encore une chose. En regardant le début du fichier corporate_bullshit.adb, on voit deux packages en inclusion, Ada.Characters.Handling et Delirium.

with Ada.Characters.Handling;           use Ada.Characters.Handling;
with Delirium;                          use Delirium;

Ada.Characters.Handling est un package standard Ada permettant la transformation majuscule/minuscule, les tests d’appartenance d’un caractère à une classe (alphanumérique, numérique…) entre autres choses.

Delirium est un package du CBSG. D’après les commentaires, il permet de gérer la grammaire des phrases générées. Ce n’est pas le plus clair des codes que j’ai vu mais ça fonctionne et comme on ne s’en sert pas directement…

Par contre, cela veut dire qu’il doit faire partie de notre lib.

On copie donc les fichiers ad[sb] de delirium et corporate_bullshit.
Finalement, le fichier généré cbsg.ads ne nous servant pas, on le retire.
Au final, on obtient cela :

cbsg
├── alire.toml
├── cbsg.gpr
├── config
│   ├── cbsg_config.ads
│   ├── cbsg_config.gpr
│   └── cbsg_config.h
└── src
    ├── corporate_bullshit.adb
    ├── corporate_bullshit.ads
    ├── delirium.adb
    └── delirium.ads

On vérifie que tout se passe bien avec un petit alr build dans le répertoire

Setup
   [mkdir]        object directory for project Cbsg
   [mkdir]        library directory for project Cbsg
Compile
   [Ada]          delirium.adb
   [Ada]          corporate_bullshit.adb
Build Libraries
   [gprlib]       Cbsg.lexch
   [archive]      libCbsg.a
   [index]        libCbsg.a

Pour tester tout ça, on va passer à la deuxième partie, la crate d’exemples.

Création d’un crate d’exemples

Maintenant que notre bibliothèque est prête, nous allons créer une crate avec un exemple simple.
Cette fois-ci, on utilisera la commande dans un répertoire au même niveau que le répertoire cbsg. Cela n’a rien d’obligatoire au final mais là, ce sera plus facile mais stop au suspense, on embraye.

fred@tatooine:~/Dev/Ada$ alr init --bin cbsg_examples

Cela donne la hiérarchie suivante:

cbsg_examples
├── alire
│   └── alire.lock
├── alire.toml
├── cbsg_examples.gpr
├── config
│   └── cbsg_examples_config.gpr
└── src
    └── cbsg_examples.adb

Pour exemple, nous prendrons le plus simple qui soit dans le code du CBSG, produce_corporate_bullshit.adb.

On copie donc ce fichier dans le répertoire src.
Le fichier cbsg_examples.adb ne nous sert pas, on le retire… Sauf que le projet s’attend à ce que l’exécutable de notre crate soit cbsg_examples.

Cela étant déclaré dans le fichier cbsg_examples.gpr, on modifie cela.

Ce fichier est un fichier au format GPRBuild.
GPRBuild, c’est un outil fourni par Adacore qui est un peu le make sur-vitaminé de GNAT, ce dernier disposant déjà de gnatmake.
La syntaxe se veut très Ada, mais il n’est pas compliqué dans notre cas de modifier celui généré pour changer l’exécutable.

Ainsi, il suffit de trouver la ligne

for Main use ("cbsg_examples.adb");

et de la transformer en

for Main use ("produce_corporate_bullshit.adb");

Au final, on obtient l’arbre suivant :

cbsg_examples/
├── alire
│   ├── alire.lock
├── alire.toml
├── cbsg_examples.gpr
├── config
│   ├── cbsg_examples_config.ads
│   ├── cbsg_examples_config.gpr
│   └── cbsg_examples_config.h
└── src
    └── produce_corporate_bullshit.adb

So far so good… sauf que l’on n’a précisé nulle part la dépendance à cbsg !!!

Pour cela, on utilisera la commande

fred@tatoiine:~/Dev/Ada/cbsg_examples$ alr with cbsg

Dans un cas normal, cela suffirait si la crate cbsg était déjà publiée mais ce n’est pas notre cas.

Edition du fichier TOML

Le fichier généré par Alire est au format TOML (pour ceux qui n’auraient pas vu) et contient maintenant ceci:

name = "cbsg_examples"
description = "Corporate bullshit generator examples"
version = "0.0.1"

authors = ["Gautier de Montmollin"]
maintainers = ["Frédéric Praca <xxxxxxxxxxxxx>"]
maintainers-logins = ["FredPraca"]

executables = ["cbsg_examples"]
[[depends-on]]  # Added by alr
cbsg = "*"  # Added by alr

C’est bien mais pas complétement, on voit une première erreur.
Les exécutables produits sont faux. On ne génère plus cet exécutable, cbsg_examples, mais produce_corporate_bullshit… Simple à modifier.

L’autre chose, c’est que l’on voit bien la dépendance vers la crate cbsg avec n’importe quelle version (le "*"). Pour plus d’informations, il faut aller voir la doc d’Alire sur les informations du TOML (ça fait du semver pour la sélection des versions de crate).

On est en dev, on n’a rien publié

Dans notre cas, comme la crate cbsg n’a pas été publiée, on fait un override, on va donc ajouter un petit morceau à notre TOML pour préciser où trouver notre dépendance.

[[pins]]
cbsg = { path = "../cbsg" }  # Overridden by local version

Et voilà, on a ajouté une épingle sur la version à utiliser, il s’agira de celle se trouvant dans le répertoire cbsg.

Le fichier complet devient donc :

name = "cbsg_examples"
description = "Corporate bullshit generator examples"
version = "0.0.1"

authors = ["Gautier de Montmollin"]
maintainers = ["Frédéric Praca <xxxxxxxxx>"]
maintainers-logins = ["FredPraca"]

executables = ["produce_corporate_bullshit"]
[[depends-on]]  # Added by alr
cbsg = "*"  # Added by alr
[[pins]]
cbsg = { path = "../cbsg" }  # Overridden by the latest sources

On teste

fred@tatooine:~/Dev/Ada/cbsg_examples$ alr run

Si cela n’a pas déjà été contsruit, on a l’étape de construction et le lancement de l’exécutable.

Compile
   [Ada]          produce_corporate_bullshit.adb
   [Ada]          delirium.adb
   [Ada]          corporate_bullshit.adb
Build Libraries
   [gprlib]       Cbsg.lexch
   [archive]      libCbsg.a
   [index]        libCbsg.a
Bind
   [gprbind]      produce_corporate_bullshit.bexch
   [Ada]          produce_corporate_bullshit.ali
Link
   [link]         produce_corporate_bullshit.adb
New corporate bullshit is in bullshit.html.  Enjoy.  Use switch -h for help about options.

Le programme écrit par défaut un fichier HTML que vous pouvez ouvrir avec votre navigateur préféré pour obtenir une belle série de bullshits :)

Si on ne veut pas de fichier HTML:

fred@tatooine:~/Dev/Ada/cbsg_examples$ bin/produce_corporate_bullshit --one
Roles and responsibilities consistently engage goals-based and cloud-based timelines.

Conclusion

Le but de cet article était de présenter Alire de façon un peu plus ludique que la documentation officielle en fournissant un premier exemple. Cela a permis de montrer toutefois le pinning de version que l’on peut considérer comme une utilisation avancée.

Alire est un outil en cours de développement et les crates ne sont pas encore nombreuses, on n’en compte que 243 à ce jour, mais tout cela est d’ores et déjà utilisable.
Alire contient des crates diverses et variées qui peuvent couvrir le développement standard tout comme le développement embarqué bare metal ou le développement Spark.

D’ailleurs, si cela n’a été qu’effleurer, Alire permet la gestion des toolchains au sein des crates, permettant ainsi de préciser une dépendance à un compilateur particulier. Ainsi, certaines crates tirent une dépendance vers le compilateur croisé ARM

Si Alire, tout comme pip, cargo ou npm rentre en collision avec le gestionnaire de paquets de votre distribution, cela reste toutefois un bon moyen de développer rapidement du code Ada.

Cherry on the cake, si vous avez GnatStudio d’installé, la commande alr edit ouvrira le projet dans celui-ci :)

Amusez-vous bien !

Aller plus loin

  • # Merci

    Posté par  . Évalué à 2.

    Merci pour le pins, c'est plus propre que ce que je faisais pour tester le binding de rtmidi que j'ai commencé (à titre d'exercice).

  • # et les signatures?

    Posté par  (Mastodon) . Évalué à 8.

    En 2022, il ne passe pas 2 semaines sans que revienne le sujet de poisonnements de librairies. Je m'étonne qu'il n'y ait aucune mention d'une signature obligatoire des crates par son mainteneur pour éviter de répéter les erreurs des gestionnaires de modules npm, pypi, cpan, gems.

    Ça n'empêcherait pas un mainteneur de saboter son propre travail, mais ça permettrait de limiter certaines attaques, comme liées à un mainteneur perdant son domain/adresse email, de voir son repo compromis, etc.

    Aussi qu'en est-il de mécanismes pour détecter le typosquatting?

    • [^] # Re: et les signatures?

      Posté par  (site web personnel, Mastodon) . Évalué à 3.

      Je pense pouvoir dire qu'il n'y a rien de tout ça :)

      La seule chose qu'il y ait est un hash de l'archive du code source récupéré.
      Par exemple, pour aPDF :

      description = "Portable package for producing dynamically PDF documents"
      name = "apdf"
      version = "5.0.3"
      authors = ["Gautier de Montmollin"]
      licenses = "MIT"
      maintainers = ["gdemont@hotmail.com"]
      maintainers-logins = ["zertovitch", "Fabien-Chouteau"]
      project-files = ["pdf_out_gnat_w_gid.gpr"]
      
      [gpr-externals]
      PDF_Build_Mode = ["Debug", "Fast"]
      
      [[depends-on]]
      gid = ">=9.0.0"
      
      [origin]
      url = "https://sourceforge.net/projects/apdf/files/apdf_005_r3.zip"
      hashes = ["sha512:dbe27598986b1744b024803348350e48b9fe14a14b4137796b3bf12fc98e400b45fd16dc3902a5ffbfa407552131bec072c287444889d5984ade6ba6d2d981cf"]

      Après, comment fait-on quand il s'agit de la récupération d'un commit précis dans un dépôt Git ?
      Doit-on vérifier que le commit a bien été signé ?
      Dans le cas d'un dépôt où le nombre de mainteneurs est élevé, doit-on vérifier le commit avec les signatures de chacun des contributeurs ?
      Si on utilise une clé GPG, quid de la date de validité de la clé ?

      Il y a effectivement une réflexion à avoir.

      Pour le typosquatting, il n'y a, à ma connaissance, rien non plus.

      • [^] # Re: et les signatures?

        Posté par  (site web personnel) . Évalué à 3.

        Pour l'instant il y a une étape de modération des crates, donc même s'il n'y a aucune garantie que ça ne peut pas arriver, c'est un point qui est pris en compte.

        Pour la signature des crates, cela sera revu plus tard quand le projet sera plus mature. En plus de demander une attention/expertise particulière, pour l'instant, l'objectif est d'abaisser l'effort nécessaire pour participer.

  • # Question bête

    Posté par  . Évalué à 2.

    Je n'ai pas trop compris comment, avec alire, gérer proprement une dépendance à une lib externe en C ou C++ (rtmidi dans mon cas).

    Pour l'instant, j'ai rajouté manuellement un '-lrtmidi' dans le fichier gpr pour que l'éditeur de lien retrouve ses petits. Mais je me doute que ce n'est pas la 'bonne' manière de faire.

    Merci

    • [^] # Re: Question bête

      Posté par  (site web personnel, Mastodon) . Évalué à 3.

      Il te faut regarder du côté des external releases.

      Par exemple, pour les Ncurses, il y a ça. Plus généralement, cette partie de l'index contient pas mal d'exemples.

      De toute façon, cela ne te dispense pas de préciser dans ton fichier GPR que tu linkes avec la lib en question.

      • [^] # Re: Question bête

        Posté par  . Évalué à 2.

        Merci pour les liens. C'est plus clair maintenant.

        Il ne me restera donc plus qu'à intégrer proprement gprbuild et pkg-config (ce qui ne semble pas gagné gprbuild and pkg-config). Sans doute en passant par meson…

        En attendant, j'ai déjà de quoi m'occuper avec mon binding :->

  • # Ada aujourd'hui ?

    Posté par  . Évalué à 2.

    Bonjour. Il ne me semble pas avoir souvent lu une dépêche sur Ada ici. Du coup j'en profite :-)

    C'est un langage que j'ai énormément apprécié dans ma jeunesse. Du coup ma question non trollesque. Je me demandais a qui il se destine aujourd'hui ? Si le besoin c'est la sûreté, j'imagine que je me tournerais vers Rust. Mas je ne connais plus du tout ni l'un ni l'autre.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.