Entwickelt man Anwendersoftware für Kunden, wollen diese das Programm meist nicht als Quelltext sondern als “anklickbare” Programmdatei ausgeliefert haben. In diesem Artikel möchte ich zeigen, wie man sich ganz einfach ein Tcl-Skript erstellt, welches bei Aufruf aus dem Projektordner selbstständig Programmdateien für alle möglichen Systeme erstellen kann – ganz ohne Compiler & Co.
Vorbereitungen
Zunächst müssen wir uns eine Umgebung einrichten, die Tcl-Interpreter und benötigte Packages für alle Systeme enthält. Bei der Wahl der Tcl/Tk-Interpreter empfehle ich vor-kompilierte Programmdateien, die bereits das Package “starkit” und ein virtuelles Dateisystem auf Basis der Metakit4-Datenbank integriert haben – so können jegliche Packages und Skriptdateien direkt an die Runtime-Datei in einem virtuellen Dateisystem angehangen werden.
Als Quelle für Runtime-Dateien sowie die typischsten Packages für Tcl empfehle ich das Repository “Teapot” von ActiveState. Die Runtime-Dateien findet man unter den Namen “base-tcl” und “base-tk” (mit Tk), sowie unter “base-tcl-thread” und “base-tk-thread” (mit integrierter Erweiterung für Multithreading).
Nun legen wir uns eine Ordner-Hierarchie mit Runtime-Dateien und Packages für alle Systeme an, für die wir Programmdateien erstellen möchten, in etwa so:
Projektordner
+-src/
+-runtime/
| +-win32-ix86
| +-linux-ix86
| +-linux-ix86_64
| +-macosx-universal
+-lib/
| +-win32-ix86/
| | +-lib1/
| | +-lib2/
| | +-...
| +-linux-ix86/
| | +-lib1/
| | +-lib2/
| | +-...
| +-...
+-out/
+-wrap.tcl
Des weiteren benötigen wir das Package “vfs::mk4″ um das virtuelle Dateisystem unserer Programmdateien zu bearbeiten, dieses kann jedoch überall liegen, wo es von Tcl gefunden wird, zum Beispiel im ‘lib’-Ordner der lokalen Tcl-Installation.
Schritt 1 – Ausgabedatei erstellen
Ab jetzt arbeiten wir in der Datei ‘wrap.tcl’! Zunächst definieren wir mit einer Schleife die Verarbeitung für alle verschiedenen Plattformen, um Code zu sparen:
#!/usr/bin/tclsh
# ^- Unix-Kompatibilität
# Package vfs::mk4 für virtuelle Dateisysteme laden
package require vfs::mk4
# Arbeitspfad korrigieren (nur zur Sicherheit)
cd [file dirname [info script]]
# Für jede verfügbare Runtime-Datei
foreach runtime [glob -nocomplain "runtime/*"] {
Ab jetzt läuft das restliche Programm innerhalb dieser foreach-Schleife! Danach kopieren wir die Runtime-Datei für das jeweilige System in den Ausgabe-Ordner ‘out’ und Mounten diese unter dem Namen ‘outfile’ mittels vfs::mk4:
file copy -force $runtime [file join "out" [file tail $runtime]]
set Mountpoint [vfs::mk4::Mount [file join "out" [file tail $runtime]] "outfile"]
Schritt 2 – Skripte und Packages kopieren
Zunächst kopieren wir alle Skript-Dateien inkl. eventueller Unterordner aus dem Verzeichnis ‘src’ in unser virtuelles Dateisystem. Wichtig ist hierbei, dass das Startskript als ‘main.tcl’ benannt ist und folgenden Header zur Initialisierung des Starkit besitzt:
package require starkit
starkit::startup
Diese Zeilen mounten mittels Package ‘starkit’ das virtuelle Dateisystem am Ende der Programmdatei, damit auf benötigte Pakete und weitere Quellcode-Dateien zugegriffen werden kann – daher unbedingt an den Anfang schreiben! Doch nun zum kopieren der Skript-Dateien:
foreach scriptfile [glob -nocomplain "src/*"] {
file copy -force $scriptfile [file join "outfile" [file tail $scriptfile]]
}
Danach kopieren wir alle benötigten Pakete aus dem zugehörigen Lib-Ordner. Die Pakete sind nach Betriebssystem geordnet und dazu die Unterordner wie das Runtime benannt, um sicher zustellen, dass nicht z.B. das Windows-Programm die Pakete für Mac OS X bekommt.
foreach library [glob -nocomplain [file join "lib" [file tail $runtime] "*"]] {
file copy -force $library [file join "outfile/lib" [file tail $library]]
}
Bei beiden Kopierbefehlen braucht man die Option -force, da Tcl den Kopiervorgang sonst abbrechen würde, wenn es sich beim zu kopierenden Objekt um einen Ordner handelt.
Schritt 3 – Fertigstellen
Um den Vorgang erfolgreich abzuschließen, müssen wir noch das virtuelle Dateisystem unmounten:
vfs::mk4::Unmount $Mountpoint [file join "out" [file tail $runtime]]
Und natürlich die Klammer unserer foreach-Schleife wieder schließen:
}
Den gesamten Quelltext in einer leicht ausgebauten Variante, u.a. mit Statusausgabe auf der Konsole, gibt es hier zum Download.
Plattformspezifische Verbesserungen
Windows
Für Windows-Systeme empfiehlt es sich, die Runtime-Datei zu personalisieren. Das Icon und die VersionInfo-Angaben (Hersteller, Programmname, Version etc.) lassen sich recht komfortabel mit dem kostenfreien Tool Resource Hacker bearbeiten. Dabei empfehle ich, die Runtime-Datei direkt zu modifizieren und dann in das ‘runtime’-Verzeichnis zu legen, damit die Änderungen beim erstellen der Programmdateien direkt übernommen werden – so muss man nicht nach jeden neu-erstellen der Dateien die Informationen wieder ändern.
Mac OS X
Das für Mac OS X erstellte Programm wird wie ein Konsolen-Programm behandelt, da es keine Metainformationen hat, wir schaffen in diesem Fall Abhilfe, indem wir für die Programmdatei ein App-Verzeichnis mit Metainformationen erstellen. Dieses sollte folgende Struktur haben:
Mein Programm.app/
+-Contents/
+-MacOS/
| +-PROGRAMMDATEI
+-Resources/
| +-ICON.icns
+-Info.plist
Wobei PROGRAMMDATEI der Name der Programmdatei ist, die in diesem Fall beliebig benannt werden kann – da den Namen später sowieso kein Endanwender zu sehen bekommt, reicht auch ein simpler Name wie “main” oder “program”. Die Datei ICON.icns kann auch beliebig benannt werden und ist eine Symbol-Datei im Mac-eignen Format ‘ICNS’, dieses kann zum Beispiel mit dem Windows-Programm ‘Axialis Icon Workshop’ erstellt werden.
In der Datei ‘Info.plist’ befindet sich alle Metainformationen für das Programm, die Datei enthält in unserem minimal-Fall nur wenige Angaben, hier habe ich eine Beispieldatei zum Download bereitgestellt.
Weitere Informationen
Der Prozess mit den nachträglichen Anpassungen für Mac OS X lässt sich natürlich auch automatisieren – dieses Beispiel schöpft nur ein Minimum der verfügbaren Möglichkeiten zur Erstellung von standalone-Programmdateien aus.
Mit entsprechenden Runtime-Dateien, die mit Mk4VFS kompatibel sind und den entsprechenden Packages sollte sich dieses Beispiel auf jegliche Plattformen erweitern lassen – mit einem Linux-Runtime für die ARM-Architektur bekommt man Beispielsweise auch GUI-Programme für Tablet-PCs wie das WeTab hin.
Kommentare und Anregungen sind immer Willkommen!
Anwendersoftware hat fertig ausgeliefert zu werden. Schon die Vorbereitungen sind zu sehr IT, als das sich dies jemals durchsetzen würde. Software soll Funktionen. Technik ist da um Arbeit abzunehmen, d.h klicken und fertig für den Anwender.
Sehr schön, dass du als erster diesen Beitrag kommentierst und offenbar noch nie mit Tcl gearbeitet hast. Tcl ist keine Compilersprache, sondern eine Skriptsprache – also ursprünglich nicht entworfen um “Klick & Fertig” Programmdateien zu erstellen, wie von dir geschildert. Es gibt verschiedene Ansätze trotzdem alleinstehende Programmpakete zu erstellen – in diesem Beitrag habe ich nur versucht, Interessierten den Ansatz Programmdaten in ein VFS an das Runtime anzuhängen, näher zu bringen, da dieser plattformunabhängig funktioniert. PS: Bevor du weiter große Töne von dir gibst, nutz mein Kontaktformular und lass ein paar Referenzen von dir sehen