Haskell · Cabal · Paketverwaltung
Wie fügt man in Cabal eine Abhängigkeit hinzu?
Die kurze Antwort lautet: Man fügt eine Abhängigkeit nicht mit einem Befehl hinzu, sondern mit einer Zeile in einer Datei. Ein Haskell-Paket deklariert seinen Bedarf in seiner .cabal-Datei unter build-depends, und alles Weitere folgt aus dieser Deklaration.
Das gehört klar gesagt, denn wer von npm oder Cargo kommt, sucht einen install-Befehl, der ein Manifest verändert, und wundert sich dann, warum nichts davon für den Compiler sichtbar ist.
Das Feld, auf das es ankommt: build-depends
Öffnen Sie Ihre .cabal-Datei und suchen Sie die Strophe für das, was Sie bauen: eine library, ein executable oder eine test-suite. Jede trägt ihr eigenes build-depends, und genau das wird am häufigsten übersehen.
library
exposed-modules: MyApp.Core
build-depends: base ^>=4.18
, containers ^>=0.6
, text ^>=2.0
default-language: Haskell2010 Ein Paket hinzuzufügen heißt, hier eine Zeile zu ergänzen. Das führende Komma ist Konvention, keine Vorschrift: Es sorgt dafür, dass das Hinzufügen oder Entfernen einer Zeile nur eine Zeile berührt.
Abhängigkeiten gelten pro Strophe. Ein Paket, das Ihre Testsuite braucht, gehört nicht in das build-depends der Bibliothek, und ein Paket, das Ihre Bibliothek braucht, steht Ihrem Executable nicht automatisch zur Verfügung, sofern dieses nicht selbst von der Bibliothek abhängt. Das ist strenger als in den meisten Ökosystemen und bewusst so: Es hält Test-Werkzeuge aus dem heraus, was Ihre Nutzer bauen müssen.
Was der Caret-Operator wirklich bedeutet
Das überall sichtbare ^>= ist keine Zierde. Es ist eine Kurzform, die an die Package Versioning Policy von Haskell gebunden ist, die PVP, und wer sie versteht, muss bei Grenzen nicht mehr raten.
Unter der PVP liest sich eine Version als A.B.C.D, wobei A.B zusammen die Hauptversion bilden. Brechende Änderungen erhöhen A oder B. Ergänzungen, die bestehenden Code nicht brechen können, erhöhen C. ^>=1.2.3 bedeutet also mindestens 1.2.3 und unterhalb der nächsten Hauptversion, ausgeschrieben >=1.2.3 && <1.3.
Die Folge überrascht viele: In Haskell ist der Schritt von 1.2 auf 1.3 ein Hauptsprung, kein Nebensprung. Wer das Denkmodell der semantischen Versionierung mitbringt, in der allein die erste Zahl die Hauptversion ist, schreibt viel lockerere Grenzen als beabsichtigt.
Warum cabal update zuerst kommt
Cabal löst Abhängigkeiten gegen eine lokale Kopie des Hackage-Paketindex auf. Ist diese Kopie veraltet, existiert ein letzte Woche veröffentlichtes Paket für Ihre Maschine schlicht nicht, und Sie erhalten einen Auflösungsfehler, der klingt, als wäre der Paketname falsch.
cabal update
cabal build Führen Sie cabal update aus, wenn ein Paket, von dem Sie wissen, dass es existiert, nicht gefunden wird, und nach jeder längeren Pause zwischen zwei Sitzungen. Es ist das Erste, was man probiert, und es kostet nichts.
Einen Auflösungsfehler ohne Panik lesen
Wenn der Solver nicht alle Bedingungen gleichzeitig erfüllen kann, meldet er den Konflikt, statt stillschweigend etwas auszuwählen. Die Meldung ist dicht, sagt aber etwas Konkretes: Zwei Ihrer Abhängigkeiten wollen unvereinbare Bereiche einer dritten.
Die ehrlichen Möglichkeiten sind wenige, und man sollte sie kennen, bevor man zu einem Schalter greift:
Eine selbst gesetzte Grenze lockern. Steht die zu enge Grenze in Ihrer eigenen .cabal-Datei, ist das Aufweiten legitim, sofern Sie danach bauen und testen, statt es anzunehmen.
Eine andere Version des hinzugefügten Pakets wählen. Oft passt eine ältere Ausgabe der neuen Abhängigkeit zu den Bedingungen, die Ihr Projekt bereits hat.
--allow-newer bewusst und vorübergehend einsetzen. Es weist den Solver an, von anderen Paketen deklarierte Obergrenzen zu ignorieren. Diese Grenzen haben Maintainer aus einem Grund geschrieben; behandeln Sie den Schalter als Diagnose, die zeigt, wo der eigentliche Konflikt liegt, nicht als Korrektur, die man committet.
Festschreiben, was aufgelöst wurde
Die Auflösung wählt Versionen zu einem bestimmten Zeitpunkt. Um diesen Zeitpunkt reproduzierbar zu machen, frieren Sie ihn ein:
cabal freeze Das schreibt eine Datei cabal.project.freeze mit den genau gewählten Versionen. Committen Sie sie, wenn ein Build sich nächsten Monat gleich verhalten soll, was bei einer Anwendung die übliche Erwartung ist. Bibliotheken sind der umgekehrte Fall: Sie sollten flexibel bleiben, damit wer von ihnen abhängt frei auflösen kann.
Bei einem Projekt über mehrere Pakete hinweg, oder wenn eine Quellabhängigkeit außerhalb von Hackage nötig ist, gehört diese Konfiguration in cabal.project und nicht in die .cabal-Datei. Beide auseinanderzuhalten ist der klarste Weg zur Orientierung: die .cabal-Datei beschreibt ein Paket, cabal.project beschreibt, wie man mehrere gemeinsam baut.
Kurz gefasst
Fügen Sie eine Abhängigkeit hinzu, indem Sie eine Zeile in build-depends ergänzen, und zwar in der Strophe, die sie tatsächlich braucht. Begrenzen Sie sie mit ^>= und denken Sie daran, dass unter der PVP die ersten beiden Zahlen zusammen die Hauptversion bilden. Führen Sie cabal update aus, bevor Sie einem Paket vorwerfen, es existiere nicht. Frieren Sie Anwendungen ein, lassen Sie Bibliotheken locker.
Wenn Sie Ihr Werkzeug noch wählen, behandelt unser Vergleich von Stack und Cabal diese Entscheidung, und die GHCup-Installationsanleitung richtet die Toolchain ein, die dieser Text als vorhanden voraussetzt.
Das Feld build-depends, die Ausschreibung des Caret-Operators und die A.B-Regel für die Hauptversion folgen dem Cabal-Benutzerhandbuch und der Haskell Package Versioning Policy, geprüft zum Zeitpunkt des Schreibens. Die Verfügbarkeit von Befehlen hängt von Ihrer cabal-install-Version ab: Führen Sie cabal --version aus und schlagen Sie im Handbuch Ihrer Version nach, bevor Sie sich auf einen bestimmten Unterbefehl verlassen.