Nächste: , Vorige: , Nach oben: Programmierschnittstelle   [Inhalt][Index]


8.3 Paketvarianten definieren

Eine der schönen Sachen an Guix ist, dass Sie aus einer Paketdefinition leicht Varianten desselben Pakets ableiten können — solche, die vom Anbieter eine andere Paketversion nehmen, als im Guix-Repository angegeben, solche mit anderen Abhängigkeiten, anders gearteten Compiler-Optionen und mehr. Manche dieser eigenen Pakete lassen sich direkt aus der Befehlszeile definieren (siehe Paketumwandlungsoptionen). Dieser Abschnitt beschreibt, wie man Paketvarianten mit Code definiert. Das kann in „Manifesten“ nützlich sein (siehe --manifest) und in Ihrer eigenen Paketsammlung (siehe Einen Kanal erstellen), unter anderem!

Wie zuvor erörtert, sind Pakete Objekte erster Klasse in der Scheme-Sprache. Das Modul (guix packages) stellt das package-Konstrukt zur Verfügung, mit dem neue Paketobjekte definiert werden können (siehe „package“-Referenz). Am einfachsten ist es, eine Paketvariante zu definieren, indem Sie das inherit-Schlüsselwort mit einem package-Objekt verwenden. Dadurch können Sie die Felder einer Paketdefinition erben lassen, aber die Felder selbst festlegen, die Sie festlegen möchten.

Betrachten wir zum Beispiel die Variable hello, die eine Definition für die aktuelle Version von GNU Hello enthält. So können Sie eine Variante für Version 2.2 definieren (welche 2006 veröffentlicht wurde — ein guter Jahrgang!):

(use-modules (gnu packages base))    ;für „hello“

(define hello-2.2
  (package
    (inherit hello)
    (version "2.2")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnu/hello/hello-" version
                                  ".tar.gz"))
              (sha256
               (base32
                "0lappv4slgb5spyqbh6yl5r013zv72yqg2pcl30mginf3wdqd8k9"))))))

Das obige Beispiel entspricht dem, was Sie mit der Paketumwandlungsoption --with-source erreichen können. Im Kern erhält hello-2.2 alle Felder von hello mit Ausnahme von version und source, die ersetzt werden (die beiden unterliegen einem „Override“). Beachten Sie, dass es die ursprüngliche hello-Variable weiterhin gibt, sie bleibt unverändert in dem Modul (gnu packages base). Wenn Sie auf diese Weise ein eigenes Paket definieren, fügen Sie tatsächlich eine neue Paketdefinition hinzu; das Original bleibt erhalten.

Genauso gut können Sie Varianten mit einer anderen Menge von Abhängigkeiten als im ursprünglichen Paket definieren. Zum Beispiel hängt das vorgegebene gdb-Paket von guile ab, aber weil es eine optionale Abhängigkeit ist, können Sie eine Variante definieren, die jene Abhängigkeit entfernt, etwa so:

(use-modules (gnu packages gdb)    ;für 'gdb'
             (srfi srfi-1))        ;für 'alist-delete'

(define gdb-sans-guile
  (package
    (inherit gdb)
    (inputs (alist-delete "guile"
                          (package-inputs gdb)))))

Der obige Aufruf von alist-delete entfernt das Tupel aus dem inputs-Feld, dessen erstes Element "guile" ist (siehe SRFI-1 Association Lists in Referenzhandbuch zu GNU Guile).

Manchmal bietet es sich an, Funktionen (also „Prozeduren“, wie Scheme-Anwender sagen) zu schreiben, die ein Paket abhängig von bestimmten Parametern zurückliefern. Als Beispiel betrachten wir die luasocket-Bibliothek für die Programmiersprache Lua. Wir möchten luasocket-Pakete für die hauptsächlichen Versionen von Lua verfügbar machen. Eine Möglichkeit, das zu erreichen, ist, eine Prozedur zu definieren, die ein Lua-Paket nimmt und ein von diesem abhängiges luasocket-Paket liefert.

(define (make-lua-socket name lua)
  ;; Liefert ein luasocket-Paket, das mit LUA erstellt wird.
  (package
    (name name)
    (version "3.0")
    ;; hier würden noch ein paar Felder stehen
    (inputs
     `(("lua" ,lua)))
    (synopsis "Socket library for Lua")))

(define-public lua5.1-socket
  (make-lua-socket "lua5.1-socket" lua-5.1))

(define-public lua5.2-socket
  (make-lua-socket "lua5.2-socket" lua-5.2))

Damit haben wir Pakete lua5.1-socket und lua5.2-socket definiert, indem wir make-lua-socket mit verschiedenen Argumenten aufgerufen haben. Siehe Procedures in Referenzhandbuch von GNU Guile für mehr Informationen über Prozeduren. Weil wir mit define-public öffentlich sichtbare Definitionen auf oberster Ebene („top-level“) für diese beiden Pakete angegeben haben, kann man sie von der Befehlszeile aus benutzen (siehe Paketmodule).

Bei diesen handelt es sich um sehr einfache Paketvarianten. Bequemer ist es dann, mit dem Modul (guix transformations) eine hochsprachliche Schnittstelle einzusetzen, die auf die komplexeren Paketumwandlungsoptionen direkt abbildet (siehe Paketumwandlungsoptionen):

Scheme-Prozedur: options->transformation Optionen

Liefert eine Prozedur, die gegeben ein zu erstellendes Objekt (ein Paket, eine Ableitung oder Ähnliches) die durch Optionen festgelegten Umwandlungen daran umsetzt und die sich ergebenden Objekte zurückliefert. Optionen muss eine Liste von Paaren aus Symbol und Zeichenkette sein wie:

((with-branch . "guile-gcrypt=master")
 (without-tests . "libgcrypt"))

Jedes Symbol benennt eine Umwandlung. Die entsprechende Zeichenkette ist ein Argument an diese Umwandlung.

Zum Beispiel wäre ein gleichwertiges Manifest zu diesem Befehl:

guix build guix \
  --with-branch=guile-gcrypt=master \
  --with-debug-info=zlib

… dieses hier:

(use-modules (guix transformations))

(define transform
  ;; Die Prozedur zur Paketumwandlung.
  (options->transformation
   '((with-branch . "guile-gcrypt=master")
     (with-debug-info . "zlib"))))

(packages->manifest
 (list (transform (specification->package "guix"))))

Die Prozedur options->transformation lässt sich einfach benutzen, ist aber vielleicht nicht so flexibel, wie Sie es sich wünschen. Wie sieht ihre Implementierung aus? Aufmerksamen Lesern mag aufgefallen sein, dass die meisten Paketumwandlungen die oberflächlichen Änderungen aus den ersten Beispielen in diesem Abschnitt übersteigen: Sie schreiben Eingaben um, was im Abhängigkeitsgraphen bestimmte Eingaben durch andere ersetzt.

Das Umschreiben des Abhängigkeitsgraphen, damit Pakete im Graphen ausgetauscht werden, ist in der Prozedur package-input-rewriting aus (guix packages) implementiert.

Scheme-Prozedur: package-input-rewriting Ersetzungen [umgeschriebener-Name] [#:deep? #t] Eine Prozedur liefern, die für ein

ihr übergebenes Paket dessen direkte und indirekte Abhängigkeit gemäß den Ersetzungen umschreibt, einschließlich ihrer impliziten Eingaben, wenn deep? wahr ist. Ersetzungen ist eine Liste von Paketpaaren; das erste Element eines Paares ist das zu ersetzende Paket und das zweite ist, wodurch es ersetzt werden soll.

Optional kann als umgeschriebener-Name eine ein Argument nehmende Prozedur angegeben werden, die einen Paketnamen nimmt und den Namen nach dem Umschreiben zurückliefert.

Betrachten Sie dieses Beispiel:

(define libressl-statt-openssl
  ;; Dies ist eine Prozedur, mit der OPENSSL durch LIBRESSL
  ;; rekursiv ersetzt wird.
  (package-input-rewriting `((,openssl . ,libressl))))

(define git-mit-libressl
  (libressl-statt-openssl git))

Hier definieren wir zuerst eine Umschreibeprozedur, die openssl durch libressl ersetzt. Dann definieren wir damit eine Variante des git-Pakets, die libressl statt openssl benutzt. Das ist genau, was auch die Befehlszeilenoption --with-input tut (siehe --with-input).

Die folgende Variante von package-input-rewriting kann für die Ersetzung passende Pakete anhand ihres Namens finden, statt zu prüfen, ob der Wert identisch ist.

Scheme-Prozedur: package-input-rewriting/spec Ersetzungen [#:deep? #t]

Liefert eine Prozedur, die für ein gegebenes Paket die angegebenen Ersetzungen auf dessen gesamten Paketgraphen anwendet (einschließlich impliziter Eingaben, außer wenn deep? falsch ist). Ersetzungen muss dabei eine Liste von Paaren aus je einer Spezifikation und Prozedur sein. Dabei ist jede Spezifikation eine Paketspezifikation wie "gcc" oder "guile@2" und jede Prozedur nimmt ein passendes Paket und liefert dafür einen Ersatz für das Paket.

Das obige Beispiel könnte auch so geschrieben werden:

(define libressl-statt-openssl
  ;; Rekursiv alle Pakete namens "openssl" durch LibreSSL ersetzen.
  (package-input-rewriting/spec `(("openssl" . ,(const libressl)))))

Der Hauptunterschied ist hier, dass diesmal Pakete zur Spezifikation passen müssen und nicht deren Wert identisch sein muss, damit sie ersetzt werden. Mit anderen Worten wird jedes Paket im Graphen ersetzt, das openssl heißt.

Eine allgemeiner anwendbare Prozedur, um den Abhängigkeitsgraphen eines Pakets umzuschreiben, ist package-mapping. Sie unterstützt beliebige Änderungen an den Knoten des Graphen.

Scheme-Prozedur: package-mapping Prozedur [Schnitt?] [#:deep? #f]

Liefert eine Prozedur, die, wenn ihr ein Paket übergeben wird, die an package-mapping übergebene Prozedur auf alle vom Paket abhängigen Pakete anwendet. Die Prozedur liefert das resultierende Paket. Wenn Schnitt? für ein Paket davon einen wahren Wert liefert, findet kein rekursiver Abstieg in dessen Abhängigkeiten statt. Steht deep? auf wahr, wird die Prozedur auch auf implizite Eingaben angewandt.


Nächste: , Vorige: , Nach oben: Programmierschnittstelle   [Inhalt][Index]