Modularização de Aplicativos – Single Package
Quem desenvolve aplicativos modularizados, pode já ter se deparado com uma desvantagem comum quando se trata de distribuir pacotes de runtime: como controlar quais packages distribuir junto com o aplicativo/módulos e suas versões e facilitar suas atualizações?
Normalmente quando compilamos um aplicativo com a opção “Build with runtime packages” marcada, no mínimo teremos que redistribuir os pacotes rtlxx.bpl e vclxx.bpl (onde o “xx” equivale à versão do Delphi, pelo menos até o Delphi 7), mais os pacotes referenciados pelas units da vcl e/ou de terceiros.
Agora imagine um projeto grande, onde temos vários componentes de terceiros empregados. Quantos arquivos extras se tornariam dependências para o aplicativo?
Procurando uma maneira simples para minimizar o número de arquivos a serem distribuídos em conjunto com um aplicativo que use runtime packages, me deparo com o excelente tutorial de Keith Johnson, ao qual não posso acrescentar muita coisa, por já ser muito completo e didático. Contento-me a simplificá-lo e torná-lo útil para a realidade de muitos aqui.
Direto ao ponto
Figura 1 – Aplicativo com pacotes dinâmicos de forma convencional.
Figura 2 – Aplicativo com pacotes dinâmicos carregando de um único pacote.
Abra o Delphi, crie um novo aplicativo e salve o projeto. Adicione os componentes necessários de forma natural, como se fosse um projeto comum e salve.
Agora você pode fechar seu projeto e criar um novo package, ou criá-lo no mesmo grupo de projetos. Para simplificar, vou usar a segunda opção. Crie uma Unit vazia dentro deste novo package.
Você deverá percorrer TODOS os forms do seu projeto, vasculhando as cláusulas Uses para que todas as units referenciadas sejam adicionadas àquela unit do nosso pacote. Nosso objetivo será fazer com que o pacote importe todas essas units implicitamente para dentro dele, para que o executável tenha apenas ele como dependência. Para isso vá até Project -> Options e escolha a opção “Directories/Conditionals”, altere o “Output directory” e o “DCP directory” para uma pasta onde estará seu executável (o “DCP directory” é necessário na hora da compilação do executável, pois ele vai procurar pelo arquivo .dcp do pacote, mas em tempo de execução, ele só precisará do arquivo .bpl), ou onde ele tenha acesso pelo path do Windows.
É preferível deixar junto ao executável como numa pasta “bin” do seu projeto para facilitar a localização e distribuição, evitando um “bpl hell”. Clique em Ok e vá até aquela unit que você criou dentro do pacote. Adicione uma cláusula Uses a ela e coloque todas as units referenciadas pelo seu aplicativo.
No diretório de saída que você acabou de configurar, coloque uma cópia da SysInit.dcu, que se encontra em $(BDS)\lib (ex: C:\Arquivos de programas\CodeGear\RAD Studio\5.0\lib). Esta unit faz-se necessária, pois se você tentar compilar seu projeto sem ela, vai disparar uma exceção, informando que não foi possível encontrar SysInit.dcu (lembre-se: o search path que você configurou sobrepõe o default do Delphi, daí ele não achar). Mesmo assim, esta unit não deve ser adicionada na sessão uses da unit do pacote, deverá apenas permanecer lá na pasta de destino. Vá até a sessão “requires” do seu pacote lá no Project manager e elimine TODAS as referências a outros packages, deixando-a vazia.
Agora compile seu pacote. Irá aparecer uma mensagem, dizendo que você deve incluir os packages da lista para manter compatibilidade com outros pacotes já instalados.
É agora que vem o segredo: Escolha “Cancel” para que as units referenciadas sejam importadas para dentro de nosso pacote, e não as referências aos seus respectivos packages. Outro diálogo aparecerá com a mensagem: “If these changes are not applied, errors may occur when the package is loaded. Cancel changes?”.
Escolha “Yes” agora. Toda vez que esse pacote for recompilado você deverá seguir estes passos.
Se você verificar o resultado, poderá comprovar um package de até vários megabytes na pasta de destino (em um projeto que temos por aqui, está em torno de 13MB). Então, acabou? Ainda não, só mais uns ajustes no executável.
Considerando que você seguiu meu conselho e criou uma pasta só para os binários (“bin”, onde estarão .exe, .dcp, .bpl), selecione o projeto do executável agora, vá até Project -> Options e escolha a opção “Directories/Conditionals”, lá em “Search path”, coloque o caminho da pasta Bin do seu projeto e faça o mesmo para o “Output directory”.
Ainda nesta tela, escolha a opção “Packages”, marque a opção “Build with runtime pakages” e no Edit, limpe tudo, para adicionar apenas o nome do seu pacote.
Clique em Ok e dê um build no seu aplicativo. Com isso, você já poderá notar uma redução de até 90% no tamanho do executável.
Como já dito antes, para a distribuição do nosso aplicativo teste, apenas os 2 arquivos acima marcados serão necessários.
Conclusão
Quais as vantagens que podemos tirar deste método? Teremos todas as vantagens de se utilizar pacotes dinâmicos em um aplicativo, como velocidade na compilação (lembre-se que na maioria das vezes, você compilará apenas o código associado diretamente ao seu aplicativo, pois o código dos componentes já está embutido no pacote), executáveis e dlls muito menores (muito mais simples e rápido para atualizar seu aplicativo junto aos clientes) e maior facilidade para implementação de plugins, seja com dlls ou bpls. Se você colocar regras de negócio em um pacote ou fizer uso de rotinas muito sigilosas, poderá distribuir apenas os bpls do pacote livremente entre os desenvolvedores do projeto sem medo, pois estará fazendo uso do encapsulamento de código a seu favor.
E as desvantagens? Se você considerar todos os benefícios desta abordagem, o fato de ter que atualizar um pacote de 13MB (se compactar pode cair para 4MB ou 5MB) toda vez que mudar algum componente não pesará tão contra assim.
Este exemplo criou um único pacote com todas as units utilizadas pelo aplicativo, mas se você preferir poderá criar 2, 3 ou quantos pacotes precisar, separando-os por categoria, como pacotes de componentes visuais, acesso a dados, regras de negócio ou outra categoria que for necessária para seu projeto.
Este é o meu primeiro post, e espero que seja útil para quem precisar aplicar tais conceitos.
Se você é novo por aqui, não deixe de assinar o feed RSS ou notificações por email. Não perca novos artigos!
Em 05 de Novembro de 2008 às 12:54
Wanderson, excelente artigo, muito boa a dica.
O artigo ficou muito bom, limpo, enxuto e conciso, na medida exata do que é necessário. Tu levas jeito para esse lance de escrever hein!!!
Parabéns cara.
Em 06 de Novembro de 2008 às 00:41
Obrigado Acid, pode ter certeza de que vou trazer mais artigos assim (se o Leonel aprová-los… :P)
Em 05 de Novembro de 2008 às 14:39
Parabéns Wanderson.
Só acrescentaria mais detalhes sobre como selecionar os pacotes que ficarão externos no Runtime Packages pois você cita sobre a criação de mais de um pacote externo e pode ter gente não entendendo como adiciona-lo para ser externo.
Em 06 de Novembro de 2008 às 00:26
Excelente observação. Inclusive pode ser até o caso de eu escrever um segundo artigo sobre o tema, abrangendo essa parte!
Em 05 de Novembro de 2008 às 16:46
Parabéns pelo artigo. BPL é muito bom mas dá trabalho em usar.
Excelente didática no contéudo.
Escreva mais
Abraços
Luis Wagner
Em 06 de Novembro de 2008 às 01:17
Exatamente Luis, lidar com BPLs é um pouco mais trabalhoso, mas pessoalmente eu gostei muito pela flexibilidade para se produzir qualquer aplicativo de médio a grande porte. E pode ter certeza, escreverei mais.
Em 06 de Novembro de 2008 às 10:04
Parabéns pelo artigo.
Vou estar testando e utilizando em minhas aplicações..
Em 13 de Novembro de 2008 às 12:27
Muito legal a dica. O toque do final é o que achei mais interessante: trabalhar com mais pacotes, utilizando essa mesma idéia e de uma maneira mais enxuta, comparada à forma tradicional de uso de packages.
Parabéns.