Mit Hilfe von Heat lässt sich die Fragmenterzeugung, welche ein Hauptbestandteil eines WiX Installers ist, automatisieren. Dies wurde bereits im Beitrag Automatische Fragmenterzeugung mit Heat gezeigt. Was aber, wenn die ein oder andere Datei im Installationsvorgang nicht kopiert werden soll oder der Component bzw. dem File noch Attribute hinzugefügt werden müssen? Das erzeugte Fragment manuell zu verändern ist nicht sehr ratsam, da dies bei jedem Durchlauf von Heat überschrieben wird.
Hier kommen XSL Transformationen zum Einsatz, welche dem Heat Vorgang angehängt werden können. Mit Hilfe einer solchen Transformation lässt sich die automatisierte Fragment Datei abschließend bearbeiten.
Im oben genannten Beitrag wurde bereits die Möglichkeit der Filterung kurz angesprochen. Mit Hilfe von XSL Transformationen können jedoch nicht nur Filter realisiert werden, sondern, wie der Name bereits sagt, auch ganze Transformationen vollzogen werden. Um jedoch Filterdateien selbst erzeugen zu können, bedarf es weiterer Erklärung.
XSLT, Extensible Stylesheet Language Transformation, ist um XML-Dokumente zu transformieren. Zwar besitzen die von Heat erzeugten Dateien die Endung .wxs sind aber gewöhnliche XML-Dokumente und können daher mit Hilfe von XSLT transformiert werden. Aber Vorsicht! WiX unterstützt leider nur XSL Version 1.0.
Im Folgenden werden Transformationen anhand von zwei Beispielen erläutert, um noch tiefer in die Thematik einzusteigen empfiehlt es sich die offizielle Dokumentation von XSLT 1.0 anzusehen. Das ganze Beispiel kann unter https://github.com/JohBa/WiXSamples/tree/master/HeatTransformation eingesehen werden.
Um eine Transformation anzuwenden erzeugen wir zunächst eine einfache XML Datei, die wir transform nennen. Um nicht nur eine leere XML Datei zu erzeugen, wird als erstes ein sogenannter identity transform angewendet, um die Sourcedaten in unsere Zieldaten zu übertragen. Dazu wird folgender Code benutzt.
Zunächst wenden wir template match an und übergeben @*|* um jedes Attribut (@*) und jedes Element (*) zu matchen. Innerhalb dieses templates wird copy auf die Attribute und Elemente angewendet, wodurch zunächst der komplette Inhalt kopiert wird. Um Einrückungen für die Lesbarkeit zu erhalten wird das output Element mit Attributen indent und method verwendet.
Dateien herausfiltern
Nun können wir beginnen einzelne Dateien herauszufiltern. Um ein File mit seiner Component aus dem Fragment zu entfernen sind im Grunde drei Zeilen notwendig.
In der ersten Zeile wird ein Key erzeugt, der einen match auf unser File durchführt. Dazu suchen wir in unseren Components, gehen tiefer in das File Element und suchen letztendlich im Source Attribut nach einem Teilstring (contains). Dieser ist im vorliegenden Beispiel unnecessary.xml. Mit use=“@Id“ speichern wir die Id des Files zwischen. Nun wollen wir die Component entfernen, dazu wird ein einfaches template angewendet. Im match Attribut suchen wir nun in den Komponenten nach dem zuvor gesicherten Key mit der entsprechenden Id. Da, im durch Heat erzeugten Fragment, jeweils ein Component sowie ein ComponentRef erzeugt werden, muss das selbe Template auf die ComponentRef Elemente angewendet werden.
Sollen mehrere Dateien ignoriert werden können wahlweise alle drei Zeilen kopiert und angepasst werden oder lediglich das key Element. Hierbei muss das match Attribut angepasst werden, das name Attribut des Keys muss jedoch identisch bleiben.
Im Beispiel wird contains verwendet, da es für viele Fälle ausreicht. Jedoch reicht diese Funktion in manchen Fällen nicht mehr, bspw. bei Dateien wie myApp.exe.config. Soll nun mit Hilfe von contains myApp.exe gefiltert werden, wird gleichzeitig auch myApp.exe.config gefiltert, da hier auch der Teilstring vorhanden ist. Hier wäre die Funktion ends-with hilfreich, leider gibt es diese in XSL 1.0 jedoch noch nicht. Wir können aber die Funktion substring verwenden um dies zu realisieren.
Mit Substring extrahieren wir einen Teilstring aus dem Source Attribut. Dazu subtrahieren wir von der Gesamtlänge unseres Source Strings unseren gesuchten String und addieren 1.
Attribute hinzufügen
Gelegentlich kann es vorkommen, dass ein Attribut einem File Element hinzugefügt werden muss. Soll bspw. eine bestimmte Datei nur als Read-Only Datei installiert werden, kann dies ebenfalls realisiert werden.
Zunächst verwenden wir wieder ein template mit match auf alle File Elemente und filtern hier mit Hilfe von contains das Source Attribut auf die Files welche im Source den Wert readonly.txt besitzen.
Um das Element zu erhalten, wird, ähnlich wie beim identity transform, copy in Verbindung mit apply-templates verwendet. Nun haben wir das betreffende Elemente zunächst lediglich kopiert. Um dem Knoten noch ein Attribut hinzuzufügen verwenden wir das XSL attribute Element. Über das name Attribut geben wir den Namen des hinzuzufügenden Attributs hinzu und können anschließend den Wert bestimmen.
Das beispielhafte Resultat ist im Folgenden zu sehen.
Fazit
Manchmal reichen einfache Heat Vorgänge nicht mehr aus, um Fragmente für WiX Installationsroutinen zu erzeugen. Mit XSL Transformationen besitzt man jedoch die Möglichkeit die Automatisierung beizubehalten und gleichzeitig individuelle Modifikationen auf das erzeugte Fragment auszuführen.