Nächste: Manifeste verfassen, Vorige: Pakete definieren, Nach oben: Programmierschnittstelle [Inhalt][Index]
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 Manifeste verfassen) 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“ (define gdb-sans-guile (package (inherit gdb) (inputs (modify-inputs (package-inputs gdb) (delete "guile")))))
Mit obiger modify-inputs
-Form wird das "guile"
-Paket aus den
Eingaben im inputs
-Feld von gdb
entfernt. Das
modify-inputs
-Makro hilft Ihnen, wann immer Sie etwas aus den
Paketeingaben entfernen, hinzufügen oder ersetzen möchten.
Ändert die übergebenen Paketeingaben, die package-inputs
& Co.
liefern können, entsprechend der angegebenen Klauseln. Jede Klausel muss
eine der folgenden Formen aufweisen:
(delete Name…)
Die Pakete mit den angegebenen Namen (als Zeichenketten) aus den Eingaben entfernen.
(prepend Paket…)
Jedes Paket vorne an die Eingabenliste anstellen.
(append Paket…)
Jedes Paket am Ende der Eingabenliste anhängen.
Mit folgendem Beispiel werden die Eingaben GMP und ACL unter denen von Coreutils weggelassen und libcap wird an dem Anfang hinzugefügt:
(modify-inputs (package-inputs coreutils)
(delete "gmp" "acl")
(prepend libcap))
Mit folgendem Beispiel wird das guile
-Paket unter den Eingaben von
guile-redis
weggelassen und stattdessen wird guile-2.2
verwendet:
(modify-inputs (package-inputs guile-redis)
(replace "guile" guile-2.2))
Die letzte Art Klausel ist append
, was bedeutet, dass Eingaben hinten
an die Liste angehängt werden.
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 (list 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):
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.
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.
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.
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: Manifeste verfassen, Vorige: Pakete definieren, Nach oben: Programmierschnittstelle [Inhalt][Index]