PHP/Tutorials/Einführung in die Interna/Das PHP-Buildsystem

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Einleitung

Diese Seite soll eine Einführung in die Zusammenhänge des PHP-Buildsystems liefern. Das Buildsystem selbst ist hochkompliziert, weswegen es sehr schwierig ist, eine detaillierte Erklärung zu geben, die den Rahmen des Artikels nicht vollständig sprengt. Daher wird sich diese Seite nur mit den allgemeinen Zusammenhängen des Buildsystems beschäftigen, um ein Verständnis dafür zu vermitteln, wo man nachsehen muss, wenn man etwas genauer wissen muss.

Buildsystem unter UNIX: Autoconf

Unter UNIX verwendet PHP als Grundlage für das Buildsystem GNU Autoconf in Verbindung mit dem Standard-Tool make. Autoconf funktioniert auf folgende Weise: Vor dem Kompilieren von PHP wird ein Shell-Script ausgeführt, das sehr viele verschiedene Checks auf dem System laufen lässt um zu überprüfen, ob das System die nötigen Voraussetzungen mitbringt und wo bestimmte Bibliotheken zu finden sind. Außerdem kann man dem Shell-Script Parameter übergeben, die steuern, welche Teile von dem Softwarepaket man überhaupt aktivieren will und wohin es installiert werden soll. Am Ende generiert das Script die eigentlichen Dateien (z. B. Makefiles) aus bestimmten Templates, die dann die nötigen Informationen enthalten, um das Softwarepaket per make kompilieren zu können. Das Shellscript heißt ./configure.

Dieses Shellscript wird aber nicht von Hand geschrieben, stattdessen wird von den Entwicklern eine Steuerdatei configure.in oder configure.ac angelegt, die in einer Makro-Sprache namens m4 geschrieben ist. Ein m4-Makroprozessor wird dann mit dieser Steuerdatei aufgerufen und erzeugt dann das eigentliche Script. Da der m4-Prozessor nicht unbedingt auf jedem System vorhanden ist geschieht dies in der Regel von den Entwicklern des Softwarepakets selbst, d. h. das fertige configure-Script wird mit dem Quellcode mitausgeliefert.

In PHP heißt die Steuerdatei configure.in. Da es aber nicht praktikabel ist, alle Informationen über das PHP-Buildsystem komplett in dieser einzigen Steuerdatei zu behalten wird eine andere Steuerdatei, aclocal.m4 abgespalten. Diese enthält die eigentlichen Definitionen von zusätzlichen Makros, die Tests durchführen während configure.in sie nur aufruft. autoconf verarbeitet per Default direkt sowohl aclocal.m4 als auch configure.in. Da PHP selbst aber modular aufgebaut ist, d. h. es verschiedene SAPIs und verschiedene Erweiterungen gibt, werden außerdem noch Dateien mit dem Namen config*.m4 in ext/* und sapi/* berücksichtigt. Diese werden über Umwege in der configure.in eingebunden. Da das ganze ziemlich kompliziert ist, folgt ein Diagramm, das dies veranschaulichen soll:

Artikel-PHP--Einführung in die Interna--php-buildsystem-buildconf.png

Der Aufruf von ./buildconf führt dazu, dass configure neu erzeugt wird. Dies ist ein sehr komplizierter Prozess. Als erstes führt buildconf das Programm make aus, dem das Makefile build/build.mk übergeben wird. Dessen zentrale Aufgabe ist es, eine Datei generated_lists im PHP-Hauptverzeichnis zu erstellen, die folgende Struktur hat:

makefile_am_files = Zend/Makefile.am TSRM/Makefile.am
config_h_files = Zend/acconfig.h TSRM/acconfig.h
config_m4_files = Zend/Zend.m4 TSRM/tsrm.m4 TSRM/threads.m4 Zend/acinclude.m4 ...

Wichtig sind hierbei vor allem die Zeilen config_m4_files und config_h_files. Nachdem es die generated_lists erzeugt hat, wird make mit dem Makefile build/build2.mk aufgerufen. Dieses bindet dann selbst die Datei generated_lists Alle Dateien, die in config_h_files angegeben sind (dies sind im Moment fest Zend/acconfig.h und TSRM/acconfig.h) werden zusammen mit acconfig.h.in zu acconfig.h konkateniert – dies wird später für autoheader wichtig. Alle Dateien innerhalb von config_m4_files dienen dagegen ausschließlich um festzustellen, ob sich der Timestamp der Dateien geändert hat, damit make weiß, ob es configure und main/php_config.h.in neu erzeugen muss oder nicht. Nach dem Konktaneieren zu der acconfig.h werden acinclude.m4 und build/libtool.m4 zu aclocal.m4 kontakeniert. Schließlich ruft build/build2.mk noch autoconf und autoheader auf, um die Dateien configure und main/php_config.h.in zu erzeugen.

aclocal.m4 definiert selbst lediglich viele m4-Hilfsmakros, die dann verwendet werden können. Dagegen ruft configure.in die Markos auf. Um die Modularität zu berücksichtigen, d. h. die vielen verschiedenen SAPIs und gebündelten Erweiterungen, enthält configure.in folgende zwei Zeilen (allerdings nicht direkt nacheinander, sondern an verschiedenen Stellen):

esyscmd(./build/config-stubs sapi)
esyscmd(./build/config-stubs ext)

Das Makro esyscmd führt dazu, dass das Script build/config-stubs aufgerufen wird. Dieses gibt für ein gegebenes Verzeichnis alle Dateien aus, die config0.m4, config.m4 und config9.m4 heißen und sich in einem Unterverzeichnis vom angegebenen Verzeichnis befinden. Der Aufruf von build/config-stubs sapi gibt bei PHP 5.3 zum Beispiel folgende Ausgabe zurück:

sinclude(sapi/*/config0.m4)
sinclude(sapi/aolserver/config.m4)
sinclude(sapi/apache2filter/config.m4)
...

Diese Zeilen werden dann von dem esyscmd-Aufruf ausgeführt, d. h. diese m4-Dateien werden alle eingebunden in configure.in. Da die Liste dynamisch erzeugt wird, muss in ext/ oder sapi/ nur ein neues Unterverzeichnis angelegt werden, das eine config.m4 enthält, und die Erweiterung kann direkt mitverwendet werden.

configure.in bindet außerdem noch folgende Dateien direkt ein: Zend/acinclude.m4, Zend/Zend.m4, TSRM/threads.m4 und TSRM/tsrm.m4.

Aus configure.in, aclocal.m4 und allein eingebundenen Dateien erzeugt autoconf das Script configure und autoheader erzeugt die Datei main/php_config.h.in – hier lädt autoheader allerdings noch implizit die Datei acconfig.h hinzu, die vorher generiert wurde.

Starten von ./configure, das erzeugte Makefile

Beim Aufruf von ./configure kann man diverse Erweiterungen aktivieren/deaktivieren mit --enable-ERWEITERUNG bzw. --disable-ERWEITERUNG. Außerdem gibt es noch --disable-all, was alles bis auf wenige Ausnahmen (standard kann man zum Beispiel nicht deaktivieren) deaktiviert. Zusätzlich kann man SAPIs über Flags aktivieren / deaktivieren.

Die verschiedenen config*.m4 von den SAPIs und Erweiterungen enthalten dann Makro-Aufrufe, die zum einen diese Flags bereitstellen, zum anderen jedoch das Buildsystem anweisen, den Code der SAPIs und Erweiterungen mitzukompilieren. Dies kann auf zwei Arten geschehen:

  • Zum einen kann über Makros wie PHP_NEW_EXTENSION, PHP_SELECT_SAPI oder PHP_ADD_SOURCES (alle in acinclude.m4 definiert) das Buildsystem angewiesen werden, bestimmte C-Dateien mitzukompilieren. Hierfür werden Regeln zur Datei Makefile.objects im Hauptverzeichnis hinzugefügt.
  • Zum anderen kann man selbst eigene Makefile-Fragmente per PHP_ADD_MAKEFILE_FRAGMENT hinzufügen, die dann von configure zu Makefile.fragments im Hauptverzeichnis zusammenkonkateniert werden.

Am Ende der Ausführung von configure werden Makefile.global (ist fest im PHP-Quellcode enthalten), Makefile.fragments (alle Fragmente) und Makefile.objects (dynamisch generiert aus allen Objekten, die kompiliert werden sollen) dann zu einer großen Makefile im Hauptverzeichnis konkateniert.

PHP verwendet folglich nur ein einziges großes Makefile im Hauptverzeichnis und nicht wie die meisten anderen Software ein Makefile pro Verzeichnis, die rekursiv aufgerufen werden.

Buildsystem unter UNIX: phpize

Erweiterungen unter UNIX kann man jedoch nicht nur kompilieren, wenn sie im PHP-Quellcode-Verzeichnis enthalten sind (wie die gebündelten Erweiterungen), sondern auch, wenn sie in einem eigenen Verzeichnis liegen – zumindest solange die Header-Dateien von PHP irgendwo verfügbar sind.

Um das Kompilieren und Installieren von PHP-Erweiterungen zu vereinfachen haben die PHP-Entwickler daher das Tool phpize entwickelt. Wenn dieses in einem Verzeichnis ausgeführt wird, das eine Datei config.m4 enthält, werden bestimmte Dateien für des PHP-Buildsystems herüberkopiert, zum Beispiel auch eine speziell angelegte configure.in, die neben einigen Standard-Checks eine Zeile enthält, die nur config.m4 einbindet. Sobald dies geschehen ist, werden autoconf und autoheader ausgeführt, sodass im aktuellen Verzeichnis ein configure-Script enthalten ist. Dieses erzeugt beim Ausführen ein Makefile, mit dem die Erweiterung alleine kompiliert und installiert werden kann.

Dieses Verfahren erlaubt es, dass man Erweiterungen kompilieren kann, indem man entweder das Verzeichnis als Unterverzeichnis von ext/ im PHP-Baum anlegt und mit PHP mitkompiliert – oder indem im Verzeichnis phpize aufruft und die Erweiterung separat kompiliert.