Alternatives Build System für RAD Studio 2007
-
Hallo,
ich habe seit gestern ein merkwürdiges Phänomen mit dem RAD Studio 2007:
Es geht um ein recht großes Projekt mit ca. 1100 Quelltextdateien, die zum Teil in unterschiedlichen statischen Bibliotheken liegen. Die Abhängigkeiten zwischen den Bibliotheken sieht etwa so aus:Executable | | LibFTC | +----------+----------+----------+ | | | | LibGFX | LibDF LibCPC | | | | +----------+----------+----------+ | LibUtil
Jetzt habe ich festgestellt, dass in einer Methode einer Klasse in der Bibliothek LibUtil ein off-by-one Fehler war und dieser Fehler hat sich in genau einer Methode einer Klasse in der Bibliothek LibDF bermerkbar gemacht. Also habe ich den Fehler korrigiert und getestet, aber das Ergebnis war immer noch um eins zu klein (konkret: 441 statt 442). Außerdem konnte ich nicht in die Methode hineindebuggen, obwohl das komplette Projekt im Debug Modus kompiliert wurde. Um das auf die Spitze zu treiben habe ich als Ergbnis einen fixen Wert zurückgeben lassen (4711), nur um zu sehen, was der Compiler daraus macht. Egal was ich auch ausprobiert habe, das Ergebnis war immer 441 und ich konnte nie in die Funktion hineindebuggen. Genaugenommen konnte ich sogar nur in zwei Methoden debuggen, für den Rest hat mir die IDE keine Breakpointmöglichkeiten angeboten und beim Single-Execution-Step hat der Debugger den Methodenaufruf immer übersprungen.
Ich habe folgende Sachen ausprobiert, aber keine davon hat das Problem gelöst:- alle obj, tds und lib Dateien gelöscht und alle Projekte komplett neu übersetzt
- kompletten Rechner nach Quelltextduplikaten durchsucht
- die entsprechende .h und .cpp aus dem Projekt entfernt und neu hinzugefügt
- eine neue Build Konfiguration erstellt und darin alle Debugging Informationen aktiviert
- alle Projekte im Release und dann im Debug Modus neu übersetzt
- das LibUtil Projekt neu aufgebautIn einem Akt der Verzweiflung habe ich die Klasse umbenannt (MyClass -> MyClassEx), danach war zumindest für diese Klasse alles so, wie es sein sollte. Der Off-by-one Bug war gefixt und ich konnte in sämtliche Methoden der Klasse hineindebuggen. In der .cpp Datei befindet sich allerdings noch eine Hilfsklasse, in deren Destruktor ich dann nicht mehr hineindebuggen konnte. Zur Laufzeit kam es dann zu Schutzverletzungen, weil der Destruktor dieser Hilfsklasse nicht mehr ausgeführt wurde. Erst nachdem ich auch die Hilfsklasse umbenannt (MyClassHelper -> MyClassHelperEx) habe war wieder alles ok.
Jetzt bin natürlich zutiefst erschüttert, weil ich nicht mehr sagen kann, welchen Code der Compiler/Linker erzeugt, denn offensichtlich passt der Quelltext nicht zu dem Code, der ausgeführt wird. Mich wundert bis jetzt, woher der Compiler/Linker den fehlerhaften Code hat, denn der Quelltext war ja korrigiert und alle obj, tds und lib Dateien habe ich vorm Übersetzen gelöscht.
Hatte jemand von euch mal ein ähnliches Problem und wie konntet ihr das lösen?
Nachdem ich auch mit dem Linker schlechte Erfahrungen gemacht habe (siehe ilink32 out of memory @ embarcadero und ilink32 out of memory @ cpp.de) und ich den Unilink Linker aus dem Thread nicht zum Laufen bekommen suche ich ein alternatives Build System für das RAD Studio 2007.
Ein Upgrade auf XE5 kommt im Moment nicht infrage, da dort ebenfalls noch ilink32 für 32bit Applikationen benutzt wird.Für Tipps und Empfehlungen bin ich wirklich dankbar.
-
Das klingt ja außerordentlich lästig. Ich hatte schon ähnliche Probleme, aber es war meistens etwas mit doppelten Headerdateien.
Um herauszufinden, ob und inwieweit die kompilierte Funktion mit deinem Quelltext zusammenhängt, könntest du erstmal folgendes probieren:
- mit dem Disassembler hineinsteppen. Das geht in jedem Fall, und so kannst du sehen, welcher Code tatsächlich ausgeführt wird
- in deiner Funktion einen manuellen Debug-Break (asm int 3;
) einfügen und schauen, ob er auch im Kompilat vorhanden ist
- du könntest die Funktion auskommentieren (mit TDump verifizieren, daß der entsprechende Export aus der .obj-Datei verschwunden ist!) und testen, ob es dann einen Linkerfehler gibt. Das schließt aus, daß das Symbol dem Linker mehrfach zur Verfügung stehtDaß es manchmal keine Debug-Informationen gibt, ist leider beim BCC nicht so ungewöhnlich. Aber trotzdem liest sich das eher wie ein Indiz, daß noch irgendwo eine .obj-Datei herumlag oder die alte .obj-Datei in eine Bibliothek hineingelinkt worden ist...
Wenn das alles zu nichts führt und du größere Kaliber brauchst, dann gäbe es noch folgendes:
- du könntest mit ProcMon (Sysinternals) protokollieren, welche .lib- und .obj-Dateien der Linker tatsächlich benutzt. Das ist etwas aufwendig und erfordert einige Detailarbeit mit den Filtern, aber wenn sich noch irgendwo eine .obj-Datei versteckt, kannst du sie so finden
- wenn sich die Zweifel erhärten, daß BCC eine fehlerhafte .obj-Datei erstellt, kannst du mit TDump die kompilierte Funktion direkt aus der .obj-Datei beziehen (natürlich dann ohne Fixups und nur als Opcodes, nicht mit Disassembler); mit einem Ansatz wie oben (asm int 3;
) könntest du nachprüfen, inwieweit da dein Quelltext kompiliert wird oder etwas anderesÜbrigens, was ILINK32 in XE5 angeht: nach meinem Kenntnisstand ist auch ILINK64 nicht nur ein ILINK32-Abkömmling, sondern auch ein 32-Bit-Prozeß, d.h. es wird dieselben Probleme geben wie mit ILINK32. Sehr schade, daß sie nicht die Gelegenheit genutzt haben, um neben der neuen Compiler- auch eine neue Linkerarchitektur zu bemühen
-
Danke für die Hinweise, audacia.
Doppelte Quelltext-, Header- oder .obj Dateien kann ich definitiv ausschließen, der Entwicklungs PC ist frisch aufgesetzt und ich habe die Quelltexte frisch ausgecheckt.
Ich habe das einen Verdacht mit einer Bibliothek eines Drittherstellers, ich habe die Klassennamen und Namen der Include Guards geändert und damit läuft´s jetzt. Trotzdem bleibt das ungute Gefühl, dass irgendein selten benutzter Codeteil Blödsinn baut und sich die Software komisch verhält.
Ich dachte, zur LLVM Toolchain gehört auch ein neuer Linker, der clang Compiler erzeugt doch ELF Object Files o.O. Das sind ja rosige Aussichten...
-
Solche Fehler kenne ich eigentlich nur bei Unterschieden in den Compiler- und Linkereinstellungen der einzelnen LIBs die LibUtil einbinden.
Das führt dann dazu, dass im gelinkten Code insbesondere statischen Elemente doppelt vorhanden waren und es dann Zufall war welche Instanz dann an einer bestimmten Stelle zugegriffen wurde.