Bij het onderhouden van een VCL-component dat in Delphi is geschreven, maar door zowel Delphi- als C++Builder-gebruikers wordt gebruikt, realiseert u zich al snel dat de twee bouwsystemen heel anders omgaan met afhankelijkheden. Een unit die perfect compileert in Delphi, kan catastrofale linkerfouten veroorzaken in C++Builder. Deze discrepantie is een veelvoorkomende valkuil voor componentauteurs, vooral bij het toevoegen van nieuwe interne units aan een bestaande bibliotheek
Hier is een gedetailleerd overzicht van waarom dit gebeurt (met praktijkvoorbeelden uit het HotXLS-component) en hoe u uw cross-compilatieproces waterdicht kunt maken met behulp van expliciete includes, {$HPPEMIT} en pragma linking
De valkuil: Impliciete compilatie versus expliciete inclusie
Stel dat u een nieuwe Delphi-unit maakt, lxXlsSummary.pas, om documentmetadata te verwerken, en u voegt deze toe aan de uses-clausule in uw hoofdparsing-unit, lxRead.pas. U klikt op compileren in de Delphi IDE, de build wordt groen en u levert de update af
De volgende dag melden uw C++Builder-gebruikers een fout tijdens de linkfase: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
De Delphi-methode (dcc32)
Wanneer de Delphi-compiler een pakket (.dpk) verwerkt, kijkt deze naar de units die expliciet worden vermeld in de contains-clausule. Als een van die units een externe unit (zoals lxXlsSummary.pas) gebruikt die niet in de contains-lijst staat, voert de Delphi-compiler impliciete statische linking uit. Hij vindt simpelweg het .pas-bestand in het zoekpad, compileert het naar een .dcu en integreert het in de resulterende .bpl. De build slaagt, waardoor de weglating volledig wordt gemaskeerd
De C++Builder-methode (MSBuild / .cbproj)
Het bouwsysteem van C++Builder is veel strikter. Het genereert alleen C++ objectbestanden (.obj) en headers (.hpp) voor de Delphi-units die expliciet zijn vermeld in de <DelphiCompile>-itemgroep van het .cbproj-bestand. Omdat lxXlsSummary.pas nooit expliciet is geregistreerd in het projectbestand, wordt er geen lxXlsSummary.obj gemaakt. Wanneer de linker de aanroepen probeert op te lossen die door lxRead.obj worden gedaan, ontbreken de symbolen, wat leidt tot een fout met onopgeloste externe verwijzingen
Externe verwijzingen oplossen met Pragma Link en HPPEMIT
Als u er zeker van wilt zijn dat een unit correct wordt gelinkt in C++ zonder de gebruiker te dwingen het .obj-bestand handmatig aan zijn project toe te voegen, kunt u Delphi's instructie {$HPPEMIT} gebruiken. Dit vertelt de Delphi-compiler om een specifieke C++ #pragma link-instructie in het gegenereerde .hpp-bestand in te voegen
unit lxXlsSummary;
interface
{$IFDEF WINDOWS}
// Inject a pragma link into the generated C++ header file
// This forces the C++ linker to include the corresponding .obj file
{$HPPEMIT '#pragma link "lxXlsSummary.obj"'}
{$ENDIF}
uses
SysUtils, Classes;
type
TXlsSummaryInfo = class(TObject)
public
Title: string;
Author: string;
CreateTime: TDateTime;
end;
function XlsReadSummaryInformation(const FileName: string): TXlsSummaryInfo;
implementation
function XlsReadSummaryInformation(const FileName: string): TXlsSummaryInfo;
begin
Result := TXlsSummaryInfo.Create;
// Metadata extraction logic here
end;
end.
Wanneer C++Builder lxXlsSummary.hpp opneemt, komt de compiler de #pragma link tegen en vertelt deze de linker (ILINK32/ILINK64) automatisch om symbolen uit lxXlsSummary.obj op te lossen
De gouden regel voor componentonderhoud
Om te voorkomen dat C++Builder-builds volledig kapotgaan, moet u een strikt registratiebeleid hanteren. Telkens wanneer een nieuwe Pascal-unit aan uw bibliotheek wordt toegevoegd, moet deze expliciet en tegelijkertijd in alle drie de projectbestandstypen worden geregistreerd
1. Werk het C++Builder-project bij (.cbproj / .bpk)
Open het .cbproj-bestand in een teksteditor en voeg de nieuwe unit toe aan de compilatielijst, en zorg ervoor dat u een unieke buildvolgorde opgeeft. Als u oudere C++Builder-versies met .bpk-bestanden gebruikt, zorg er dan voor dat de tag <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> is toegevoegd
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Werk het Delphi-pakket bij (.dpk)
Voeg de unit toe aan de expliciete contains-clausule. Dit zorgt ervoor dat de Delphi-compiler niet hoeft te vertrouwen op impliciete linking, wat over het algemeen toch als een slechte praktijk wordt beschouwd
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Continue integratie validatie
De ultieme verdediging tegen deze valkuil is CI/CD-validatie. Vertrouw nooit alleen op een succesvolle Delphi-build voordat u een tweetalige component uitbrengt. Uw buildscripts moeten MSBuild of de opdrachtregelprogramma's van bcc32c aanroepen op de C++Builder-projecten (bijv. build-Win32-Lib-CB.cmd) en een volledige link van de C++ proefversie en de volledige demo's uitvoeren. Pas wanneer de C++-linker slaagt, kunt u er zeker van zijn dat alle Delphi-units correct zijn geregistreerd en hun symbolen aan de C++-runtime blootstellen
Opmerking: Cross-platform compilercompatibiliteit wordt strikt gehandhaafd in de Delphi- en C++Builder-edities van de HotXLS VCL Component