Nächste: Suchpfade, Vorige: Erstellungsphasen, Nach oben: Programmierschnittstelle [Inhalt][Index]
Sobald Sie anfangen, nichttriviale Paketdefinitionen (siehe Pakete definieren) oder andere Erstellungsaktionen (siehe G-Ausdrücke) zu
schreiben, würden Sie sich wahrscheinlich darüber freuen, Helferlein für
„Shell-artige“ Aktionen vordefiniert zu bekommen, also Code, den Sie
benutzen können, um Verzeichnisse anzulegen, Dateien rekursiv zu kopieren
oder zu löschen, Erstellungsphasen anzupassen und Ähnliches. Das Modul
(guix build utils)
macht solche nützlichen Werkzeugprozeduren
verfügbar.
Die meisten Erstellungssysteme laden (guix build utils)
(siehe
Erstellungssysteme). Wenn Sie also eigene Erstellungsphasen für Ihre
Paketdefinitionen schreiben, können Sie in den meisten Fällen annehmen, dass
diese Prozeduren bei der Auswertung sichtbar sein werden.
Beim Schreiben von G-Ausdrücken können Sie auf der „Erstellungsseite“
(guix build utils)
mit with-imported-modules
importieren und
anschließend mit der use-modules
-Form sichtbar machen (siehe
Using Guile Modules in Referenzhandbuch zu GNU Guile):
(with-imported-modules '((guix build utils)) ;importieren
(computed-file "leerer-verzeichnisbaum"
#~(begin
;; Sichtbar machen.
(use-modules (guix build utils))
;; Jetzt kann man problemlos 'mkdir-p' nutzen.
(mkdir-p (string-append #$output "/a/b/c")))))
Der Rest dieses Abschnitts stellt eine Referenz der meisten
Werkzeugprozeduren dar, die (guix build utils)
anbietet.
Dieser Abschnitt dokumentiert Prozeduren, die sich mit Dateinamen von Store-Objekten befassen.
Liefert den Verzeichnisnamen des Stores.
Liefert wahr zurück, wenn sich Datei innerhalb des Stores befindet.
Liefert den Namen der Datei, die im Store liegt, ohne den Anfang
/gnu/store und ohne die Prüfsumme am Namensanfang. Als Ergebnis
ergibt sich typischerweise eine Zeichenkette aus
"Paket-Version"
.
Liefert für den Paket-Namen (so etwas wie "foo-0.9.1b"
) zwei
Werte zurück: zum einen "foo"
und zum anderen "0.9.1b"
. Wenn
der Teil mit der Version fehlt, werden der Name und #f
zurückgeliefert. Am ersten Bindestrich, auf den eine Ziffer folgt, wird der
Versionsteil abgetrennt.
Bei den folgenden Prozeduren geht es um Dateien und Dateitypen.
Liefert #t
, wenn das Verzeichnis existiert und ein Verzeichnis
ist.
Liefert #t
, wenn die Datei existiert und ausführbar ist.
Liefert #t
, wenn die Datei eine symbolische Verknüpfung ist
(auch bekannt als „Symlink“).
Liefert #t
, wenn die Datei jeweils eine ELF-Datei, ein
ar
-Archiv (etwa eine statische Bibliothek mit .a) oder eine
gzip-Datei ist.
Wenn die Datei eine gzip-Datei ist, wird ihr eingebetteter Zeitstempel
zurückgesetzt (wie bei gzip --no-name
) und wahr
geliefert. Ansonsten wird #f
geliefert. Wenn keep-mtime? wahr
ist, wird der Zeitstempel der letzten Modifikation von Datei
beibehalten.
Die folgenden Prozeduren und Makros helfen beim Erstellen, Ändern und
Löschen von Dateien. Sie machen Funktionen ähnlich zu Shell-Werkzeugen wie
mkdir -p
, cp -r
, rm -r
und sed
verfügbar. Sie ergänzen Guiles ausgiebige aber kleinschrittige
Dateisystemschnittstelle (siehe POSIX in Referenzhandbuch zu GNU
Guile).
Den Rumpf ausführen mit dem Verzeichnis als aktuellem Verzeichnis des Prozesses.
Im Grunde ändert das Makro das aktuelle Arbeitsverzeichnis auf
Verzeichnis bevor der Rumpf ausgewertet wird, mittels
chdir
(siehe Processes in Referenzhandbuch zu GNU
Guile). Wenn der dynamische Bereich von Rumpf wieder verlassen wird,
wechselt es wieder ins anfängliche Verzeichnis zurück, egal ob der
Rumpf durch normales Zurückliefern eines Ergebnisses oder durch einen
nichtlokalen Sprung wie etwa eine Ausnahme verlassen wurde.
Das Verzeichnis und all seine Vorgänger erstellen.
Verzeichnis erstellen, wenn es noch nicht existiert, und die Datei mit ihrem Namen dorthin kopieren.
Dem Besitzer der Datei Schreibberechtigung darauf erteilen.
copy-file] [#:keep-mtime? #f] [#:keep-permissions? #t] Das Verzeichnis Quelle rekursiv an den Zielort kopieren. Wenn follow-symlinks? wahr ist, folgt die Rekursion symbolischen Verknüpfungen, ansonsten werden die Verknüpfungen als solche beibehalten. Zum Kopieren regulärer Dateien wird copy-file aufgerufen. Wenn keep-mtime? wahr ist, bleibt der Zeitstempel der letzten Änderung an den Dateien in Quelle dabei bei denen am Zielort erhalten. Wenn keep-permissions? wahr ist, bleiben Dateiberechtigungen erhalten. Ein ausführliches Protokoll wird in den bei log angegebenen Port geschrieben.
rm -rf
, ohne symbolischen Verknüpfungen zu folgen. Auch
Einhängepunkten wird nicht gefolgt, außer falls follow-mounts?
wahr ist. Fehler dabei werden angezeigt aber ignoriert.
Ausdruck Regexp in der Datei durch die durch Rumpf berechnete Zeichenkette ersetzen. Bei der Auswertung von Rumpf wird jede Muster-Variable an den Teilausdruck an der entsprechenden Position der Regexp gebunden. Zum Beispiel:
(substitute* file
(("Hallo")
"Guten Morgen\n")
(("foo([a-z]+)bar(.*)$" alles Buchstaben Ende)
(string-append "baz" Buchstaben Ende)))
Jedes Mal, wenn eine Zeile in der Datei den Text Hallo
enthält,
wird dieser durch Guten Morgen
ersetzt. Jedes Mal, wenn eine Zeile
zum zweiten regulären Ausdruck passt, wird alles
an die vollständige
Übereinstimmung gebunden, Buchstaben
wird an den ersten Teilausdruck
gebunden und Ende
an den letzten.
Wird für eine Muster-Variable nur _
geschrieben, so wird keine
Variable an die Teilzeichenkette an der entsprechenden Position im Muster
gebunden.
Alternativ kann statt einer Datei auch eine Liste von Dateinamen angegeben werden. In diesem Fall wird jede davon den Substitutionen unterzogen.
Seien Sie vorsichtig bei der Nutzung von $
, um auf das Ende einer
Zeile zu passen. $
passt nämlich nicht auf den Zeilenumbruch
am Ende einer Zeile.
Dieser Abschnitt beschreibt Prozeduren, um Dateien zu suchen und zu filtern.
Liefert ein Prädikat, das gegeben einen Dateinamen, dessen Basisnamen auf Regexp passt, wahr liefert.
lexikografisch sortierte Liste der Dateien innerhalb Verzeichnis, für
die das Prädikat wahr liefert. An Prädikat werden zwei Argumente
übergeben: Der absolute Dateiname und der zugehörige Stat-Puffer. Das
vorgegebene Prädikat liefert immer wahr. Als Prädikat kann auch ein
regulärer Ausdruck benutzt werden; in diesem Fall ist er äquivalent zu
(file-name-predicate Prädikat)
. Mit stat werden
Informationen über die Datei ermittelt; wenn dafür lstat
benutzt
wird, bedeutet das, dass symbolische Verknüpfungen nicht verfolgt
werden. Wenn directories? wahr ist, dann werden auch Verzeichnisse
aufgezählt. Wenn fail-on-error? wahr ist, dann wird bei einem Fehler
eine Ausnahme ausgelöst.
Nun folgen ein paar Beispiele, wobei wir annehmen, dass das aktuelle Verzeichnis der Wurzel des Guix-Quellbaums entspricht.
;; Alle regulären Dateien im aktuellen Verzeichnis auflisten. (find-files ".") ⇒ ("./.dir-locals.el" "./.gitignore" …) ;; Alle .scm-Dateien unter gnu/services auflisten. (find-files "gnu/services" "\\.scm$") ⇒ ("gnu/services/admin.scm" "gnu/services/audio.scm" …) ;; ar-Dateien im aktuellen Verzeichnis auflisten. (find-files "." (lambda (file stat) (ar-file? file))) ⇒ ("./libformat.a" "./libstore.a" …)
Liefert den vollständigen Dateinamen für das Programm, der in
$PATH
gesucht wird, oder #f
, wenn das Programm nicht
gefunden werden konnte.
Liefert den vollständigen Dateinamen von Name, das in allen
Eingaben gesucht wird. search-input-file
sucht nach einer
regulären Datei, während search-input-directory
nach einem
Verzeichnis sucht. Wenn der Name nicht vorkommt, wird eine Ausnahme
ausgelöst.
Hierbei muss für Eingaben eine assoziative Liste wie inputs
oder native-inputs
übergeben werden, die für Erstellungsphasen zur
Verfügung steht (siehe Erstellungsphasen).
Hier ist ein (vereinfachtes) Beispiel, wie search-input-file
in einer
Erstellungsphase des wireguard-tools
-Pakets benutzt wird:
(add-after 'install 'wrap-wg-quick
(lambda* (#:key inputs outputs #:allow-other-keys)
(let ((coreutils (string-append (assoc-ref inputs "coreutils")
"/bin")))
(wrap-program (search-input-file outputs "bin/wg-quick")
#:sh (search-input-file inputs "bin/bash")
`("PATH" ":" prefix ,(list coreutils))))))
Im Modul finden Sie Prozeduren, die geeignet sind, Prozesse zu
erzeugen. Hauptsächlich handelt es sich um praktische Wrapper für Guiles
system*
(siehe system*
in Referenzhandbuch zu GNU Guile).
Programm mit Argumente aufrufen. Es wird eine
&invoke-error
-Ausnahme ausgelöst, wenn der Exit-Code ungleich null
ist, ansonsten wird #t
zurückgeliefert.
Der Vorteil gegenüber system*
ist, dass Sie den Rückgabewert nicht zu
überprüfen brauchen. So vermeiden Sie umständlichen Code in
Shell-Skript-haften Schnipseln etwa in Erstellungsphasen von Paketen.
Liefert wahr, wenn c ein &invoke-error
-Zustand ist.
Auf bestimmte Felder von c zugreifen, einem
&invoke-error
-Zustand.
Auf Port (nach Vorgabe der current-error-port
) eine Meldung für
c, einem &invoke-error
-Zustand, menschenlesbar ausgeben.
Normalerweise würden Sie das so benutzen:
(use-modules (srfi srfi-34) ;für 'guard' (guix build utils)) (guard (c ((invoke-error? c) (report-invoke-error c))) (invoke "date" "--imaginary-option")) -| command "date" "--imaginary-option" failed with status 1
Programm mit Argumente aufrufen und dabei die Standardausgabe
und Standardfehlerausgabe von Programm einfangen. Wenn Programm
erfolgreich ausgeführt wird, wird nichts ausgegeben und der
unbestimmte Wert *unspecified*
zurückgeliefert. Andernfalls wird ein
&message
-Fehlerzustand ausgelöst, der den Status-Code und die Ausgabe
von Programm enthält.
Hier ist ein Beispiel:
(use-modules (srfi srfi-34) ;für 'guard' (srfi srfi-35) ;für 'message-condition?' (guix build utils)) (guard (c ((message-condition? c) (display (condition-message c)))) (invoke/quiet "date") ;alles in Ordnung (invoke/quiet "date" "--imaginary-option")) -| 'date --imaginary-option' exited with status 1; output follows: date: Unbekannte Option »--imaginary-option« „date --help“ liefert weitere Informationen.
(guix build utils)
enthält auch Werkzeuge, um die von
Erstellungssystemen benutzten Erstellungsphasen zu verändern (siehe
Erstellungssysteme). Erstellungsphasen werden durch assoziative Listen oder
„Alists“ repräsentiert (siehe Association Lists in Referenzhandbuch zu GNU Guile), wo jeder Schlüssel ein Symbol ist, das den
Namen der Phase angibt, und der assoziierte Wert eine Prozedur ist (siehe
Erstellungsphasen).
Die zum Kern von Guile („Guile core“) gehörenden Prozeduren und das Modul
(srfi srfi-1)
stellen beide Werkzeuge zum Bearbeiten von Alists zur
Verfügung. Das Modul (guix build utils)
ergänzt sie um Werkzeuge, die
speziell für Erstellungsphasen gedacht sind.
Die Phasen der Reihe nach entsprechend jeder Klausel ändern. Die Klauseln dürfen eine der folgenden Formen haben:
(delete alter-Phasenname) (replace alter-Phasenname neue-Phase) (add-before alter-Phasenname neuer-Phasenname neue-Phase) (add-after alter-Phasenname neuer-Phasenname neue-Phase)
Jeder Phasenname oben ist ein Ausdruck, der zu einem Symbol auswertet, und neue-Phase ist ein Ausdruck, der zu einer Prozedur auswertet.
Folgendes Beispiel stammt aus der Definition des grep
-Pakets. Es fügt
eine neue Phase namens egrep-und-fgrep-korrigieren
hinzu, die auf die
install
-Phase folgen soll. Diese Phase ist eine Prozedur
(lambda*
bedeutet, sie ist eine Prozedur ohne eigenen Namen), die ein
Schlüsselwort #:outputs
bekommt und die restlichen
Schlüsselwortargumente ignoriert (siehe Optional Arguments in Referenzhandbuch zu GNU Guile für mehr Informationen zu lambda*
und
optionalen sowie Schlüsselwort-Argumenten). In der Phase wird
substitute*
benutzt, um die installierten Skripte egrep und
fgrep so zu verändern, dass sie grep
anhand seines absoluten
Dateinamens aufrufen:
(modify-phases %standard-phases
(add-after 'install 'egrep-und-fgrep-korrigieren
;; 'egrep' und 'fgrep' patchen, damit diese 'grep' über den
;; absoluten Dateinamen ausführen, statt es in $PATH zu suchen.
(lambda* (#:key outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(bin (string-append out "/bin")))
(substitute* (list (string-append bin "/egrep")
(string-append bin "/fgrep"))
(("^exec grep")
(string-append "exec " bin "/grep")))))))
In dem Beispiel, das nun folgt, werden Phasen auf zweierlei Art geändert:
Die Standard-configure
-Phase wird gelöscht, meistens weil das Paket
über kein configure-Skript oder etwas Ähnliches verfügt, und die
vorgegebene install
-Phase wird durch eine ersetzt, in der die zu
installierenden ausführbaren Dateien manuell kopiert werden.
(modify-phases %standard-phases
(delete 'configure) ;kein 'configure'-Skript
(replace 'install
(lambda* (#:key outputs #:allow-other-keys)
;; Das Makefile im Paket enthält kein "install"-Ziel,
;; also müssen wir es selber machen.
(let ((bin (string-append (assoc-ref outputs "out")
"/bin")))
(install-file "footswitch" bin)
(install-file "scythe" bin)))))
Es kommt vor, dass Befehle nur richtig funktionieren, wenn bestimmte Umgebungsvariable festgelegt sind. Meistens geht es dabei um Suchpfade (siehe Suchpfade). Wenn man sie nicht zuweist, finden die Programme vielleicht benötigte Dateien oder andere Befehle nicht oder sie nehmen die „falschen“, je nachdem, in welcher Umgebung man sie ausführt. Einige Beispiele:
PATH
zu finden sind,
GUILE_LOAD_PATH
und GUILE_LOAD_COMPILED_PATH
liegen,
QT_PLUGIN_PATH
erwartet werden.
Das Ziel einer Paketautorin ist, dass Befehle immer auf gleiche Weise
funktionieren statt von externen Einstellungen abhängig zu sein. Eine
Möglichkeit, das zu bewerkstelligen, ist, Befehle in ein dünnes
Wrapper-Skript einzukleiden, welches diese Umgebungsvariablen festlegt
und so dafür sorgt, dass solche Laufzeitabhängigkeiten sicherlich gefunden
werden. Der Wrapper würde in den obigen Beispielen also benutzt, um
PATH
, GUILE_LOAD_PATH
oder QT_PLUGIN_PATH
festzulegen.
Das Wrappen wird erleichtert durch ein paar Hilfsprozeduren im Modul
(guix build utils)
, mit denen Sie Befehle wrappen können.
anlegen. Die Liste Variable sollte so aussehen:
'(Variable Trennzeichen Position Liste-von-Verzeichnissen)
Das Trennzeichen ist optional. Wenn Sie keines angeben, wird :
genommen.
Zum Beispiel kopiert dieser Aufruf:
(wrap-program "foo"
'("PATH" ":" = ("/gnu/…/bar/bin"))
'("CERT_PATH" suffix ("/gnu/…/baz/certs"
"/qux/certs")))
foo nach .foo-real und erzeugt die Datei foo mit folgendem Inhalt:
#!ort/mit/bin/bash export PATH="/gnu/…/bar/bin" export CERT_PATH="$CERT_PATH${CERT_PATH:+:}/gnu/…/baz/certs:/qux/certs" exec -a $0 ort/mit/.foo-real "$@"
Wenn Programm bereits mit wrap-program
gewrappt wurde, wird
sein bestehender Wrapper um die Definitionen jeder Variable in der
Variable-Liste ergänzt. Ansonsten wird ein Wrapper mit sh als
Interpretierer angelegt.
einen Wrapper wickeln, damit Variable vorher festgelegt werden. Das
Format für Variable ist genau wie bei der Prozedur
wrap-program
. Der Unterschied zu wrap-program
ist, dass kein
getrenntes Shell-Skript erzeugt wird, sondern das Programm selbst
abgeändert wird, indem zu Beginn von Programm ein Guile-Skript
platziert wird. In der Sprache des Skripts wird das Guile-Skript als
Kommentar interpretiert.
Besondere Kommentare zur Kodierung der Datei, wie es sie bei Python geben kann, werden auf der zweiten Zeile neu erzeugt.
Beachten Sie, dass diese Prozedur auf dieselbe Datei nur einmal angewandt werden kann, denn sie auf Guile-Skripts loszulassen wird nicht unterstützt.
Nächste: Suchpfade, Vorige: Erstellungsphasen, Nach oben: Programmierschnittstelle [Inhalt][Index]