Amikor egy Delphiben írt, de mind a Delphi, mind a C++Builder felhasználók által használt VCL komponenst tart fenn, hamar rájön, hogy a két fordítási (build) rendszer nagyon eltérően kezeli a függőségeket. Egy Delphiben tökéletesen leforduló egység (unit) katasztrofális linker hibákat okozhat a C++Builderben. Ez az eltérés gyakori csapda a komponensszerzők számára, különösen akkor, ha új belső egységeket adnak hozzá egy meglévő könyvtárhoz.
Íme egy részletes lebontás arról, hogy miért történik ez—a HotXLS komponens valós példáját felhasználva—, és hogyan teheti golyóállóvá a keresztfordítási folyamatot explicit beillesztések (includes), a {$HPPEMIT} és a pragma linkelés használatával.
A csapda: Implicit fordítás kontra explicit felvétel
Tegyük fel, hogy létrehoz egy új Delphi egységet, lxXlsSummary.pas néven, a dokumentum metaadatainak kezelésére, és uses paranccsal behívja a fő elemző egységbe, az lxRead.pas fájlba. Megnyomja a fordítás gombot a Delphi IDE-ben, a build zöld lesz, és Ön kiadja a frissítést.
Másnap a C++Builder felhasználói hibát jeleznek a linkelési szakaszban: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj.
A Delphi módszer (dcc32)
Amikor a Delphi fordító feldolgoz egy csomagot (.dpk), megnézi a contains záradékban kifejezetten felsorolt egységeket. Ha az egyik ilyen egység uses-szal egy olyan külső egységet (például lxXlsSummary.pas) használ, amely nem szerepel a contains listán, a Delphi fordító implicit statikus linkelést hajt végre. Egyszerűen megtalálja a .pas fájlt a keresési útvonalon, lefordítja .dcu fájllá, és beégeti a kapott .bpl-be. A fordítás sikeres lesz, teljesen elfedve a kihagyást.
A C++Builder módszer (MSBuild / .cbproj)
A C++Builder build rendszere sokkal szigorúbb. Csak azokhoz a Delphi egységekhez generál C++ objektumfájlokat (.obj) és fejléceket (.hpp), amelyek kifejezetten fel vannak sorolva a .cbproj fájl <DelphiCompile> elemcsoportjában. Mivel az lxXlsSummary.pas soha nem lett kifejezetten regisztrálva a projektfájlban, nem jön létre lxXlsSummary.obj. Amikor a linker megpróbálja feloldani az lxRead.obj által végzett hívásokat, a szimbólumok hiányoznak, ami feloldatlan külső hivatkozás hibához vezet.
Külső hivatkozások feloldása Pragma Link és HPPEMIT segítségével
Ha biztosítani szeretné, hogy egy egység megfelelően legyen linkelve C++-ban anélkül, hogy a felhasználót arra kényszerítené, hogy manuálisan hozzáadja az .obj fájlt a projektjéhez, használhatja a Delphi {$HPPEMIT} direktíváját. Ez megmondja a Delphi fordítónak, hogy fecskendezzen be egy specifikus C++ #pragma link direktívát a generált .hpp fájlba.
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.
Amikor a C++Builder beilleszti az lxXlsSummary.hpp fájlt, a fordító találkozik a #pragma link direktívával, és automatikusan utasítja a linkert (ILINK32/ILINK64), hogy oldja fel a szimbólumokat az lxXlsSummary.obj fájlból.
Az aranyszabály a komponensek karbantartásához
A C++Builder buildek tönkretételének elkerülése érdekében szigorú regisztrációs házirendet kell elfogadnia. Amikor egy új Pascal egységet ad hozzá a könyvtárához, azt kifejezetten regisztrálni kell mind a három projektfájltípusban egyidejűleg.
1. Frissítse a C++Builder projektet (.cbproj / .bpk)
Nyissa meg a .cbproj fájlt egy szövegszerkesztőben, és adja hozzá az új egységet a fordítási listához, biztosítva egyedi fordítási sorrend megadását. Ha régebbi C++Builder verziókat használ .bpk fájlokkal, győződjön meg arról, hogy a <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> címke hozzá van adva.
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Frissítse a Delphi csomagot (.dpk)
Adja hozzá az egységet az explicit contains záradékhoz. Ez biztosítja, hogy a Delphi fordítónak ne kelljen az implicit linkelésre támaszkodnia, ami általában amúgy is rossz gyakorlatnak számít.
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Folyamatos integráció általi érvényesítés
A végső védekezés ezen csapda ellen a CI/CD érvényesítés. Soha ne hagyatkozzon kizárólag a sikeres Delphi fordításra, mielőtt egy kétnyelvű komponenst kiadna. A fordítási szkriptjeinek meg kell hívniuk az MSBuild-et vagy a bcc32c parancssori eszközöket a C++Builder projekteken (pl. build-Win32-Lib-CB.cmd), és le kell futtatniuk a C++ próba (trial) és teljes demók komplett linkelését. Csak akkor lehet biztos abban, hogy minden Delphi egység megfelelően regisztrálva van, és felfedi szimbólumait a C++ futtatókörnyezet számára, ha a C++ linker sikeresen lefut.
Megjegyzés: A platformokon átívelő fordító-kompatibilitást szigorúan fenntartjuk a HotXLS VCL komponens Delphi és C++Builder kiadásai között.