Back-End

5 nov, 2012

Building com Phing – Parte 01

Publicidade

Salve, caros.

Estou voltando com uma série de artigos (antes de 2013, por mais incrível que pareça). Pegando carona no último, no qual usamos uma receita de bolo para Integração Contínua, vamos de fato fazer maravilhas, entrando no maravilhoso mundo do maravilhoso Phing (tá, muito “maravilhoso” até pro meu gosto).

Antes até de falar sobre o Phing, vamos falar sobre o que ele é responsável: Build.

Building é todo e qualquer processo executado sobre o código fonte a fim de gerar artefatos de software independentes. Por padrão, uma compilação é um build. “Ok, mas por que diabos no mundo você está falando de compilação e PHP no mesmo artigo?” – você deve estar se perguntando. Eu disse que por padrão, mas não necessariamente.

Geralmente, o que você faz antes de executar seu código fonte em um ambiente que não é o seu? Limpa logs, relatórios, cache… roda seus testes unitários (se não faz teste unitário ou não sabe o que é isso, algo está beeeeeeeeeem errado)… enfim… você normalmente já executa uma lista de tarefas antes de usar o código PHP que está na sua máquina antes de executá-lo em outra. Você já faz building… mas manualmente.

Ferramentas de building existem a rodo por aí. Você já instalou alguma extensão php via pecl? Ou mesmo baixando alguma do pecl.php.net e instalando “manualmente”? Provavelmente, você já viu aquelas linhas “Checking Foo OK… Checking Bar Ok…” – isso é gerado por uma ferramenta chamada (Gnu)make … que é um automatizador de build (ok, você deve estar pensando agora “tá louco? eu uso make para compilar os programas” – se sim, aguarde…); como geralmente se instala uma extensão:

  • download do pacote da extensão
  • descompactação da extensão
  • cd /path/para/o/diretorio/da/extensão
  • phpize (que gera artefatos a partir do código fonte especificamente para a versão do PHP que está sendo alvo da extensão)
  •  make (que testa as dependências da extensão e a compila)
  •  make install (que instala de fato a extensão, agora já compilada)

Não é o make que compila o código C da extensão – é o GCC (ou outro compilador disponível em seu sistema operacional) que de fato compila – o make realiza os testes, executa o compilador e instala… como tarefas predefinidas. O próprio comando pecl por si só é uma ferramenta de automação também.

Então qual é a proposta? Em vez de rodar manualmente cada uma das suas operações de build, automatize-as. Geralmente as ferramentas de build contam com um “buildfile” – que é um descritor das tarefas que podem ser executadas pela ferramenta. Ao executá-la, o buildfile é lido, e a ferramenta executa as tarefas nele contido. Mas nem todas as tarefas descritas precisam ser executadas, como no caso da nossa extensão: o make checa o ambiente e compila se tudo estiver correto, e o make install (outra tarefa) copia a extensão para o lugar correto.

O Phing é um automatizador de build, baseado no Apache Ant, feito em, e para PHP. Entre os destaques dessa ferramenta estão:

  • muitas tarefas prontas
  • possibilidade de criação de tarefas
  • muiltiplataforma
  • instalável via pear

Mas para adiantar algumas coisas, vamos instalar o Phing:

pear config-set auto_discover 1 && pear install -a pear.phing.info/phing

Com isso (e uns  minutinhos de espera), você já terá o dito cujo em seu ambiente. Vamos começar um projeto do zero com o Phing. Vá até seu diretório de projetos e crie mais um. Dentro desse diretório, crie e edite um arquivo build.xml.

Sim, a base da operação do Phing é um arquivo xml – evitei ao máximo abrir o jogo com relação a isso, mas não deu… o buildfile do Phing é o maldito do build.xml – mas vocês vão gostar dele… principalmente começando com um basicão assim:

<?xml version="1.0"?>
<project name="myChuckProject" basedir="." default="helloworld">
<target name="helloworld">
<echo msg="Hello Phing!"/>
</target>
</project>

Explicando o que cada tag está fazendo aí:

  • project – a tag root, na qual você define o base path, nome do projeto e target default
  • target – um passo em seu processo de build, no qual você pode agrupar várias tarefas
  • echo– uma tarefa predefinida que faz o que se propõe: “echoar” uma mensagem.

Para executar, basta no terminal executá-lo com o comando phing:

Essa é a saída do nosso build.xml, inclusive com o tempo decorrido na execução. Por padrão, é usado o arquivo build.xml – mas você pode usar outro arquivo usando o parametro -f outro.xml. Ao ser executado, o Phing busca pelo target padrão (definido pelo atributo default, que é obrigatório) e o executa – mas você pode forçar que outro target seja executado, bastando usá-lo como parâmetro (assim como no caso de make install). Como disse antes, o Phing já possui várias tarefas predefinidas – cada uma delas tem suas particularidades e vamos falar de algumas neste artigo.

O Phing disponibiliza um mecanismo de variáveis, o que nos dá muita liberdade para trabalhar com ele. Para definir variáveis, usamos a tarefa property e, para usá-las, utilizamos a sintaxe ${variavel}. Também é possível usar um arquivo externo para defini-las de uma vez, no formato variavel=valor – e o melhor é que podemos, assim como no php, usar variáveis para compor valores de variáveis.

Vamos a mais um exemplo que criará uma estrutura básica de diretórios para nosso projeto, escrevendo bootstraps e frontcontroller, utilizando um arquivo de variáveis externo.

project.name=LeProject
vendor=Duodraco
version=0.0.1
lista.generica=a,b,c

<?xml version="1.0"?>
<project basedir="." default="start">
    <property file="build.properties" />
    <target name="start" depends="cleanup,dirs,bootstrap,frontcontroller" />
    <target name="cleanup">
        <delete dir="application" includeemptydirs="true" verbose="false" failonerror="true" />
        <delete dir="vendor" includeemptydirs="true" verbose="false" failonerror="true" />
        <delete dir="tests" includeemptydirs="true" verbose="false" failonerror="true" />
        <delete dir="web" includeemptydirs="true" verbose="false" failonerror="true" />
        <delete dir="log" includeemptydirs="true" verbose="false" failonerror="true" />
        <delete dir="cache" includeemptydirs="true" verbose="false" failonerror="true" />
    </target>
    <target name="dirs">
        <mkdir dir="application/library/${vendor}/${project.name}" />
        <mkdir dir="tests/${$vendor}/${project.name}" />
        <mkdir dir="vendor" />
        <mkdir dir="web/script" />
        <mkdir dir="web/style" />
        <mkdir dir="web/media" />
        <mkdir dir="log"/>
        <mkdir dir="cache"/>
        <chmod mode="777" file="log"/>
        <chmod mode="777" file="cache"/>
    </target>
    <target name="bootstrap">
        <property name="autoloader" value="spl_autoload_register(function ($classname) {&#10;
$classname = ltrim($classname, '\\');&#10;
preg_match('/^(.+)?([^\\\\]+)$/U', $classname, $match);&#10;
$classname = str_replace('\\', '/', $match[1]).str_replace(['\\', '_'], '/', $match[2]).'.php';&#10;
require_once $classname;&#10;});"/>
        <property name="app_includepath" value="set_include_path(__DIR__.'/library'.PATH_SEPARATOR.get_include_path());"/>
        <property name="test_includepath" value="set_include_path(__DIR__.'/../library'.PATH_SEPARATOR.__DIR__.'/../tests'.get_include_path());"/>
        <property name="app_bootstrap" value="&lt;?php&#10;${app_includepath}&#10;${autoloader}"/>
        <property name="test_bootstrap" value="&lt;?php&#10;${test_includepath}&#10;${autoloader}"/>
        <echo msg="${app_bootstrap}" file="application/bootstrap.php"/>
        <echo msg="${test_bootstrap}" file="tests/bootstrap.php"/>
    </target>
    <target name="frontcontroller">
        <echo msg="&lt;?php&#10;require __DIR__.'/../application/bootstrap.php';" file="web/index.php"/>
    </target>
</project>

Com isso, temos um esqueleto de projeto, pronto para receber outros projetos (que sigam a PSR-0), testes unitários e seu precioso código. Veja que utilizamos vários targets e um target vazio, com um atributo depends. Esse atributo define que os targets listados nele devem ser executados em sequência antes do target no qual ele está definido.

Ah, outro ponto que ainda não mencionei sobre ferramentas de build: ele para o processo em caso de falhas. No exemplo anterior, caso não conseguíssemos criar os diretórios, ele abortaria o processo, indicando o erro, impedindo outros erros como a criação do bootstrap em um diretório que não existe. Esse mecanismo das ferramentas de build é o grande trunfo de usá-las, por exemplo, em scripts de instalação. Imagine que seu sistema depende de uma extensão muito específica – você pode testar se ela está instalada e em uma versão suportada por sua aplicação antes de realizar seu deploy de fato.

Por hora é só. Na parte 2, abordarei recursos do Phing que de fato nos ajudam na automação da árdua tarefa de building. Até lá.