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


8.6 Werkzeuge zur Erstellung

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.

8.6.1 Umgehen mit Store-Dateinamen

Dieser Abschnitt dokumentiert Prozeduren, die sich mit Dateinamen von Store-Objekten befassen.

Scheme-Prozedur: %store-directory

Liefert den Verzeichnisnamen des Stores.

Scheme-Prozedur: store-file-name? Datei

Liefert wahr zurück, wenn sich Datei innerhalb des Stores befindet.

Scheme-Prozedur: strip-store-file-name Datei

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".

Scheme-Prozedur: package-name->name+version Name

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.

8.6.2 Dateitypen

Bei den folgenden Prozeduren geht es um Dateien und Dateitypen.

Scheme-Prozedur: directory-exists? Verzeichnis

Liefert #t, wenn das Verzeichnis existiert und ein Verzeichnis ist.

Scheme-Prozedur: executable-file? Datei

Liefert #t, wenn die Datei existiert und ausführbar ist.

Liefert #t, wenn die Datei eine symbolische Verknüpfung ist (auch bekannt als „Symlink“).

Scheme-Prozedur: elf-file? Datei
Scheme-Prozedur: ar-file? Datei
Scheme-Prozedur: gzip-file? Datei

Liefert #t, wenn die Datei jeweils eine ELF-Datei, ein ar-Archiv (etwa eine statische Bibliothek mit .a) oder eine gzip-Datei ist.

Scheme-Prozedur: reset-gzip-timestamp Datei [#:keep-mtime? #t]

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.

8.6.3 Änderungen an Dateien

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).

Scheme-Syntax: with-directory-excursion Verzeichnis Rumpf

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.

Scheme-Prozedur: mkdir-p Verzeichnis

Das Verzeichnis und all seine Vorgänger erstellen.

Scheme-Prozedur: install-file Datei Verzeichnis

Verzeichnis erstellen, wenn es noch nicht existiert, und die Datei mit ihrem Namen dorthin kopieren.

Scheme-Prozedur: make-file-writable Datei

Dem Besitzer der Datei Schreibberechtigung darauf erteilen.

Scheme-Prozedur: copy-recursively Quelle Zielort [#:log (current-output-port)] [#:follow-symlinks? #f] [#:keep-mtime? #f] 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. Wenn keep-mtime? wahr ist, bleibt der Zeitstempel der letzten Änderung an den Dateien in Quelle dabei bei denen am Zielort erhalten. Ein ausführliches Protokoll wird in den bei log angegebenen Port geschrieben.

Scheme-Prozedur: delete-file-recursively Verzeichnis [#:follow-mounts? #f] Das Verzeichnis rekursiv löschen, wie bei

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.

Scheme-Syntax: substitute* Datei ((Regexp Muster-Variable…) Rumpf…) … Den regulären

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.

8.6.4 Dateien suchen

Dieser Abschnitt beschreibt Prozeduren, um Dateien zu suchen und zu filtern.

Scheme-Prozedur: file-name-predicate Regexp

Liefert ein Prädikat, das gegeben einen Dateinamen, dessen Basisnamen auf Regexp passt, wahr liefert.

Scheme-Prozedur: find-files Verzeichnis [Prädikat] [#:stat lstat] [#:directories? #f] [#:fail-on-error? #f] Liefert die

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" )
Scheme-Prozedur: which Programm

Liefert den vollständigen Dateinamen für das Programm, der in $PATH gesucht wird, oder #f, wenn das Programm nicht gefunden werden konnte.

8.6.5 Erstellungsphasen

(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.

Scheme-Syntax: modify-phases Phasen Klausel

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")))
        #t))))

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)
        #t))))

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