Ao manter um componente VCL escrito em 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 minucioso 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ê a usa (uses) na sua unidade de análise principal, lxRead.pas. Você clica em compilar no IDE do Delphi, 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 arquivos de objeto C++ (.obj) e cabeçalhos (.hpp) apenas para as unidades Delphi listadas explicitamente no grupo de itens <DelphiCompile> do arquivo .cbproj. Como lxXlsSummary.pas nunca foi registrado explicitamente 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 external 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. Ela instrui o compilador Delphi a injetar uma diretiva C++ #pragma link específica no arquivo .hpp gerado
unit lxXlsSummary;
interface
{$IFDEF WINDOWS}
// Injeta um pragma link no arquivo de cabeçalho C++ gerado
// Isso força o vinculador C++ a incluir o arquivo .obj correspondente
{$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;
// Lógica de extração de metadados aqui
end;
end.
Quando o C++Builder inclui lxXlsSummary.hpp, o compilador encontra o #pragma link e automaticamente diz ao vinculador (ILINK32/ILINK64) para resolver os símbolos a partir de lxXlsSummary.obj
A Regra de Ouro para a Manutenção de Componentes
Para evitar quebrar completamente os 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 estiver usando 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 da vinculação implícita, que de qualquer forma é geralmente considerada uma prática ruim
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 por Integração Contínua
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 o MSBuild ou as ferramentas de linha de comando bcc32c nos projetos C++Builder (por exemplo, build-Win32-Lib-CB.cmd) e executar um link completo das demonstrações de avaliação e completas em C++. 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