Lors de la maintenance d'un composant VCL écrit en Delphi mais consommé à la fois par les utilisateurs de Delphi et de C++Builder, vous réalisez rapidement que les deux systèmes de compilation traitent les dépendances très différemment. Une unité qui se compile parfaitement sous Delphi peut provoquer des erreurs d'édition de liens (linker) catastrophiques sous C++Builder. Cet écart est un piège fréquent pour les auteurs de composants, en particulier lors de l'ajout de nouvelles unités internes à une bibliothèque existante
Voici une description détaillée de la raison pour laquelle cela se produit, à l'aide d'exemples concrets du composant HotXLS, et de la manière de blinder votre processus de compilation croisée en utilisant des inclusions explicites, {$HPPEMIT} et l'édition de liens pragma
Le piège : Compilation implicite vs Inclusion explicite
Supposons que vous créiez une nouvelle unité Delphi, lxXlsSummary.pas, pour gérer les métadonnées de document, et que vous fassiez un uses de celle-ci dans votre unité d'analyse principale, lxRead.pas. Vous lancez la compilation dans l'EDI Delphi, la compilation réussit (devient verte) et vous publiez la mise à jour
Le lendemain, vos utilisateurs C++Builder signalent une erreur lors de la phase d'édition de liens : Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
La méthode Delphi (dcc32)
Lorsque le compilateur Delphi traite un paquet (.dpk), il examine les unités explicitement listées dans la clause contains. Si l'une de ces unités uses (utilise) une unité externe (comme lxXlsSummary.pas) qui ne figure pas dans la liste contains, le compilateur Delphi effectue une édition de liens statique implicite. Il trouve simplement le fichier .pas dans le chemin de recherche, le compile en un .dcu et l'intègre dans le .bpl résultant. La compilation réussit, masquant complètement l'omission
La méthode C++Builder (MSBuild / .cbproj)
Le système de compilation de C++Builder est beaucoup plus strict. Il ne génère des fichiers objets C++ (.obj) et des en-têtes (.hpp) que pour les unités Delphi explicitement répertoriées dans le groupe d'éléments <DelphiCompile> du fichier .cbproj. Étant donné que lxXlsSummary.pas n'a jamais été explicitement enregistré dans le fichier de projet, aucun lxXlsSummary.obj n'est créé. Lorsque l'éditeur de liens tente de résoudre les appels effectués par lxRead.obj, les symboles sont manquants, ce qui entraîne une erreur d'externe non résolu (unresolved external)
Résolution des externes avec Pragma Link et HPPEMIT
Si vous voulez vous assurer qu'une unité est correctement liée en C++ sans forcer l'utilisateur à ajouter manuellement le fichier .obj à son projet, vous pouvez utiliser la directive {$HPPEMIT} de Delphi. Cela indique au compilateur Delphi d'injecter une directive C++ spécifique #pragma link dans le fichier .hpp généré
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.
Lorsque C++Builder inclut lxXlsSummary.hpp, le compilateur rencontre le #pragma link et indique automatiquement à l'éditeur de liens (ILINK32/ILINK64) de résoudre les symboles à partir de lxXlsSummary.obj
La règle d'or pour la maintenance des composants
Pour éviter de casser complètement les compilations C++Builder, vous devez adopter une politique d'enregistrement stricte. Chaque fois qu'une nouvelle unité Pascal est ajoutée à votre bibliothèque, elle doit être explicitement enregistrée simultanément dans les trois types de fichiers de projet
1. Mettre à jour le projet C++Builder (.cbproj / .bpk)
Ouvrez le fichier .cbproj dans un éditeur de texte et ajoutez la nouvelle unité à la liste de compilation, en vous assurant de fournir un ordre de compilation unique (BuildOrder). Si vous utilisez d'anciennes versions de C++Builder avec des fichiers .bpk, assurez-vous que la balise <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> est ajoutée
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Mettre à jour le paquet Delphi (.dpk)
Ajoutez l'unité à la clause explicite contains. Cela garantit que le compilateur Delphi n'a pas à s'appuyer sur l'édition de liens implicite, ce qui est généralement considéré comme une mauvaise pratique de toute façon
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validation de l'intégration continue
L'ultime défense contre ce piège est la validation CI/CD. Ne vous fiez jamais uniquement à une compilation Delphi réussie avant de publier un composant bilingue. Vos scripts de build doivent invoquer MSBuild ou les outils en ligne de commande bcc32c sur les projets C++Builder (par exemple, build-Win32-Lib-CB.cmd) et exécuter une édition de liens complète des versions d'essai et démos complètes C++. Ce n'est que lorsque l'éditeur de liens C++ réussit que vous pouvez être certain que toutes les unités Delphi sont correctement enregistrées et exposent leurs symboles au runtime C++
Remarque : La compatibilité multiplateforme du compilateur est strictement maintenue entre les éditions Delphi et C++Builder du composant VCL HotXLS