Ich habe vor kurzen gerade ein merkwürdiges Problem gelöst: Je nachdem ob ich den internen oder externen Compiler verwendet hatte, hat Delphi mir eine andere Exe-Datei ausgespuckt.

Man würde ja eigentlich erwarten das immer das gleiche Ergebnis herauskommt, aber das ist nicht der Fall, aber erstmal ein Überblick über die verschiedenen Möglichkeiten mit Delphi zu kompilieren:

Delphi Compilerbefehle

1) interner Compiler

klar, das ist die Funktion die die meisten von euch sicherlich im alltäglichen Gebrauch haben. "Projekt->compilieren", bzw im Objektexplorer rechte Maustaste und "compilieren" ist die Standardmethode.

2) interner Compiler via Kommandozeile

Aber der interne Compiler läßt sich auch über die Kommandozeile aufrufen. Mit folgendem Befehl startet die Delphi Oberfläche, lädt das angegebene Projekt, kompiliert es, und die Oberfläche beendet sich wieder:

<DelphiRoot>\bin\bds.exe -b <path_to_dproj>

3) Delphi-Compiler an der Kommandozeile (Compiler CDD32/ DCC64)

Delphi läßt sich aber auch direkt an der Kommandozeile kompilieren. Die erste und ältere Möglichkeit ist es, direkt die dpr-Datei zu kompilieren. Diese Möglichkeit besteht nicht für die Community-Edition (Stand Delphi 11). Es handelt sich hierbei um eine der wenigen Einschränkung zu Professional und höher.

Der Syntax sieht hierbei so aus:

<DelphiRoot>\bin\rsvars.bat

<DelphiRoot>\bin\dcc32.exe [options] <path_to_dproj>[options]
oder
<DelphiRoot>\bin\dcc64.exe [options] <path_to_dproj>[options]

Es gibt also eine 32bit und eine64 Version vom Compiler, als Optionen, können zuallererst einmal die verschiedenen Pfade zu den Komponenten angegeben werden, und die Optionen die man aus den Einstellungen kennt ("Projekt->Optionen->Delphi Compiler") gesetzt werden. Zusätzlich kann man aber auch die Compilerschalter setzen, und so ggf die des Sourcecodes übersteuern. Alles in allem entsteht dann so eine Kommandozeile, welche man auch sieht, wenn man beim kompilieren in der Oberfläche im "Meldungen"-Fenster sieht. RSVars.bat ist übrigens erforderlich, um die benötigen Umgebungspfade zu laden. 

4) MSBuild an der Kommandozeile

MSBuild ist ein Build-Werkzeug, welches anhand einer Konfigurationsdatei (in unserem Falle die DProj), sich alle Aufgaben zum kompilieren heraussucht, und auch für den Compiler die entsprechende Kommandozeile erzeugt. Es wird ja nicht nur der Compiler ausgeführt, sondern auch der Ressource-Compiler und vor und nach dem Build, können ja auch Skripte ausgeführt werden. 

Der Syntax:

<DelphiRoot>\bin\rsvars.bat

MSBuild <Projektname> [/t:<Zielname>][/p:config=<Konfigurationsname>][target:Deploy][/p:platform=<Name_des_Plattformknotens>]

msbuild MyApps_CBuilder.cbproj /t:Build /p:config=Debug /p:platform=Android

Man sieht, dank der Konfigurationsdatei, ist die Kommandozeile erheblich einfacher aufgebaut, und wie bereits erwähnt, tut MSBuild auch wesentlich mehr.

Hier nochmal eine kurze Erklärung zu den Parametern:

  • <Projektname> ist der Pfad zur dproj-Datei
  • /t:  (oder /target:) gibt die Compileraufgabe an, also Clean, Build, Make, Deploy
  • /p:config= gibt den Namen des Konfigurationsprofils an. Standardmäßig hat ein Projekt "Debug" und "Release", aber auch eigene gehen auch
  • /p:platform= gibt die plattform an, z.B: Win32, Win64, Android, Android64, iOSDevice32, iOSDevice64, iOSSimulator, OSX32, OSX64, OSXARM64

RSVars.bat ist hier ebenfalls erforderlich, um die benötigen Umgebungspfade zu laden. 

Unterschiedliches Verhalten der Compiler

Nun zum eigentlichen Thema. Die Compilerbefehle verhalten sich unterschiedlich, und das erzeugt natürlich auch unterschiedliche Ergebnisse.

Compiler executable Größe
intern 30MB
intern via Kommandozeile 28MB
Compiler DCC32/DCC64 60MB
MSBuild 60MB
extern/MSBuild mit MakeJCLDbg (siehe unten)  66MB

Der offensichtliche Unterschied ist, das der "interne Compiler", die "interne Kommandozeile" und "MSBuild" eine Werkzeugkette verwenden, also alle Aufgaben erledigen, während der Kommandozeilencompiler wirklich nur die Exe erzeugt. Nun gibt es aber noch die Besonderheit, das nur der "interne Compiler" auch Plugins beachtet, die werden sonst nicht mit geladen. Sollte also ein Tool die Source manipulieren, oder sich in den Build-Prozess einklinken, dann gehts nur mit dem "internen Compiler". Der Kommandozeilencompiler beachtet logischerweise auch keine Pre/Post-Skripte, und kompiliert auch keine Ressourcen. 

Eine spezielle Besonderheit, die mich Nerven gekostet hat ist die Tatsache, das der Kommandozeilencompiler auch "cfg"-Dateien unterstützt. liegt so eine im Verzeichnis, wird diese beachtet, und kann somit das Verhalten übersteuern. Der "interne Compiler" und auch die "interne Kommandozeile" ignoriert diese Datei. Die "cfg"-Dateien sind in einer Zeit vor MSBuild entstanden, um den Prozess zu vereinfachen. Aktuell übernimmt MSBuild diese Aufgabe (beachtet aber die cfg-Dateien mit, da er auf den Kommandozeilencompiler zugreift). 

Hmm, hört sich kompliziert an, ist es auch. Deshalb hier eine Übersicht hierrüber.

Compiler pre/post Skripte RC-Compiler cfg-Dateien IDE-Plugins
interner Compiler Ja Ja Nein Ja
interner Compiler via Kommandozeile Ja Ja Nein Nein
Compiler (DCC32/DCC64) Nein Nein Ja Nein
MSBuild Ja Ja Ja Nein

Es gibt also keine Variante die alles Beachtet, dennoch benötgt man ja die cfg-Dateien eigentlich nicht, man kann es über die IDE konfigurieren, somit muss man nur Obacht auf die Plugins haben, wenn man das ausklammert, dann erzeugen der "interne Compiler" und auch MSBuild das gleiche Ergebnis.

JCLDebug

In meinem Falle, ging es erstmal um JCLDebug. Das wird standardmäßig via Plugin in die Exe integriert. Es gibt aber die Möglichkeit über das Tool Makejdbg das gleiche Ergebnis mit MSBuild zu erreichen. Dazu den Befehl entweder per Post-Skript einbinden, oder seperat mit einem weiteren Kommandozeilenaufruf ausführen.

Der Syntax zu diesem Tool ist übrigens:

<Embarcadero_Benutzer_Pfad>\Studio\<bdsVersion>\CatalogRepository\<JCLDir>\bin\makeJclDbg -J -E -M <Pfad_zu_map_Datei>
  • -J bedeutet: Erstelle aus der Map-Datei eine jdbg - Datei
  • -E bedeutet: integriere jdbg-Datei in die exe (diese muss im gleichen Verzeichnis liegen)
  • -M bedeutet: lösche die "überflüssige" Map-Datei

Klar ist, dass das ganze funktioniert, muss natürlich die Map-Datei in den Compileroptionen angeschaltet sein.

Gesagt getan, nun war ich aber auf 66MB ich hatte mich also vom Ziel 30MB noch weiter entfernt. Etwas anderes muss noch zum Problem führen. Via Diff hatte ich schon gesehen: Das sind irgendwelche Debug-Infos. Und wie oben in der Tabelle zu sehen: Da wird wohl eine cfg daran Schuld sein.

TDS-Informationen

Wie sich herausstellte, lag noch eine alte cfg-Datei im Verzeichnis des Projektes. Die war noch aus alten Delphi 7 Zeiten übrig geblieben. Bisher hatte sie gar nicht gestört. Nun musste Sie aber weg. Und Siehe da, die exe war nun nur noch die gewünschten 30MB groß. In der cfg war neben zahlreichen Defaults auch ein paar kleinere Unterschiede. Das wichtigste war, das TDS-Symbole (Turbo Debugger Symbole) angeschaltet waren. Ich hatte schon sowas vermutet, da ich durch einen Diff gesehen hatte, das hier scheinbar Debug-Infos in einem mir nicht zuordnenbaren Format integriert waren. Das hat also die zusätzliche Größe erzeugt. Merkwürdig daran ist nur, das:

  1. TDS meines Wissens nach bei 64-bit exe gar nicht funktioniert (und das war eine)
  2. TDS nur in Form von spearaten tds-Dateien erzeugt wird, also nicht integriert wird.

Aber das Ziel, gleiche Compiler-Ergebnisse zu erzielen wurde erreicht, nur Merkwürdiglkeit mit TDS, kann ja jemand noch etwas in die Kommentare posten, wenn er dazu Infos hat.


Keine Kommentare zu “Unterschiedliche Ergebnisse beim kompilieren mit Delphi”

Kommentar hinterlassen

Als Antwort auf Some User