Prižiūrint VCL komponentą, parašytą naudojant Delphi, bet vartojamą tiek Delphi, tiek C++Builder naudotojų, greitai suprantate, kad šios dvi konstravimo sistemos priklausomybes vertina labai skirtingai. Modulis (angl. unit), kuris puikiai kompiliuojamas Delphi aplinkoje, gali sukelti katastrofiškas saistytuvo (angl. linker) klaidas C++Builder programoje. Šis neatitikimas yra dažni spąstai komponentų autoriams, ypač kai į esamą biblioteką pridedami nauji vidiniai moduliai
Štai išsami analizė, kodėl taip nutinka, naudojant realaus pasaulio pavyzdžius iš „HotXLS“ komponento, ir kaip padaryti jūsų kryžminio kompiliavimo procesą nepramušamu (angl. bulletproof) naudojant aiškius įtraukimus (angl. explicit includes), {$HPPEMIT} ir pragma saistymą (angl. linking)
Spąstai: numanomas kompiliavimas prieš aiškų įtraukimą
Tarkime, sukuriate naują Delphi modulį lxXlsSummary.pas, skirtą dokumento metaduomenims apdoroti, ir naudojate (angl. uses) jį savo pagrindiniame analizės modulyje lxRead.pas. Paspaudžiate „compile“ Delphi IDE aplinkoje, konstravimas pažaliuoja ir jūs išsiunčiate naujinimą (angl. update)
Kitą dieną jūsų C++Builder naudotojai praneša apie klaidą saistymo etape: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
Delphi būdas (dcc32)
Kai Delphi kompiliatorius apdoroja paketą (.dpk), jis peržiūri modulius, aiškiai išvardytus contains sąlygoje. Jei vienas iš tų modulių naudoja (angl. uses) išorinį modulį (pvz., lxXlsSummary.pas), kurio nėra contains sąraše, Delphi kompiliatorius atlieka numanomą statinį saistymą (angl. implicit static linking). Jis tiesiog randa .pas failą paieškos kelyje, sukompiliuoja jį į .dcu ir „įkepa“ į galutinį .bpl. Konstravimas pavyksta, visiškai užmaskuodamas praleidimą
C++Builder būdas (MSBuild / .cbproj)
C++Builder konstravimo sistema yra daug griežtesnė. Ji generuoja C++ objekto failus (.obj) ir antraštes (.hpp) tik tiems Delphi moduliams, kurie aiškiai išvardyti .cbproj failo <DelphiCompile> elementų grupėje. Kadangi lxXlsSummary.pas niekada nebuvo aiškiai užregistruotas projekto faile, lxXlsSummary.obj nesukuriamas. Kai saistytojas bando išspręsti iškvietimus (angl. calls), kuriuos padarė lxRead.obj, trūksta simbolių, o tai sukelia neišspręsto išorinio simbolio klaidą
Išorinių simbolių išsprendimas su Pragma Link ir HPPEMIT
Jei norite užtikrinti, kad modulis būtų tinkamai susietas C++ aplinkoje, neverčiant naudotojo rankiniu būdu pridėti .obj failo į savo projektą, galite naudoti Delphi {$HPPEMIT} direktyvą. Tai nurodo Delphi kompiliatoriui įterpti konkrečią C++ #pragma link direktyvą į sugeneruotą .hpp failą
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.
Kai C++Builder įtraukia lxXlsSummary.hpp, kompiliatorius susiduria su #pragma link ir automatiškai nurodo saistytojui (ILINK32/ILINK64) išspręsti simbolius iš lxXlsSummary.obj
Auksinė komponentų priežiūros taisyklė
Norėdami išvengti visiško C++Builder konstravimų sugadinimo, turite priimti griežtą registracijos politiką. Kaskart, kai į jūsų biblioteką pridedamas naujas Pascal modulis, jis turi būti aiškiai užregistruotas visų trijų tipų projektų failuose vienu metu
1. Atnaujinkite C++Builder projektą (.cbproj / .bpk)
Atidarykite .cbproj failą teksto rengyklėje ir pridėkite naują modulį į kompiliavimo sąrašą, užtikrindami, kad pateiktumėte unikalią konstravimo tvarką (angl. build order). Jei naudojate senesnes C++Builder versijas su .bpk failais, įsitikinkite, kad pridėta žyma <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file>
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Atnaujinkite Delphi paketą (.dpk)
Pridėkite modulį prie aiškios contains sąlygos. Tai užtikrina, kad Delphi kompiliatoriui nereikės kliautis numanomu saistymu, kas bet kuriuo atveju paprastai laikoma bloga praktika
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Nuolatinės integracijos (angl. Continuous Integration) patvirtinimas
Geriausia gynyba nuo šių spąstų yra CI/CD patvirtinimas. Niekada nepasikliaukite vien tik sėkmingu Delphi konstravimu prieš išsiųsdami dviejų kalbų (angl. dual-language) komponentą. Jūsų konstravimo skriptai turi iškviesti MSBuild arba bcc32c komandų eilutės įrankius C++Builder projektuose (pvz., build-Win32-Lib-CB.cmd) ir atlikti pilną C++ bandomosios (angl. trial) ir pilnos versijų demonstracinių programų saistymą. Tik tada, kai C++ saistytojas sėkmingai baigia darbą, galite būti tikri, kad visi Delphi moduliai yra teisingai užregistruoti ir atskleidžia savo simbolius C++ vykdymo aplinkai (angl. runtime)
Pastaba: Kelių platformų (angl. cross-platform) kompiliatorių suderinamumas yra griežtai palaikomas HotXLS VCL Component Delphi ir C++Builder leidimuose