Cuando mantiene un componente VCL escrito en Delphi y lo consumen tanto proyectos de Delphi como de C++Builder, enseguida ve que ambos sistemas de compilación tratan las dependencias de forma distinta. Una unidad que compila sin problemas en Delphi puede acabar provocando errores de enlace graves en C++Builder. Esa diferencia es una trampa frecuente para los autores de componentes, sobre todo cuando añaden nuevas unidades internas a una biblioteca ya existente
A continuación se presenta un desglose detallado de por qué sucede esto, usando ejemplos reales del componente HotXLS, y de cómo blindar el proceso de compilación cruzada con inclusiones explícitas, {$HPPEMIT} y #pragma link
La trampa: compilación implícita vs. inclusión explícita
Suponga que crea una nueva unidad Delphi, lxXlsSummary.pas, para manejar metadatos de documentos, y la usa (uses) en su unidad de análisis principal, lxRead.pas. Hace clic en compilar en el IDE de Delphi, la compilación se realiza correctamente y lanza la actualización
Al día siguiente, sus usuarios de C++Builder informan un error durante la fase de enlace: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
A la manera de Delphi (dcc32)
Cuando el compilador de Delphi procesa un paquete (.dpk), observa las unidades enumeradas explícitamente en la cláusula contains. Si una de esas unidades usa una unidad externa (como lxXlsSummary.pas) que no está en la lista contains, el compilador de Delphi realiza una vinculación estática implícita. Simplemente encuentra el archivo .pas en la ruta de búsqueda, lo compila en un .dcu y lo integra en el .bpl resultante. La compilación tiene éxito, enmascarando completamente la omisión
A la manera de C++Builder (MSBuild / .cbproj)
El sistema de compilación de C++Builder es mucho más estricto. Solo genera archivos objeto de C++ (.obj) y encabezados (.hpp) para las unidades Delphi enumeradas explícitamente en el grupo de elementos <DelphiCompile> del archivo .cbproj. Como lxXlsSummary.pas nunca se registró de forma explícita en el proyecto, no se crea ningún lxXlsSummary.obj. Cuando el enlazador intenta resolver las llamadas que hace lxRead.obj, faltan los símbolos y aparece el error de referencia externa no resuelta
Resolviendo referencias externas con Pragma Link y HPPEMIT
Si desea asegurarse de que una unidad esté correctamente vinculada en C++ sin obligar al usuario a agregar manualmente el archivo .obj a su proyecto, puede usar la directiva {$HPPEMIT} de Delphi. Esto le dice al compilador de Delphi que inyecte una directiva #pragma link de C++ específica en el archivo .hpp generado
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.
Cuando C++Builder incluye lxXlsSummary.hpp, el compilador encuentra el #pragma link y le dice automáticamente al enlazador (ILINK32/ILINK64) que resuelva los símbolos de lxXlsSummary.obj
La regla de oro para el mantenimiento de componentes
Para evitar romper las compilaciones de C++Builder por completo, debe adoptar una política de registro estricta. Cada vez que se agrega una nueva unidad Pascal a su biblioteca, se debe registrar explícitamente en los tres tipos de archivos de proyecto simultáneamente
1. Actualice el proyecto C++Builder (.cbproj / .bpk)
Abra el archivo .cbproj en un editor de texto y agregue la nueva unidad a la lista de compilación, asegurándose de proporcionar un orden de compilación único. Si usa versiones anteriores de C++Builder con archivos `.bpk`, asegúrese de agregar la etiqueta `<file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file>`
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Actualice el paquete Delphi (.dpk)
Agregue la unidad a la cláusula contains explícita. Esto asegura que el compilador de Delphi no tenga que depender de la vinculación implícita, lo que generalmente se considera una mala práctica de todos modos
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validación de integración continua
La defensa definitiva contra esta trampa es la validación CI/CD. Nunca confíe únicamente en una compilación exitosa de Delphi antes de enviar un componente de lenguaje dual. Sus scripts de compilación deben invocar MSBuild o las herramientas de línea de comandos bcc32c en los proyectos de C++Builder (por ejemplo, build-Win32-Lib-CB.cmd) y ejecutar un enlace completo de la versión de prueba y las demostraciones completas de C++. Solo cuando el enlazador de C++ tiene éxito, puede estar seguro de que todas las unidades de Delphi están correctamente registradas y exponen sus símbolos al tiempo de ejecución de C++
Nota: La compatibilidad de compilación multiplataforma se mantiene estrictamente en las ediciones Delphi y C++Builder del HotXLS VCL Component