Você está na página 1de 6

: : www.mundoj.com.

br : :

Cecilia Fernandes
(cecilia.fernandes@caelum.com.br): desenvolvedora, instrutora e
consultora pela Caelum, cursa Ciência da Computação na USP e
estagiou na IBM Research em Nova York. Entusiasta de métodos
ágeis, faz palestras sobre o assunto e aplica métodos ágeis ao
seu dia-a-dia, desde metodologias e práticas até ferramentas para
auxiliar times ágeis, como a apresentada neste artigo.

Builds com Gradle:


programativo e declarativo
Builds Java são feitos com Ant e Maven há
anos. São duas excelentes ferramentas,
cada uma com suas vantagens e
desvantagens. Tentando encontrar
um balanço entre as duas, surgiu o
Gradle, que através da linguagem
Groovy fornece uma maneira simples de
trabalhar tanto declarativamente quanto
programaticamente, quando necessário.

Conheça a ferramenta de build para a JVM que está


ganhando muita popularidade.

urante o desenvolvimento de software, todo desenvol- O Gradle é um sistema de builds cuja configuração é feita progra-
vedor precisa automatizar o build de seu projeto e mais maticamente, usando uma elegante DSL em Groovy. Isso significa
frequentemente do que deveria, esta atividade se torna que um build feito em Gradle pode ter métodos, closures e tudo o
uma grande “dor de cabeça” ao fugirmos minimamente dos pa- mais que a linguagem Groovy permite.
drões preestabelecidos.
Neste artigo, você verá como configurar o build de um projeto em
As abordagens mais comuns em Java para configuração do build Java usando o Gradle, inclusive customizando diversas opções.
de projetos dividem-se entre o XML programático do Ant e o uso Conforme avançamos, a própria sintaxe do Groovy será explicada,
de Maven, mas sabemos que nenhuma delas é ideal – muitas ve- para que você seja capaz de continuar explorando o Gradle, ainda
zes, apenas convivemos com essas ferramentas por falta de opção que nunca tenha programado em Groovy antes.
ou, melhor dizendo, falta de opção compatível com o restante do
mercado. Já era hora de trocarmos a programação de XML e os Descubra quais vantagens fizeram com que tanto o próprio Groo-
inúmeros problemas de customizações, sem mencionar o tempo vy quanto o Grails e até mesmo o Hibernate estejam optando por
desperdiçado sempre que aguardamos o Maven sincronizar com o essa nova ferramenta gratuita de build, distribuida sob a licença
repositório, por uma nova alternativa. Apache 2.0.

40
Instalação ta um padrão, o que evita configuração desnecessária. Também é
possível alterar esses valores através da configuração sourceSets.
A instalação do gradle é bastante simples: basta baixar o pacote
de www.gradle.org, descompactá-lo e colocar o seu diretório bin Para começar a usar a estrutura já preparada para Java, basta in-
dentro do seu path. Sua única dependência é um JRE 1.5+ insta- dicar que você usará esse plugin, adicionando ao build.gradle,
lado e configurado. Rodando gradle –v deve listar as versões do necessariamente no início do arquivo, a linha abaixo.
gradle e de suas dependências.
apply plugin: 'java'
Primeiros exemplos
À moda do build.xml do Ant, o arquivo que configura um build É necessário que essa linha esteja logo no início do arquivo, ou
no Gradle chama-se build.gradle. Ele é um arquivo em Groovy, ao menos antes da primeira task que utilize a infraestrutura do
que contém a configuração do projeto através de objetos do tipo plugin de Java, já que o Gradle, como qualquer código Groovy,
“task”, que o Gradle é capaz de executar. interpreta o arquivo sequencialmente. Isso significa que, se cha-
marmos qualquer dependência do plugin de Java antes de decla-
Iniciemos nosso contato com a ferramenta através de um clássico rar que utilizaremos esse plugin, o Gradle falhará, dizendo que
Hello World, para ilustrar nosso exemplo. Em um arquivo build. não conhece tal dependência.
gradle localizado na raiz do projeto, escreva as seguintes linhas:
A partir desse momento, algumas tasks e variáveis padrão passam
a estar disponíveis e, num momento inicial, se seu projeto não
task olaMundo << { depender de nenhuma biblioteca, já é possível chamar a criação
println 'Oi mundo!' de um build dele, rodando no terminal o comando:
}

> gradle build


Em seguida, no terminal, execute:

A task build chama, internamente, diversas outras. Vejamos agora


> gradle olaMundo -q
sem a opção -q, mais a fundo o que acontece quando executa-
mos esse comando. Sua saída no terminal já nos provê bastante
Embora o parâmetro -q seja desnecessário, ele indica que pre- informação, indicando, linha a linha, quais outras tasks foram
ferimos a forma "quieta" de execução, que não informa o que internamente chamadas:
exatamente está sendo feito. Contudo, se for interessante ver
detalhadamente o que está sendo executado, basta rodar a mesma
:compileJava
linha sem esse parâmetro. :processResources
:classes
Tasks Gradle x Tasks Ant :jar
:assemble
Se você já teve contato com o Ant, poderia pensar que as tasks do :compileTestJava
:processTestResources
Gradle são equivalentes às tasks do Ant. Já vamos, então, desde o
:testClasses
início, desfazer essa confusão. :test
:check
Trabalhando com o Ant, tasks são comandos simples que o Ant
:build
consegue executar, como echo e javac. Uma sequência de tasks BUILD SUCCESSFUL
do Ant é chamada de Target. Já no Gradle, chamamos de tasks
o equivalente aos Ant targets, isto é, um conjunto de comandos
para executar uma operação, algo que seja valioso em nosso build. Acompanhe nessa lista o que é feito: o build começa compilando as
classes e copiando os recursos para as localizações pertinentes. Em
E para trabalhar com Java? seguida, cria o JAR e monta a estrutura da pasta build, contendo
todos os subprodutos dessas ações.
Seguindo o mote do Java de Convenção sobre Configuração, se
seu projeto seguir algumas poucas convenções, basta uma única As ações de verificação são, então, executadas, embora nesse
linha para montar um build inicial com o Gradle. particular momento não tenham efeito algum ainda, já que não
temos classes de teste. Em seguida, a verificação é finalizada e o
Entre essas convenções, destaca-se a localização das classes de build completo – se você não esqueceu de nada, o build será bem-
códigos de produção e de testes. Elas devem estar, respectivamen- sucedido. Repare que agora temos o jar do nosso projeto, apenas
te, em src/main/java e src/test/java. Similarmente, os resources de seguindo as convenções já conhecidas pelos usuários do maven, e
produção e de testes devem estar em pastas: src/main/resources e com o arquivo de configuração praticamente vazio.
src/test/resources.
Nosso próximo passo é, então, adicionar classes de teste e, com elas,
Se você já usou Maven, certamente notou que a convenção é a o JAR da biblioteca de testes – o que nos leva ao próximo problema.
mesma e isso é intencional – tanto facilita a migração quanto ado-

41
: : www.mundoj.com.br : :

Tratando dependências manifest.from("path/to/MANIFEST.MF")

A partir do momento que temos testes, temos quase que neces-


sariamente a adição da dependência de uma biblioteca de testes, A segunda maneira seria configurar o Manifest no próprio build.gra-
comumente o Junit. E, mesmo sem eles, é quase regra que nossos dle, centralizando ali todas as configurações do projeto. Nesse caso,
projetos dependam de outros JARs. Dessa forma, o gerenciamento podemos configurar o Manifest programaticamente e ele será gerado
dessas dependências é assunto mais do que importante em uma dinamicamente. No exemplo que estamos construindo, teríamos:
ferramenta de build.

O Gradle permite que resolvamos dependências buscando-as tan- manifest.mainAttributes("Main-Class": "br.com.empresa.projeto.Principal",


to em diretórios locais, como uma pasta "lib/" no projeto, quanto
em repositórios Maven ou Ivy. Se usando gerenciadores de depen- "Class-Path": "lib/xstream-1.3.jar lib/log4j-1.2.12.jar")
dências, os repositórios padrão já vêm pressupostos.

Para configurar as dependências do seu projeto, basta adicionar


comandos ao bloco "dependencies". No exemplo da Listagem Seja criando um MANIFEST.MF ou configurando suas infor-
1, pedimos que os JARs do JUnit e do Log4J sejam baixados do mações diretamente no build.gradle, a partir do momento que
repositório do Maven e, além disso, que os jars no diretório "lib/" essa configuração existe já é possível rodar o JAR chamando
sejam registrados. simplesmente "java -jar nomeDoJar". Nesse momento, rodemos
novamente, no terminal, o comando:

Listagem 1. Configurando dependências.


> gradle build
repositories {
mavenCentral()
Note, agora, que ao final da execução bem-sucedida, o Gradle
}
criará, na raiz do projeto, a pasta “build/”, com uma estrutura de
dependencies { diretórios dentro dela. Dentro dessa pasta, o JAR da aplicação é
compile group: ‘junit’, name: ‘junit’, version: ‘4.+’ colocado dentro da pasta “libs/”. Seu nome será, por padrão, no
compile group: ‘log4j’, name: ‘log4j’, version: ‘1.2.12’ formato: NomeDoProjeto,jar. O nome do projeto vai ser o nome
do diretório em que o build.gradle se encontra, ou você pode
lib = “$projectDir/lib” alterar esse nome setando a variável “rootProject.name” dentro de
compile files(fileTree(dir: lib as File, includes: [‘*.jar’])) um novo arquivo settings.gradle.
}
Se seu projeto for versionado, também é simples adicionar um nú-
mero de versão a ele e, automaticamente, ao nome do JAR gerado.
Dessa forma, combinamos tanto o download automático de de- Para essa configuração, faça:
pendências quanto o uso de bibliotecas que já estejam no nosso
diretório lib. É bastante comum deixar no diretório lib as biblio-
tecas que não estão disponíveis em repositórios maven, como é version = '0.5'
o caso do mail.jar, ou de algum JAR interno da sua empresa. Há
Dessa forma, o JAR gerado será automaticamente nomeado: NomeDoProje-
ainda uma sintaxe mais sucinta para declarar as dependências,
to-0.5.jar.
como: compile (‘junit:junit:4.+’).

Caso sua dependência esteja em algum outro repositório, diga-


mos, do Maven, você pode customizar os repositórios nos quais Criando distribuíveis
o Gradle buscará as dependências. Essa configuração é feita com-
plementando o bloco “repositories”. O próximo passo natural do seu projeto provavelmente será dis-
ponibilizar um entregável zipado contendo tanto o JAR da sua
aplicação quanto suas dependências e talvez alguns arquivos
repositories { acessórios a mais. Com o Gradle é muito fácil fazer isso. Basta
mavenCentral() escrever uma task do tipo Zip e, a partir do momento que ela
mavenRepo urls: ‘http://repository.jboss.org/nexus/content/groups/public' existir, o plugin do Java já tomará a iniciativa de rodá-la, criando
}
um arquivo com o conteúdo especificado na implementação dessa
task. No nosso exemplo, veja como fica a task de empacotamento,
Apenas configurando as dependências, o build já saberá quais JARs na Listagem 2.
usar se precisar executar os testes, por exemplo. Contudo, isso não
faz com que o Classpath seja configurado, ou seja, teríamos que rodar Listagem 2. Montando um entregável.
o projeto passando, em linha de comando, os JARs da aplicação.
task zip(type: Zip, dependsOn: jar) {
Para configurar informações que usualmente ficariam no Manifest, from “$buildDir/libs”
temos duas formas padrão. A primeira, trivial, seria exatamente criar into(‘lib’) {
um MANIFEST.MF e indicá-lo no build.gradle usando a linha: from configurations.runtime
}
}
42
Um pequeno detalhe sobre esse exemplo é que podemos criar uma Basta configurar a variável encoding das opções de compilação de
task de nome "zip" sem o parâmetro "dependsOn: jar", mas isso é classes e testes e ele já assumirá que deve usar esse encoding dali
apenas uma gentileza que o plugin do Java nos oferece por usar o em diante. Por exemplo, se o encoding adotado pela equipe é o
nome padrão dessa extensão. Porém, se a task tiver outro nome, é UTF-8, adicionaremos o seguinte código ao build.gradle:
necessário especificar esse parâmetro. Caso contrário, a task pode-
rá ser executada antes de o JAR ser montado, isto é, o Zip gerado
Listagem 3. Mudando o encoding.
conterá apenas as dependências, mas não o JAR da sua aplicação.

Destrinchando o pequeno código da Listagem 2, estamos dizendo options.encoding=’UTF-8’


que é uma tarefa a ser executada e que, no diretório raiz do Zip cria- }
compileTestJava {
do, será colocado o conteúdo da pasta “build/libs/”, que já vimos
options.encoding=’UTF-8’
ser o JAR da sua aplicação. Além dele, dentro de uma pasta “lib/” }
no pacote zipado, serão colocados os JARs dos quais ela depende.
Ou então, abusando um pouco da sintaxe do groovy, podemos sucintamente
Um pouco de Groovy escrever:

Os métodos from e into, inerentes de tasks do tipo Zip e do tipo [compileJava, compileTestJava]*.options*.encoding=’UTF-8’
Copy do Gradle, têm uma forma simples, que recebe um Object in-
Adicionando essas linhas, o Gradle interpretará seu código como UTF-8 na
dicando de ou para onde, respectivamente, vão os arquivos – você
compilação das classes.
pode ver essa forma sendo utilizada na primeira linha da task zip,
na Listagem 2.

Contudo, existe também uma sobrecarga desses métodos que re- Mais um pouco de Groovy
cebe, além desse parâmetro, uma closure que deve conter o lado
oposto dessa relação, isto é, se chamamos o from originalmente, Note, na Listagem 3, o código resumido para fazer o mesmo que
precisamos dizer onde os arquivos devem ser colocados e, no outro o código com blocos acima. Essa sintaxe também causa confusão
caso, vice-versa. ou estranheza, comumente. O que significa, entretanto, é bastante
simples.
Pode-se, inclusive, adicionar mais diretórios de origem e as subpas-
tas, usando essas closures. O Gradle cuida de montar o Zip da Os colchetes delimitam uma lista de dois itens, separados pela vír-
melhor forma possível, empacotando todos os arquivos indicados. gula. Em seguida, vemos uma incomum construção da forma *. que
significa “para cada item da lista cuja instrução se aplique, faça...”.
Isto é, lendo a sentença como um todo temos: “para cada item da
De volta ao exemplo lista de grupos de compilação, pegue as opções e, para cada opção
que tenha a propriedade encoding atribua UTF-8 a ele.” Esta é apenas
Adicionando a task de zip que acabamos de criar e entender ao
uma forma com características mais funcionais de lidar com objetos.
nosso build.gradle, basta rodar o build novamente para notar a dife-
rença: uma task zip surge entre as executadas pelo comando build.
Diferenciando JARs de testes
:compileJava Ao fazer testes automatizados, é frequente utilizarmos JARs, como
:processResources UP-TO-DATE Junit e Jmock ou Mockito, que servem exclusivamente para fazer
:classes rodar esses testes, e não serão usados na distribuição ou deploy. Já
:jar
que escrever testes automatizados é nada mais que obrigação de
:zip
um bom programador atual, o Gradle já vem preparado para lidar
:assemble
:compileTestJava com diferentes grupos de compilação.
:processTestResources UP-TO-DATE
:testClasses
Usando esse recurso, é fácil separar os JARs necessários para o
:test funcionamento da aplicação daqueles que são apenas usados pe-
:check los testes automatizados – e que, portanto, podem ficar fora do
:build pacote de produção.
BUILD SUCCESSFUL
Sempre que utilizamos o plugin de Java para o gradle, já obtemos
dois grupos de compilação padrão, o “compile” e o “testCompile”.
Problemas de encoding? Separando os JARs nesses grupos padrão, o próprio Gradle já é
capaz de separar corretamente os contextos.
Configurações de encoding são “pesadelos” na vida de muitos
programadores e, em particular, assombram os que configuram Mudando seu código da definição de dependências para usar am-
os builds de projetos. Misteriosas configurações que alteram ape- bos os grupos, então, teremos o código de dependências do build.
nas o escopo de compilação no Maven, mas não o de execução, gradle alterado como mostrado na Listagem 4.
confundem até o mais experiente programador. No Gradle, essa
configuração é trivial.

43
: : www.mundoj.com.br : :

Vamos então criar uma task que execute o bootstrap do servidor.


Listagem 4. Separando grupos de compilação. Nela, configuramos o Classpath para usar as dependências do
Tomcat, além de aproveitar para configurar alguns parâmetros da
dependencies{
JVM – veja a Listagem 6.
testCompile group: ‘junit’, name: ‘junit’, version: ‘4.+’
compile group: ‘log4j’, name: ‘log4j’, version: ‘1.2.12’
Listagem 6. Task para rodar o Tomcat 7.
lib = “$projectDir/lib”
compile files(fileTree(dir: lib as File, includes: [‘*.jar’]))
task runTomcat7(type: JavaExec, dependsOn: build) {
}
classpath configurations.tomcat
jvmArgs ‘-Xmx800M’
main = ‘org.apache.catalina.startup.Bootstrap’
Dessa forma, quando construirmos o pacote de distribuição, }
apenas os JARs de produção serão empacotados, mas quando
rodando os testes, o JUnit será incluído.
Para rodar essa task, basta executar no terminal: “gradle run-
Rodando seu projeto web Tomcat7”. Repare que agora não há uma integração clara entre
o Tomcat e o Gradle, o que torna necessário um diretório “conf/”
Caso seu projeto seja web, basta seguir as mesmas convenções do dentro do seu projeto, junto com o server.xml e a configuração
Maven, com seus JSPs e WEB-INF dentro de src/main/webapp. para que o Tomcat carregue o seu contexto de dentro do WAR que
O gradle por enquanto possui apenas suporte nativo ao Jetty 6, está em build/libs.
e para rodá-lo e fazer o deploy da sua aplicação web, basta fazer
Suporte ao Eclipse
gradle jetty
Assim como o Maven, o Gradle pode facilmente gerar os arquivos
necessários para que seu projeto seja importado dentro do Eclip-
Mas suponha que precisamos rodar em outro container, como o se. Basta avisar que usaremos o plugin do Eclipse, exatamente
Tomcat 7. Vejamos algo um pouco mais sofisticado no Gradle. como fizemos com o de Java logo acima (apply plugin: 'eclipse')
Poderíamos adicionar as dependências para que a nossa configu- e, então, rodar:
ração compile, mas aí, quando gerássemos nossa distribuição, o
tomcat 7 viria junto! gradle eclipse

Criando seu grupo de compilação


Ao rodar tal comando, ele gera até mesmo os arquivos necessários
Para organizar melhor, vamos criar uma configuração à parte, para o WTP, possibilitando o start do seu servlet container através
chamada “tomcat”, para guardar essas dependências: do Eclipse. É importante, no entanto, configurar uma variável de
classpath GRADLE_CACHE dentro do seu Eclipse apontando
para o diretório do seu usuário somado a /.gradle/cache.
configurations {
tomcat
} Excelente, mas...
Por ser uma ferramenta consideravelmente nova, ainda há muitas
Agora, dentro de dependencies, podemos adicionar as dependên- mudanças na própria sintaxe do Gradle, a cada lançamento de uma
cias referentes ao tomcat 7. Repare que há um erro no pom.xml nova versão – e, com isso, por mais que a documentação atual esteja
do tomcat 7.0.2, e por isso contornamos esse problema excluindo disponível é frequente que buscas tragam informações desatuali-
a dependência que foi registrada incorretamente (veja referência): zadas e que não mais correspondem à versão corrente do Gradle.

Ainda há, também, algumas deficiências em mensagens de erros


Listagem 5. Configurando web container no grupo de com- mais incomuns. Por exemplo, testando o que acontece se a internet
pilação. cai enquanto o Gradle conversa com um repositório Maven2, a
dependencies{ mensagem de erro descreve, não que o repositório não pode ser
alcançado, mas que tal JAR não existe, o que pode ser confuso.
// outras dependencias...
tomcat (‘org.apache.tomcat:tomcat-catalina:7.0.2’)
Lentidão característica
tomcat ‘org.apache.tomcat.embed:tomcat-embed-core:7.0.2’
tomcat (‘org.apache.tomcat:tomcat-jasper:7.0.2’) { O Gradle é, sem dúvida, um salto em simplicidade, quando com-
exclude group:’org.eclipse.jdt’, name:’ecj’,version:’3.6’ parado ao Maven ou ao Ant, contudo ainda sofre sensivelmente
} com a vagarosidade do Groovy. Por ser uma trivial chamada a có-
tomcat group:’org.eclipse.jdt.core.compiler’, name:’ecj’,version:’3.5.1’
digo Groovy, a cada instância o JIT não reaproveita as otimizações
que podem ter ocorrido previamente.
}

44
Felizmente, o tempo de build de um projeto aumenta cada vez como base. Essa ferramenta também vale um teste por sua rapi-
a uma taxa menor conforme a complexidade e o tamanho dele dez, principalmente se já houver familiaridade com a linguagem.
aumenta, mas ainda assim o tempo necessário para rodar é um Ela é, juntamente com o Gradle, recomendada por Jez Humble e
dos pontos mais notoriamente negativos do Groovy. David Farley no livro Continuous Delivery.

Sua ideia perde apenas para a Simple Build Tool (SBT): outra
ferramenta de build, voltada para Scala, que permite interação
programática através de um shell. A ideia é que o SBT roda duran-
Para Saber Mais te todo o tempo de desenvolvimento e, com isso, tem mais chance
Na edição 42, o artigo Conhecendo as Principais Linguagens de ter seu script otimizado•
para JVM já falava de Groovy e seu uso para produzir elegan-
tes DSLs como o Gradle.
Referências
Considerações Finais • Por que Gradle? Grupo JBoss responde
http://community.jboss.org/wiki/Gradlewhy
• Migrando do Maven pro Gradle
Ainda que bastante recente, o Gradle é uma ferramenta promis- http://www.beeworks.be/maven-to-gradle-part-1/
sora ao trazer grandes melhorias quando comparado aos seus • Erro no pom.xml do Tomcat 7.0.2
predecessores, Ant e Maven. Finalmente podemos configurar http://www.apacheserver.net/tomcat-7-pom-listing-wrong-ECJ-dependency-
at232339.htm
builds de forma programática e declarativa, usando uma sintaxe • Site do Gradle
elegante que encapsula o Ivy. O Gradle também possui plugins http://gradle.org/
para compilar e trabalhar com projetos em Scala e Groovy. • Documentação da versão citada no artigo
http://www.gradle.org/0.9-rc-1/docs/userguide/userguide.html
Outra alternativa bastante similar é o Apache Buildr (http://buildr.
apache.org/), que tem a mesma proposta, mas usa sintaxe Ruby

Você também pode gostar