Artikel Teknis

Kompilasi Silang Komponen Delphi untuk C++Builder: Menghindari Eksternal Tak Terpecahkan

Saat memelihara komponen VCL yang ditulis dalam bahasa Delphi tetapi digunakan oleh pengguna Delphi dan C++Builder, Anda akan segera menyadari bahwa kedua sistem build memperlakukan dependensi secara sangat berbeda. Sebuah unit yang berhasil dikompilasi dengan sempurna di Delphi dapat menyebabkan kesalahan penaut di C++Builder. Perbedaan ini sering menjadi jebakan bagi pembuat komponen, khususnya saat menambahkan unit internal baru ke pustaka yang sudah ada

Berikut adalah rincian mendetail mengapa hal ini terjadi, menggunakan contoh dunia nyata dari komponen HotXLS, dan bagaimana membentengi proses kompilasi silang Anda menggunakan inklusi eksplisit, {$HPPEMIT}, dan penautan pragma

Perbedaan ini muncul karena Delphi dapat menemukan unit melalui search path, sedangkan C++Builder perlu melihat unit dan objek yang benar-benar masuk ke proyek atau paketnya. Panduan ini menggunakan contoh komponen HotXLS untuk menjelaskan inklusi eksplisit, {$HPPEMIT}, dan pragma linking tanpa mengubah API Delphi

Jebakan: Kompilasi Implisit vs. Inklusi Eksplisit

Misalkan Anda membuat unit Delphi baru, lxXlsSummary.pas, untuk menangani metadata dokumen, dan Anda meng-uses unit tersebut di unit pem-parsing-an utama Anda, lxRead.pas. Anda menekan kompilasi di IDE Delphi, build menjadi hijau, dan Anda mengirimkan pembaruan

Keesokan harinya, pengguna C++Builder Anda melaporkan kesalahan selama fase penautan: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj

Cara Delphi (dcc32)

Saat kompiler Delphi memproses sebuah paket (.dpk), ia melihat unit-unit yang secara eksplisit terdaftar di klausa contains. Jika salah satu dari unit tersebut meng-uses unit eksternal (seperti lxXlsSummary.pas) yang tidak ada dalam daftar contains, kompiler Delphi melakukan penautan statis implisit. Kompiler tersebut secara sederhana menemukan file .pas di jalur pencarian, mengompilasinya menjadi .dcu, dan memasukkannya ke dalam .bpl yang dihasilkan. Build berhasil, sepenuhnya menutupi kelalaian tersebut

Cara C++Builder (MSBuild / .cbproj)

Sistem build C++Builder jauh lebih ketat. Sistem ini hanya menghasilkan file objek C++ (.obj) dan header (.hpp) untuk unit Delphi yang secara eksplisit terdaftar dalam grup item <DelphiCompile> di file .cbproj. Karena lxXlsSummary.pas tidak pernah didaftarkan secara eksplisit dalam file proyek, tidak ada lxXlsSummary.obj yang dibuat. Saat penaut mencoba menyelesaikan panggilan yang dibuat oleh lxRead.obj, simbol-simbol tersebut hilang, sehingga menyebabkan kesalahan eksternal yang tak terpecahkan

Memecahkan Eksternal dengan Pragma Link dan HPPEMIT

Jika Anda ingin memastikan bahwa sebuah unit ditautkan dengan benar di C++ tanpa memaksa pengguna untuk menambahkan file .obj ke proyek mereka secara manual, Anda dapat menggunakan arahan {$HPPEMIT} Delphi. Ini memberi tahu kompiler Delphi untuk menyuntikkan arahan #pragma link C++ tertentu ke dalam file .hpp yang dihasilkan

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.

Saat C++Builder menyertakan lxXlsSummary.hpp, kompiler tersebut menemui #pragma link dan secara otomatis memberi tahu penaut (ILINK32/ILINK64) untuk menyelesaikan simbol dari lxXlsSummary.obj

Aturan Emas untuk Pemeliharaan Komponen

Untuk menghindari kerusakan build C++Builder sepenuhnya, Anda harus menerapkan kebijakan pendaftaran yang ketat. Kapan pun unit Pascal baru ditambahkan ke pustaka Anda, unit tersebut harus didaftarkan secara eksplisit di ketiga jenis file proyek secara bersamaan

1. Perbarui Proyek C++Builder (.cbproj / .bpk)

Buka file .cbproj di editor teks dan tambahkan unit baru ke daftar kompilasi, pastikan Anda memberikan urutan build yang unik. Jika menggunakan versi C++Builder lama dengan file .bpk, pastikan tag <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> ditambahkan

<DelphiCompile Include="lxXlsSummary.pas">
  <BuildOrder>101</BuildOrder>
</DelphiCompile>

2. Perbarui Paket Delphi (.dpk)

Tambahkan unit ke klausa eksplisit contains. Ini memastikan kompiler Delphi tidak perlu bergantung pada penautan implisit, yang umumnya dianggap sebagai praktik yang buruk

package HotXLS;

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}

requires
  rtl,
  vcl;

contains
  lxRead in 'lxRead.pas',
  lxXlsSummary in 'lxXlsSummary.pas';

end.

Validasi Integrasi Berkelanjutan

Pertahanan utama terhadap jebakan ini adalah validasi CI/CD. Jangan pernah mengandalkan build Delphi yang berhasil semata sebelum mengirimkan komponen bahasa ganda. Skrip build Anda harus memanggil MSBuild atau alat bantu baris perintah bcc32c pada proyek C++Builder (misalnya, build-Win32-Lib-CB.cmd) dan menjalankan penautan lengkap dari demo C++ uji coba dan penuh. Hanya ketika penaut C++ berhasil Anda dapat memastikan bahwa semua unit Delphi terdaftar dengan benar dan memaparkan simbolnya ke runtime C++

Catatan: Kompatibilitas kompiler lintas platform dipertahankan secara ketat di semua edisi Delphi dan C++Builder dari Komponen VCL HotXLS