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


8.5 Erstellungsphasen

Fast alle Erstellungssysteme für Pakete implementieren ein Konzept von Erstellungsphasen: einer Abfolge von Aktionen, die vom Erstellungssystem ausgeführt werden, wenn Sie das Paket erstellen. Dabei fallen in den Store installierte Nebenerzeugnisse an. Eine Ausnahme ist der Erwähnung wert: das magere trivial-build-system (siehe Erstellungssysteme).

Wie im letzten Abschnitt erläutert, stellen diese Erstellungssysteme eine standardisierte Liste von Phasen zur Verfügung. Für gnu-build-system gehören zu den Standardphasen eine unpack-Phase, wo der Quellcode-Tarball entpackt wird, eine configure-Phase, wo ./configure ausgeführt wird, eine build-Phase, um make auszuführen, und (unter anderem) eine install-Phase, um make install auszuführen. Siehe Erstellungssysteme für eine Detailansicht dieser Phasen. cmake-build-system erbt diese Phasen, aber seine configure-Phase führt cmake statt ./configure aus. Andere Erstellungssysteme wie z.B. python-build-system haben eine völlig andere Liste von Standardphasen. Dieser gesamte Code läuft erstellungsseitig: Er wird dann ausgewertet, wenn Sie das Paket wirklich erstellen, in einem eigenen Erstellungprozess nur dafür, den der Erstellungs-Daemon erzeugt (siehe Aufruf des guix-daemon).

Erstellungsphasen werden durch assoziative Listen (kurz „Alists“) repräsentiert (siehe Association Lists in Referenzhandbuch zu GNU Guile) wo jeder Schlüssel ein Symbol für den Namen der Phase ist und der assoziierte Wert eine Prozedur ist, die eine beliebige Anzahl von Argumenten nimmt. Nach Konvention empfangen diese Prozeduren dadurch Informationen über die Erstellung in Form von Schlüsselwort-Parametern, die darin benutzt oder ignoriert werden können.

Zum Beispiel werden die %standard-phases, das ist die Variable mit der Alist der Erstellungsphasen, in (guix build gnu-build-system) so definiert17:

;; Die Erstellungsphasen von 'gnu-build-system'.

(define* (unpack #:key source #:allow-other-keys)
  ;; Quelltarball extrahieren.
  (invoke "tar" "xvf" source))

(define* (configure #:key outputs #:allow-other-keys)
  ;; 'configure'-Skript ausführen. In Ausgabe "out" installieren.
  (let ((out (assoc-ref outputs "out")))
    (invoke "./configure"
            (string-append "--prefix=" out))))

(define* (build #:allow-other-keys)
  ;; Kompilieren.
  (invoke "make"))

(define* (check #:key (test-target "check") (tests? #true)
                #:allow-other-keys)
  ;; Testkatalog ausführen.
  (if tests?
      (invoke "make" test-target)
      (display "test suite not run\n")))

(define* (install #:allow-other-keys)
  ;; Dateien ins bei 'configure' festgelegte Präfix installieren.
  (invoke "make" "install"))

(define %standard-phases
  ;; Die Liste der Standardphasen (der Kürze halber lassen wir einige
  ;; aus). Jedes Element ist ein Paar aus Symbol und Prozedur.
  (list (cons 'unpack unpack)
        (cons 'configure configure)
        (cons 'build build)
        (cons 'check check)
        (cons 'install install)))

Hier sieht man wie %standard-phases als eine Liste von Paaren aus Symbol und Prozedur (siehe Pairs in Referenzhandbuch zu GNU Guile) definiert ist. Das erste Paar assoziiert die unpack-Prozedur mit dem unpack-Symbol — es gibt einen Namen an. Das zweite Paar definiert die configure-Phase auf ähnliche Weise, ebenso die anderen. Wenn ein Paket erstellt wird, das gnu-build-system benutzt, werden diese Phasen der Reihe nach ausgeführt. Sie können beim Erstellen von Paketen im Erstellungsprotokoll den Namen jeder gestarteten und abgeschlossenen Phase sehen.

Schauen wir uns jetzt die Prozeduren selbst an. Jede davon wird mit define* definiert, dadurch können bei #:key die Schlüsselwortparameter aufgelistet werden, die die Prozedur annimmt, wenn gewünscht auch zusammen mit einem Vorgabewert, und durch #:allow-other-keys wird veranlasst, dass andere Schlüsselwortparameter ignoriert werden (siehe Optional Arguments in Referenzhandbuch zu GNU Guile).

Die unpack-Prozedur berücksichtigt den source-Parameter; das Erstellungssystem benutzt ihn, um den Dateinamen des Quell-Tarballs (oder des Checkouts aus einer Versionskontrolle) zu finden. Die anderen Parameter ignoriert sie. Die configure-Phase interessiert sich nur für den outputs-Parameter, eine Alist, die die Namen von Paketausgaben auf ihre Dateinamen im Store abbildet (siehe Pakete mit mehreren Ausgaben.). Sie extrahiert den Dateinamen für out, die Standardausgabe, und gibt ihn an ./configure als das Installationspräfix weiter, wodurch make install zum Schluss alle Dateien in dieses Verzeichnis kopieren wird (siehe configuration and makefile conventions in GNU Coding Standards). build und install ignorieren all ihre Argumente. check berücksichtigt das Argument test-target, worin der Name des Makefile-Ziels angegeben wird, um die Tests auszuführen. Es wird stattdessen eine Nachricht angezeigt und die Tests übersprungen, wenn tests? falsch ist.

Die Liste der Phasen, die für ein bestimmtes Paket benutzt werden, kann über den #:phases-Parameter an das Erstellungssystem geändert werden. Das Ändern des Satzes von Erstellungsphasen funktioniert so, dass eine neue Alist von Phasen aus der oben beschriebenen %standard-phases-Alist heraus erzeugt wird. Dazu können die Standardprozeduren zur Bearbeitung von assoziativen Listen wie alist-delete benutzt werden (siehe SRFI-1 Association Lists in Referenzhandbuch zu GNU Guile), aber es ist bequemer, dafür die Prozedur modify-phases zu benutzen (siehe modify-phases).

Hier ist ein Beispiel für eine Paketdefinition, die die configure-Phase aus %standard-phases entfernt und eine neue Phase vor der build-Phase namens set-prefix-in-makefile einfügt:

(define-public beispiel
  (package
    (name "beispiel")
    ;; wir lassen die anderen Felder aus
    (build-system gnu-build-system)
    (arguments
     '(#:phases (modify-phases %standard-phases
                  (delete 'configure)
                  (add-before 'build 'set-prefix-in-makefile
                    (lambda* (#:key outputs #:allow-other-keys)
                      ;; Makefile anpassen, damit die 'PREFIX'-
                      ;; Variable auf "out" verweist.
                      (let ((out (assoc-ref outputs "out")))
                        (substitute* "Makefile"
                          (("PREFIX =.*")
                           (string-append "PREFIX = "
                                          out "\n")))
                        #true))))))))

Die neu eingefügte Phase wurde als anonyme Prozedur geschrieben. Solche namenlosen Prozeduren schreibt man mit lambda*. Oben berücksichtigt sie den outputs-Parameter, den wir zuvor gesehen haben. Siehe Werkzeuge zur Erstellung für mehr Informationen über die in dieser Phase benutzten Hilfsmittel und für mehr Beispiele zu modify-phases.

Sie sollten im Kopf behalten, dass Erstellungsphasen aus Code bestehen, der erst dann ausgewertet wird, wenn das Paket erstellt wird. Das ist der Grund, warum der gesamte modify-phases-Ausdruck oben quotiert wird. Quotiert heißt, er steht nach einem ' oder Apostrophenzeichen: Er wird nicht sofort als Code ausgewertet, sondern nur zur späteren Ausführung vorgemerkt (wir sagen staged, siehe G-Ausdrücke für eine Erläuterung von Code-Staging und den beteiligten Code-Schichten (oder „Strata“).


Fußnoten

(17)

Wir stellen hier nur eine vereinfachte Sicht auf diese Erstellungsphasen vor. Wenn Sie alle Details wollen, schauen Sie sich (guix build gnu-build-system) an!


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