coldwa.st
Tous les guidesProgrammationWebDonnéesOutilsBases de donnéesHaskellConceptsCabal & buildsChaîne d’outilsCompilateurPerformanceÉditeur & HLS

Haskell · Cabal · outillage de build

Cabal 2.0 : la version qui a redéfini les builds Haskell

Par ColdwastMis à jour le 13 juin 20267 min de lecture#haskell#cabal#build
Un graphe de dépendances et un terminal exécutant cabal new-build, illustrant les builds façon nix de Cabal 2.0
Cabal 2.0 a fait de la planification par projet façon nix le socle de la chaîne d’outils moderne.
Réécriture mise à jour, maintenue par la communauté — contenu original, non rédigé par l’ancien propriétaire du domaine ni affilié à lui. Là où des faits de 2017 ont changé depuis, le statut 2026 est explicitement signalé.

Cabal 2.0 est sorti en septembre 2017 aux côtés de GHC 8.2, et avec le recul c’est la version qui a défini l’orientation de l’outillage Haskell pour la décennie suivante. C’est le moment où trois choses sont devenues réelles d’un coup : les builds locaux façon nix, Backpack, et un format .cabal moderne avec une syntaxe de version plus saine. Voici ce qu’était chacune, et où elle en est aujourd’hui.

1. Builds locaux façon nix (new-build)

La fonctionnalité phare. Auparavant, les sandboxes isolaient les dépendances par projet mais reconstruisaient tout de zéro et n’étaient pas reproductibles. Cabal 2.0 a promu cabal new-build : un modèle adressé par contenu où chaque combinaison unique de paquet + version + options + dépendances est construite une seule fois, stockée sous un hash dans un store global partagé, et réutilisée par tout projet dont le plan de build en a besoin.

$ cabal new-build      # 2017 spelling
$ cabal build          # 2026: this IS new-build — it became the default in Cabal 3.0

Statut 2026 : le préfixe new-* a disparu. cabal build, cabal run, cabal repl et cabal test sont tous façon nix par défaut. Les alias v2- existent toujours pour les scripts ; les anciennes commandes v1- sont dépréciées. Ce que Cabal 2.0 a introduit en option est désormais simplement la façon dont Cabal fonctionne.

2. Backpack — paquets mixins et signatures de modules

Backpack a ajouté une véritable modularité paramétrique à Haskell : une bibliothèque peut déclarer une signature de module (une interface) et être typée contre elle sans s’engager sur une implémentation concrète ; un consommateur « mixe » ensuite l’implémentation qu’il veut. L’exemple classique est une bibliothèque écrite contre une signature abstraite Str qui peut être instanciée avec String, Text strict ou ByteString sans duplication de code.

-- in a .cabal file
library
    signatures:   Str
    exposed-modules: Generic.Algorithm

library impl-text
    mixins: indef-lib (Generic.Algorithm as Text.Algorithm)
                      requires (Str as Data.Text)

Statut 2026 : Backpack fonctionne et est toujours pris en charge, mais c’est resté un outil de spécialiste plutôt qu’un schéma grand public — les classes de types et la paramétrisation ordinaire couvrent la plupart des besoins, et le support outillage/IDE des signatures est demeuré limité. Recourez-y quand vous avez réellement besoin de compiler le même code contre plusieurs types concrets ; sinon vous le rencontrerez rarement.

3. Un format .cabal moderne

Définir cabal-version: 2.0 a débloqué des améliorations de format qui sont aujourd’hui le quotidien de Haskell :

  • L’opérateur caret ^>=. build-depends: aeson ^>= 2.1 signifie « >= 2.1 && < 2.2 » — une façon compacte et révélatrice d’intention de suivre la borne supérieure du PVP. On le trouve partout dans les paquets modernes.
  • autogen-modules, pour que les modules générés comme Paths_yourpkg soient déclarés correctement et livrés dans le sdist.
  • Bibliothèques étrangères (la stanza foreign-library), permettant de construire du code Haskell comme objet partagé pour être consommé par d’autres langages.
  • build-depends par composant, pour que la bibliothèque, les tests et les benchmarks d’un paquet aient chacun leur propre ensemble précis de dépendances.

Statut 2026 : tout est standard. La recommandation aujourd’hui est de définir cabal-version: 3.0 ou plus récent (cela implique tout ce que 2.0 apportait, plus les stanzas communes de 2.2, le joker ** pour les fichiers de données, et davantage). N’utilisez 2.0 que si vous devez prendre en charge une chaîne d’outils très ancienne.

4. Le solveur de dépendances s’est amélioré

Du code source ouvert dans un éditeur de code
Du code source ouvert dans un éditeur de code

Cabal 2.0 a livré des améliorations du solveur qui l’ont rendu à la fois plus rapide et plus susceptible de trouver un plan d’installation valide dans un graphe de dépendances profond, avec des explications plus claires quand aucun plan n’existe. Le solveur a continué de s’améliorer depuis, mais c’est avec 2.0 que « le solveur fonctionne généralement tout seul » a commencé à être vrai pour les projets non triviaux.

Pourquoi cela a compté

Avant 2.0, l’histoire des builds Haskell était « installe globalement et prie », rapiécée par les sandboxes. Après 2.0, c’est devenu « déclare ce que tu veux, obtiens un build reproductible, partagé et isolé » — le même modèle mental que Nix, Cargo ou les gestionnaires de paquets modernes ailleurs. Chaque cabal build que vous exécutez en 2026 est le descendant direct de ce que 2.0 a fait le chemin par défaut.

Si vous mettez à niveau un vieux projet

# Bump the format and modernise bounds:
cabal-version: 3.0          # was 1.x or 2.0

# Replace hand-written ranges with caret bounds where it fits:
build-depends:  base ^>= 4.18
              , text ^>= 2.0

# Drop any v1-/sandbox era commands from CI; use:
cabal build --enable-tests
cabal test
cabal freeze                # commit cabal.project.freeze for reproducibility

À lire aussi : Les sandboxes Cabal et ce qui les a remplacées · Paralléliser cabal-install · tous les guides