När du underhåller en VCL-komponent skriven i Delphi som används av både Delphi- och C++Builder-användare inser du snabbt att de två byggsystemen hanterar beroenden väldigt olika. En enhet (unit) som kompileras perfekt i Delphi kan orsaka katastrofala länkningsfel i C++Builder. Denna avvikelse är en vanlig fälla för komponentförfattare, särskilt när de lägger till nya interna enheter i ett befintligt bibliotek.
Här är en detaljerad genomgång av varför detta händer, med verkliga exempel från HotXLS-komponenten, och hur du gör din korskompileringsprocess bombsäker med hjälp av explicita inkluderingar, {$HPPEMIT} och pragma-länkning.
Fällan: Implicit kompilering vs. Explicit inkludering
Anta att du skapar en ny Delphi-enhet, lxXlsSummary.pas, för att hantera dokumentmetadata, och du använder (uses) den i din huvudsakliga tolkningsenhet, lxRead.pas. Du klickar på kompilera i Delphi IDE, bygget blir grönt och du skickar ut uppdateringen.
Dagen efter rapporterar dina C++Builder-användare ett fel under länkningsfasen: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj.
Delphi-metoden (dcc32)
När Delphi-kompilatorn bearbetar ett paket (.dpk) tittar den på enheterna som uttryckligen listas i contains-satsen. Om en av dessa enheter använder (uses) en extern enhet (som lxXlsSummary.pas) som inte finns i contains-listan, utför Delphi-kompilatorn implicit statisk länkning. Den hittar helt enkelt .pas-filen i sökvägen, kompilerar den till en .dcu, och bakar in den i den resulterande .bpl. Bygget lyckas, vilket helt döljer utelämnandet.
C++Builder-metoden (MSBuild / .cbproj)
C++Builders byggsystem är mycket striktare. Det genererar bara C++-objektfiler (.obj) och header-filer (.hpp) för de Delphi-enheter som uttryckligen listas i objektgruppen <DelphiCompile> i .cbproj-filen. Eftersom lxXlsSummary.pas aldrig uttryckligen registrerades i projektfilen skapas ingen lxXlsSummary.obj. När länkaren försöker lösa anropen som görs av lxRead.obj saknas symbolerna, vilket leder till ett fel för en olöst extern referens (unresolved external error).
Lösa externa referenser med Pragma Link och HPPEMIT
Om du vill säkerställa att en enhet är korrekt länkad i C++ utan att tvinga användaren att manuellt lägga till .obj-filen i sitt projekt, kan du använda Delphis {$HPPEMIT}-direktiv. Detta talar om för Delphi-kompilatorn att injicera ett specifikt C++ #pragma link-direktiv i den genererade .hpp-filen.
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.
Den gyllene regeln för komponentunderhåll
För att undvika att bryta C++Builder-byggen helt och hållet måste du anta en strikt registreringspolicy. När en ny Pascal-enhet läggs till i ditt bibliotek måste den uttryckligen registreras i alla tre projektfiltyper samtidigt.
1. Uppdatera C++Builder-projektet (.cbproj / .bpk)
Öppna .cbproj-filen i en textredigerare och lägg till den nya enheten i kompileringslistan, och se till att du anger en unik byggordning (build order). Om du använder äldre C++Builder-versioner med .bpk-filer, se till att taggen <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> läggs till.
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Uppdatera Delphi-paketet (.dpk)
Lägg till enheten i den explicita contains-satsen. Detta säkerställer att Delphi-kompilatorn inte behöver förlita sig på implicit länkning, vilket i allmänhet ändå anses vara dålig praxis.
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validering av kontinuerlig integration (CI)
Det ultimata försvaret mot denna fälla är CI/CD-validering. Förlita dig aldrig enbart på ett lyckat Delphi-bygge innan du levererar en komponent för två språk. Dina byggskript måste anropa MSBuild eller kommandoradsverktygen bcc32c på C++Builder-projekten (t.ex. build-Win32-Lib-CB.cmd) och köra en fullständig länkning av C++ trial-versionen och fullständiga demonstrationer. Först när C++-länkaren lyckas kan du vara säker på att alla Delphi-enheter är korrekt registrerade och exponerar sina symboler för C++-körtiden (runtime).
Obs: Kompatibilitet för korsplattformskompilatorer upprätthålls strikt över Delphi- och C++Builder-utgåvorna av HotXLS VCL Component.