Ao manter um componente VCL escrito no Delphi mas consumido tanto por usuários de Delphi quanto de C++Builder, você rapidamente percebe que os dois sistemas de build tratam as dependências de forma muito diferente. Uma unidade que compila perfeitamente no Delphi pode causar erros catastróficos de vinculador (linker) no C++Builder. Essa discrepância é uma armadilha frequente para autores de componentes, especialmente ao adicionar novas unidades internas a uma biblioteca existente.
Aqui está um detalhamento de por que isso acontece — usando exemplos do mundo real do componente HotXLS — e como blindar seu processo de compilação cruzada usando inclusões explícitas, {$HPPEMIT} e vinculação de pragma.
A Armadilha: Compilação Implícita vs. Inclusão Explícita
Suponha que você crie uma nova unidade Delphi, lxXlsSummary.pas, para lidar com metadados de documentos, e você usa uses nela em sua unidade de análise principal, lxRead.pas. Você clica em compilar no Delphi IDE, o build fica verde e você envia a atualização.
No dia seguinte, seus usuários do C++Builder relatam um erro durante a fase de vinculação: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj.
O Jeito Delphi (dcc32)
Quando o compilador Delphi processa um pacote (.dpk), ele analisa as unidades listadas explicitamente na cláusula contains. Se uma dessas unidades usa (uses) uma unidade externa (como lxXlsSummary.pas) que não está na lista contains, o compilador Delphi executa a vinculação estática implícita. Ele simplesmente encontra o arquivo .pas no caminho de pesquisa, compila-o em um .dcu e o incorpora no .bpl resultante. O build é bem-sucedido, mascarando completamente a omissão.
O Jeito C++Builder (MSBuild / .cbproj)
O sistema de build do C++Builder é muito mais rigoroso. Ele gera apenas arquivos de objeto C++ (.obj) e cabeçalhos (.hpp) para as unidades Delphi listadas explicitamente no grupo de itens <DelphiCompile> do arquivo .cbproj. Como lxXlsSummary.pas nunca foi explicitamente registrado no arquivo do projeto, nenhum lxXlsSummary.obj é criado. Quando o vinculador tenta resolver as chamadas feitas por lxRead.obj, os símbolos estão ausentes, levando a um erro de "unresolved external" (externo não resolvido).
Resolvendo Externals com Pragma Link e HPPEMIT
Se você deseja garantir que uma unidade seja vinculada corretamente em C++ sem forçar o usuário a adicionar manualmente o arquivo .obj ao seu projeto, você pode usar a diretiva {$HPPEMIT} do Delphi. Isso instrui o compilador Delphi a injetar uma diretiva C++ #pragma link específica no arquivo .hpp gerado.
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.
Quando o C++Builder inclui lxXlsSummary.hpp, o compilador encontra o #pragma link e diz automaticamente ao vinculador (ILINK32/ILINK64) para resolver símbolos de lxXlsSummary.obj.
A Regra de Ouro para a Manutenção de Componentes
Para evitar a quebra total de builds do C++Builder, você deve adotar uma política de registro estrita. Sempre que uma nova unidade Pascal for adicionada à sua biblioteca, ela deverá ser registrada explicitamente em todos os três tipos de arquivo de projeto simultaneamente.
1. Atualize o Projeto C++Builder (.cbproj / .bpk)
Abra o arquivo .cbproj em um editor de texto e adicione a nova unidade à lista de compilação, garantindo que você forneça uma ordem de build exclusiva. Se usar versões mais antigas do C++Builder com arquivos `.bpk`, certifique-se de que a tag `<file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file>` seja adicionada.
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Atualize o Pacote Delphi (.dpk)
Adicione a unidade à cláusula contains explícita. Isso garante que o compilador Delphi não tenha que depender de vinculação implícita, que geralmente é considerada uma prática ruim de qualquer maneira.
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validação de Integração Contínua (CI)
A defesa definitiva contra essa armadilha é a validação de CI/CD. Nunca confie apenas em um build Delphi bem-sucedido antes de enviar um componente de linguagem dupla. Seus scripts de build devem invocar ferramentas de linha de comando do MSBuild ou bcc32c nos projetos C++Builder (por exemplo, build-Win32-Lib-CB.cmd) e executar um link completo da versão de avaliação C++ e das demonstrações completas. Somente quando o vinculador C++ é bem-sucedido você pode ter certeza de que todas as unidades Delphi estão registradas corretamente e expondo seus símbolos ao runtime do C++.
Nota: A compatibilidade do compilador multiplataforma é estritamente mantida nas edições Delphi e C++Builder do HotXLS VCL Component.