Entendendo a instalação de componentes - Apresentando os vilões: Search Path & Library Path
Escrito por Carlos B. Feitoza Filho | |
Categoria: Artigos | |
Categoria Pai: Addicted 2 Delphi! | |
Acessos: 57403 |
Páginas dentro deste artigo
Apresentando os vilões: Search Path & Library Path
Sim! Estas duas configurações são maliciosas e são a causa de muitos problemas de instalação de pacotes e até mesmo de compilação de projetos em geral. Na verdade a maior vantagem de ambas é também sua maior desvantagem: a versatilidade! De tão versáteis, estas propriedades se tornam perigosas aos incautos e eu vou explicar o porquê.
Bem, um path funciona listando vários diretórios (pastas) de busca. Suponha que você tenha uns 100 diretórios listados, neste caso os arquivos que o Delphi procura podem estar em quaisquer destes diretórios, inclusive em mais de um deles e é aí onde está o lado negro dessa propriedade. Se você faz referência a MinhaUnit em uma cláusula uses e existe MinhaUnit.pas (ou .dcu) em mais de um desses 100 diretórios, o Delphi vai usar aquele que achar primeiro, segundo a ordem da lista de diretórios. Veja, por exemplo, a lista de um Library Path típico:
Acima, a ordem que o Delphi realiza a busca por um arquivo é, de fato, de cima para baixo. Se nossa unit estiver em todos estes caminhos, o Delphi vai usar a versão que está no primeiro diretório listado e vai ignorar a presença de tal unit nos demais. Se a versão contida no primeiro diretório for mais antiga do que aquela que está nos outros, podem haver erros de compilação ou comportamentos errados de um programa que use esta unit.
Caso no path escolhido pelo Delphi esteja uma versão compilada (.dcu) ela será usada, tal como foi dito anteriormente, mas caso este arquivo .dcu seja uma compilação de uma versão diferente daquela que se espera, a famosa mensagem "Unit MinhaUnit was compiled with a different version of MinhaOutraUnit.TMinhaClasse" será exibida! Ignore a posição de MinhaUnit nesta mensagem. Ela poderia estar no lugar de MinhaOutraUnit, não importa, o que importa é que este tipo de erro acontece por existirem versões de arquivos .dcu diferentes daquelas que deveriam existir.
Se você estiver tendo alguns dos problemas listados aqui, o primeiro passo é procurar nos caminhos listados tanto no Library Path como no Search Path do projeto as units problemáticas e verificar se elas não se repetem, mantendo apenas uma cópia de cada uma no seu caminho correto. Procure sempre por nomedaunit.* e mantenha inicialmente apenas arquivos .pas. Isso é uma regra geral para ajudar a resolver problemas rapidamente, mas como eu explicarei posteriormente neste artigo, devemos manter no Library Path apenas arquivos .dcu, .res, .inc e .dfm, enquanto que no Search Path só devem existir arquivos .pas.
A fim de tirar você da ignorância quanto à instalação de pacotes eu fiz este artigo e a partir deste ponto eu vou explicar para que servem cada uma das configurações, de forma que você as use corretamente e possa amaldiçoar quem as usa de forma errada.
O que é o Search Path?
Todos os projetos no Delphi possuem o Search Path que nada mais é do que uma coleção de caminhos (paths) onde o Delphi busca por arquivos que são referenciados dentro do projeto em cláusulas uses ou diretivas {$R} ou {$I}. O Search Path deve ser considerado como uma lista privada de caminhos acessíveis apenas pelo projeto onde ele for configurado.
Suponha que você possui um sistema dividido em módulos executáveis. Neste caso você terá n projetos Delphi, e caso dentro deste sistema, projetos distintos utilizem uma mesma unit, esta unit pode ficar em um único local e ser referenciada por cada um dos projetos por meio do Search Path, configurado individualmente em cada um deles.
Um exemplo de uso real disso é quando se desenvolve uma aplicação DataSnap, a qual tem no mínimo dois projetos, um para o cliente (Thin Client) e outro para a camada do meio (MiddleWare). Ambos os módulos fazem parte de um único sistema (separado pelo DataSnap), logo eles podem compartilhar arquivos entre si e estes arquivos devem ser referenciadas por meio do Search Path de cada um dos projetos (Thin Client e MiddleWare).
Falando especificamente de units, tanto arquivos .dcu como arquivos .pas podem existir nos caminhos do Search Path, no entanto, como ele é uma coleção de caminhos de busca de um projeto específico, fica claro que arquivos listados no Search Path fazem parte do código-fonte do projeto onde ele for definido, logo, as units presentes nos caminhos de um Search Path devem ser arquivos .pas.
Acima podemos ver, mais ao fundo, a caixa de diálogo Project Options, que pode ser acessada através do menu Project > Options ou através da combinação de teclas Shift+Ctrl+F11. A localização da configuração do Search Path varia de acordo com a versão do Delphi. A imagem mostra a versão do Delphi XE5, mas se seu Delphi não apresentar a caixa de diálogo como na imagem, certamente será fácil de achar esta configuração.
Clicando no botão de reticências vai exibir o editor do Search Path, o qual, na imagem, mostra 3 caminhos. Ao compilar este projeto estes 3 caminhos serão vasculhados em busca de arquivos referenciados em cláusulas uses e diretivas {$R} e {$I}.
O papel do Search Path nos pacotes
O Search Path parece ser bem útil para organização de projetos, no entanto, no tocante aos pacotes especificamente, seu uso deve ser limitado apenas para indicar caminhos com arquivos .res, .dcr, .rc, .dfm e .inc. O motivo disso é que o Delphi não permite que pacotes distintos contenham units compartilhadas entre si.
Se você usar o Search Path para compartilhar units entre pacotes distintos, duas coisas vão acontecer. No caso mais brando será emitido um aviso ao compilar, e no caso mais grave seu pacote não será compilado ou não poderá ser carregado pelo Delphi.
No primeiro caso, a referência indireta por meio de Search Path vai gerar o seguinte aviso:
[dcc32 Warning] MeuPacote.dpk(88): W1033 Unit 'UMinhaUnit' implicitly imported into package 'MeuPacote'
A ajuda do Delphi é clara a respeito deste aviso, quando diz "This message will help the programmer avoid violating the rule that a unit may not reside in more than one related package", ou seja, pacotes relacionados não podem conter referências às mesmas units. Um pacote está relacionado a outro quando, por exemplo, PackageB.bpl depende de PackageA.bpl, porque PackageB.bpl contém em sua cláusula requires uma referência a PackageA.dcp. Quando dois pacotes estão relacioados desta forma apenas um dos pacotes precisa conter diretamente todas as units, por exemplo, suponha que PackageA.bpl contenha as units Unit1.pas, Unit2.pas e Unit3.pas. Suponha que PackageB.bpl também precise destas units (todas ou algumas delas, não importa). Neste caso, é suficiente que PackageB.bpl contenha na sua cláusula requires uma referência a PackageA.dcp para que ele "enxergue" e use todas as units que existem em PackageA.bpl, sem necessidade de duplicar ou compartilhar units.
O segundo problema vai acontecer se você carregar (instalar) um pacote e depois tentar compilar um outro pacote que tem units compartilhadas com o primeiro, o seguinte erro de compilação vai aparecer:
[dcc32 Error] MeuPacote.dpk(45): E2200 Package 'MeuOutroPacote' already contains unit 'MinhUnit'
Caso você consiga compilar os dois pacotes com units compartilhadas entre si e carregar um deles, ao tentar carregar o segundo, o seguinte erro será exibido:
Em suma, o Delphi vai dar um jeito de evitar que você faça esse tipo de coisa, portanto, vou deixar abaixo um aviso importante:
NÃO USE O SEARCH PATH PARA COMPARTILHAR UNITS ENTRE PACOTES. AO INVÉS DISSO, COLOQUE TODAS AS UNITS COMPARTILHÁVEIS EM UM DOS PACOTES E FAÇA COM QUE OUTROS PACOTES DEPENDAM DELE
O que é o Library Path?
O Library Path é uma configuração que afeta todos os projetos (configuração global) e sua definição é basicamente a mesma do Search Path. Ele é, pois, uma coleção de paths onde o Delphi busca por arquivos. A diferença entre o Library Path e o Search Path é apenas quanto aos tipos de units que devem ser encontradas em cada um deles e quanto a sua especificidade.
Quanto aos tipos de units, enquanto no Search Path devemos ter apenas units não compiladas (.pas), no Library Path devemos ter apenas units compiladas (.dcu). A especificidade refere-se ao quão as units estão relacionadas ao projeto. Observe a imagem a seguir:
Nesta imagem, a busca por units se dá no sentido das setas. Inicialmente se busca dentre as units do próprio projeto, em seguida se tenta encontrar as units no Search Path e por fim no Library Path. Units que pertencem diretamente ao projeto devem ser exclusivamente arquivos .pas, pois estes arquivos podem ser editados por nós e compilados posteriormente para gerar o arquivo final de nosso projeto (.exe, .dll, .bpl, etc.).
Units que estão no Search Path também devem ser arquivos .pas, e também pertencem ao projeto, pois o Search Path é uma configuração específica de cada projeto, a diferença é que as units acessadas via Search Path também podem ser usadas por outros projetos em um sistema que utiliza vários módulos (várias DLLs, vários executáveis, etc.). Faz sentido que projetos distintos façam uso de units em comum, quando estas units compartilharem código que pertença exclusivamente a estes projetos, seja por conterem regras de negócios específicas, seja por fazerem parte de algum framework customizado para alguns projetos apenas. Estas units, portanto, podem ser colocadas em diretórios que estão fora da hierarquia de diretórios dos projetos e poderão ser encontradas via Search Path.
Units que estão no Library Path devem ser exclusivamente arquios .dcu. Esta é a dica de ouro que justifica a existência de todo este artigo. Observe novamente a tela de edição do Library Path:
O primeiro path referenciado no Library Path informa ao Delphi onde encontrar todas as units compiladas (.dcu) de sistema. Units de sistema são units do próprio Delphi, que nós usamos frequentemente, tais como StdCtrls, SysUtils, StrUtils, dentre outras. Se você remover este path o Delphi vai parar de funcionar. No caminho indicado, caso você tenha curiosidade, todos os arquivos presentes são binários e lá, além de encontrarmos arquivos .dcu, também encontraremos alguns arquivos .dfm.
O Library Path, claro, também funciona apontando para caminhos com arquivos .pas, no entanto, por esta ser uma configuração global, não faz sentido que projetos completamente distintos compilem esses arquivos .pas toda vez, que é justamente o que acontece quando arquivos .pas existem no Library Path.
Isso mesmo, arquivos de código-fonte .pas encontrados via Library Path são compilados separadamente por cada projeto que fizer referência a suas units, e estas units são salvas no Unit Output Directory (veja mais adiante neste artigo) do projeto, dando a falsa impressão de que aquela unit pertence ao projeto diretamente, quando na verdade ela deveria ter sido apenas referenciada de forma binária indiretamente, para apenas ser usada na fase de linking de um projeto.
Units encontradas via Library Path não precisam ser modificadas e consequentemente não precisam ser compiladas, além disso, elas podem ser usadas por quaisquer de nossos projetos, portanto, elas não podem estar ligadas a projetos específicos, como acontece com o Search Path.
Quando o Library Path está configurado corretamente, nenhuma de suas units será incluída no Unit Output Directory de nenhum projeto e consequentemente projetos diferentes não conterão sua própria versão de um .dcu que deveria ser único, por nunca precisar sofrer alterações por ser genérico (utilizável em vários projetos diferentes).
O fato de encontrarmos apenas units compiladas no Library Path nos leva a concluir que em algum momento elas foram compiladas, portanto, units presentes no Library Path foram criadas durante a compilação de algum pacote, logo, normalmente os caminhos existentes no Library Path pertencem, de uma forma ou de outra, a pacotes.