Når du vedlikeholder en VCL-komponent skrevet i Delphi, men brukt av både Delphi- og C++Builder-brukere, oppdager du raskt at de to byggesystemene behandler avhengigheter svært ulikt. En unit som kompilerer helt fint i Delphi, kan gi katastrofale lenkerfeil i C++Builder. Dette avviket er en hyppig felle for komponentforfattere, særlig når nye interne units legges til i et eksisterende bibliotek
Her er en detaljert gjennomgang av hvorfor dette skjer, med eksempler fra virkeligheten hentet fra Delphi-komponenten HotXLS, og av hvordan du gjør kryssbyggingen din skuddsikker med eksplisitte inkluderinger, {$HPPEMIT} og pragma-lenking
Fellen: implisitt kompilering kontra eksplisitt inkludering
Anta at du lager en ny Delphi-unit, lxXlsSummary.pas, for å håndtere dokumentmetadata, og at du legger den i uses i hovedtolkingsuniten din, lxRead.pas. Du trykker kompiler i Delphi-IDE-en, bygget blir grønt, og du sender ut oppdateringen
Dagen etter melder C++Builder-brukerne dine om en feil i lenkefasen: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
Delphi-måten (dcc32)
Når Delphi-kompilatoren behandler en pakke (.dpk), ser den på de unitene som er eksplisitt listet i contains-klausulen. Hvis en av dem har en ekstern unit i uses (som lxXlsSummary.pas) som ikke står i contains-listen, utfører Delphi-kompilatoren implisitt statisk lenking. Den finner rett og slett .pas-filen i søkestien, kompilerer den til en .dcu og baker den inn i den ferdige .bpl-en. Bygget lykkes, og utelatelsen blir fullstendig maskert
C++Builder-måten (MSBuild / .cbproj)
Byggesystemet i C++Builder er langt strengere. Det genererer C++-objektfiler (.obj) og headere (.hpp) bare for de Delphi-unitene som er eksplisitt listet i <DelphiCompile>-elementgruppen i .cbproj-filen. Fordi lxXlsSummary.pas aldri ble eksplisitt registrert i prosjektfilen, opprettes ingen lxXlsSummary.obj. Når lenkeren prøver å løse opp kallene fra lxRead.obj, mangler symbolene, og resultatet er en feil om et uløst eksternt symbol
Løse eksterne symboler med pragma link og HPPEMIT
Vil du sikre at en unit blir riktig lenket i C++ uten å tvinge brukeren til å legge .obj-filen manuelt inn i prosjektet sitt, kan du bruke Delphis {$HPPEMIT}-direktiv. Det ber Delphi-kompilatoren om å sprøyte et bestemt C++-direktiv, #pragma link, inn i den genererte .hpp-filen
unit lxXlsSummary;
interface
{$IFDEF WINDOWS}
// Sprøyt en pragma link inn i den genererte C++-headerfilen
// Dette tvinger C++-lenkeren til å ta med den tilhørende .obj-filen
{$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;
// Logikk for uttrekk av metadata her
end;
end.
Når C++Builder inkluderer lxXlsSummary.hpp, møter kompilatoren #pragma link og ber automatisk lenkeren (ILINK32/ILINK64) om å løse symboler fra lxXlsSummary.obj
Gullregelen for komponentvedlikehold
For å unngå å ødelegge C++Builder-bygg fullstendig må du innføre en streng registreringspolicy. Hver gang en ny Pascal-unit legges til i biblioteket ditt, må den registreres eksplisitt i alle tre prosjektfiltypene samtidig
1. Oppdater C++Builder-prosjektet (.cbproj / .bpk)
Åpne .cbproj-filen i en teksteditor og legg den nye uniten til i kompileringslisten, og pass på å gi den en unik byggerekkefølge. Bruker du eldre C++Builder-versjoner med .bpk-filer, må du sørge for at taggen <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> legges til
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Oppdater Delphi-pakken (.dpk)
Legg uniten til i den eksplisitte contains-klausulen. Da slipper Delphi-kompilatoren å lene seg på implisitt lenking, som uansett regnes som dårlig praksis
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validering med kontinuerlig integrasjon
Det ytterste forsvaret mot denne fellen er validering i CI/CD. Stol aldri på et vellykket Delphi-bygg alene før du sender ut en komponent for to språk. Byggeskriptene dine må kalle MSBuild eller kommandolinjeverktøyene bcc32c på C++Builder-prosjektene (for eksempel build-Win32-Lib-CB.cmd) og kjøre en fullstendig lenking av C++-demoene, både prøveversjon og fullversjon. Først når C++-lenkeren lykkes, kan du være sikker på at alle Delphi-units er riktig registrert og eksponerer symbolene sine for C++-kjøretiden
Merk: Kompilatorkompatibilitet på tvers av plattformer opprettholdes strengt i både Delphi- og C++Builder-utgavene av HotXLS Delphi VCL Component