Fenner
Compilador

Packages na prática

Como se gera um package (.dll + .fxd), como um cliente o usa, as versões do formato e os erros típicos.

O que é e por que existe#

A página Packages e o .fxd explica a ideia (o equivalente do .bpl + .dcp do Delphi). Esta aqui é o lado prático: o que escrever, o que sai, como o cliente liga e o que dá errado.

Resumo da ideia: uma library exporta só o que está no exports e o cliente precisa escrever external … name … à mão. Um package exporta tudo o que é public e deixa um arquivo .fxd do lado, com tudo o que o compilador precisa para compilar um cliente: layout dos tipos, slots da VMT, assinaturas, constantes, o texto dos genéricos e os corpos inline.

Gerando um package#

O arquivo do package só lista os módulos que ele reúne; o corpo é vazio:

Fenner
package GreeterPkg;

uses Greeter;          // Greeter.fxm: um módulo com declarações public

begin
end.
terminal
fc GreeterPkg.fxp

Esse é o formato dos packages de teste do próprio compilador (package libm1322pk; uses …; begin end.). Saem: a DLL com o código, o .fxd (formato binário, serialização da árvore sintática), o .fxnames (o mapa de nomes dos símbolos) e a .lib de importação. Se o package precisa que o compilador exporte tudo (como a RTL faz), o fonte começa com {$config exportall on}.

Usando em um cliente#

No .config do cliente, LinkPackage liga um .fxd (nome-base, sem extensão); UnlinkPackage cancela um que veio de uma configuração acima; --NoPackages ignora todos numa compilação.

config
Config  All  LinkPackage  ($ProjectPath)\..\GreeterPkg

O cliente escreve uses Greeter; normalmente: o compilador resolve os tipos a partir do .fxd, e o programa em execução precisa da DLL ao lado. A RTL BedRock é o primeiro cliente do mecanismo (RtlPackage), com o .fxd dela.

Versões e erros#

O .fxd leva o número de versão do formato e a impressão digital do compilador que o gerou. O compilador atual escreve a v20 e lê da v17 à v20. A ordem das recusas ao abrir:

CódigoSituação
FE1922versão do .fxd mais nova que a deste compilador: use um compilador mais novo, ou recompile o package
FE1923versão anterior à v17: recompile o package com este compilador
FE1489mesma versão de formato, mas escrito por outro build do compilador
FE1490arquivo corrompido (as três acima não são isso)

Na prática, um package feito por outra versão do fc aparece como erro de .fxd ou como símbolo não resolvido na ligação. Recompile o package com o fc que compila o cliente.

Classes, genéricos e tuplas atravessando#

  • Herdar de classe do package funciona, inclusive de classe que implementa interface: o cliente que deriva (TCli = class(TPkImpl)) ganha Supports, as e a atribuição à interface (a partir do formato v20).
  • Create de classe de package no cliente é recusado por padrão (FE0971): a instância seria alocada no gerenciador de memória do cliente e devolvida, na destruição, ao da DLL. O idioma é uma fábrica do package (MakeGreeter), com o Free no cliente rodando o destrutor virtual na DLL. O package pode optar por {$config pkgcreate on}, que exporta o alocador e deixa o cliente dar Create (exige o .fxnames ao lado).
  • Genéricos viajam como texto no .fxd (a monomorfização reparseia texto). O cliente adota a instância que o package já fez, se o texto for o mesmo, ou instancia sozinho.
  • Tuplas do package e do cliente passam a ser o mesmo tipo.
  • protected é a visibilidade que existe para isso: visível entre os módulos do projeto, não exportada (Módulos).
  • Posições: o .fxd pode levar uma seção opcional FXPS (arquivo e linha de cada declaração) para o "ir para a definição" do editor chegar ao fonte do package. Sem o fonte, o editor mostra arquivo:linha mas não abre nada. Renomear um símbolo de package é sempre recusado pelo servidor de linguagem.

O que ainda não está decidido#

Segundo o desenho do compilador: a ordem de initialization entre vários packages (rodam no DllMain de cada um); a impressão digital conferida na carga da DLL (trocar a .dll por outra compilada de fonte diferente muda offsets e slots da VMT em silêncio); package que usa package (o requires do Delphi); e a poda (o que é público vira raiz, então escrever algo public num package tem custo).

Veja também#