Maven

Outubro 2, 2011

Uma ferramenta que conheci recentemente é o apache maven, que serve para gerenciar projetos java, como por exemplo compilar código java, gerar documentação, fazer deploy de aplicações web etc.

O objetivo desse post é cobrir os principais conceitos dessa ferramenta, apontando referências para quem quiser se aprofundar mais. Me baseei na versão mais recente do maven, o maven 3 (versão 3.0.3 mais especificamente).

O arquivo pom.xml

O maven usa um arquivo de configuração chamado Project Object Model (POM) para determinar as características do projeto e suas dependências.

Maven usa o paradigma de convenção ao invés de configuração. Isso quer dizer que, a menos que explicitado de outra forma, ele supõe configurações padrões (como por exemplo a localização dos códigos-fontes), o que permite que o arquivo pom.xml fique bem pequeno.

Um exemplo de um arquivo pom.xml é o seguinte:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>br.com.organizacao</groupId>
  <artifactId>teste</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
</project>

No exemplo acima, modelVersion define a versão da estrutura POM utilizada e a versão atualmente usada pelo maven é a 4.0.0. Segundo o blog da Caelum [2], os seguintes parâmetros são suficientes para identificar unicamente um projeto:

  • groupId – um identificador da empresa/grupo ao qual o projeto pertence. Geralmente o nome do site da empresa/grupo ao contrário;
  • artifactId – o nome do projeto;
  • version – a versão atual do projeto (PS: o modificador SNAPSHOT indica que essa versão é de desenvolvimento);

Além disso, no livro Maven By Example [1], eles definem o conceito de coordenadas maven, que é um conjunto de informações utilizadas para se referir a um projeto. Além das três propriedades acima, inclue-se o parâmetro packaging, que definirá se o pacote será comprimido em um jar, war, ear, etc. Dessa forma, o projeto acima seria referido por: br.com.organizacao:teste:jar:1.0-SNAPSHOT.

O maven implementa um mecanismo de herança. Isso quer dizer que um dado POM herda as configurações de seus ancestrais podendo sobrescrevê-las. Existe um POM que é ancestral de todos os POMs, o chamado super POM, de onde se herdam as configurações padrões.

Outro mecanismo é o de agregação, onde um POM principal pode definir módulos (sub-projetos) associados a outros POMs, que serão chamados pelo POM principal. Em [4], há um tópico detalhando e fornecendo exemplos para esses mecanismos.

Plugins

Se formos ver, o tamanho do maven é bem pequeno. Isso porque a maioria de suas funcionalidades é provida através de plugins. Um plugin consiste basicamente de uma coleção de goals. A sintaxe para executar um goal é

$mvn <plugin>:<goal>

Um exemplo é o plugin compiler, que possui o goal compile. Esse goal compila o código java e pode ser invocado da seguinte maneira:

$mvn compiler:compile

Repositório central do maven

Tanto os plugins quanto as bibliotecas mais comuns são mantidas em um repositório remoto central, chamado Maven Central Repository. Quando há uma dependência, quer de um plugin ou de uma biblioteca, o maven consulta esse repositório e cuida de fazer o download automaticamente.

Os objetos baixados são armazenados em um repositório local, que fica geralmente no diretório ~/.m2. Quando fazemos o build de um projeto nosso com o maven, ele também é copiado para esse diretório.

Ciclo de Vida do Build

O maven utiliza o conceito de ciclos de vida, que são compostos por uma sequência ordenada de fases. Há três ciclos de vida pré-existentes no maven: clean, default e site. O ciclo de vida default é composto pela sequência de fases: validate, compile, test, package, integration-test, verify, install, deploy (na verdade essa sequência está simplificada – a lista completa está nessa referência). Essas fases são sempre executadas nessa ordem. Assim, se executarmos a fase

$mvn install

Todas as fases que são anteriores a install serão executadas. Se quisermos executar o ciclo de vida default completo, basta executar a última fase, que é o deploy.

Na verdade uma fase não é executada, mas sim os goals associados a elas. Alguns goals já vêm associados por padrão a algumas fases. Por exemplo, ao declararmos o tipo de packaging como jar, o goal compiler:compile é associado à fase compile do ciclo de vida default. Para saber quais goals estão associados a quais fases por padrão, consulte essa referência.

Podemos associar mais goals às fases de build especificando no próprio arquivo pom.xml. Em [5], supõe-se a existência de um plugin chamado display-maven-plugin, que tem o goal time, que imprime o horário atual na saída padrão. Se quisermos imprimir o horário após a fase de testes terem sido executados, podemos associar esse goal à fase test, como no exemplo a seguir:

...
 <plugin>
   <groupId>com.mycompany.example</groupId>
   <artifactId>display-maven-plugin</artifactId>
   <version>1.0</version>
   <executions>
     <execution>
       <phase>test</phase>
       <goals>
         <goal>time</goal>
       </goals>
     </execution>
   </executions>
 </plugin>
...

Note que os conceitos de ciclos de vida e fases são uma maneira de organizar os diversos goals usados no processo de build. Em teoria poderíamos especificá-los explicitamente e estes seriam executados na ordem em que foram passados à linha de comando.

Plugin para o eclipse

Finalmente, para quem usa eclipse, existe um plugin para integração com o maven. A versão do eclipse que estou usando é a 3.7 (Indigo) e o plugin é o m2eclipse.

Para instalar o plugin, vá em Help > Install New Software…

Na barra de endereços coloque: http://download.eclipse.org/technology/m2e/releases

Selecione o item Maven Integration For Eclipse e siga as instruções até concluir a instalação. Algumas funcionalidades que esse plugin oferece são:

Importar um projeto que usa maven, no eclipse. O plugin utiliza informações do pom.xml para isso. Depois de instalado o plugin, vá em:

File > Import… > Maven > Existing Maven Projects

Next, preencha o campo Root directory e então Finish. Pronto, o projeto deve ter sido importado.

Uma alternativa é usar o plugin do próprio maven, o eclipse, para gerar os .classpath e .project. Para isso basta fazer

mvn eclipse:eclipse

Depois é só importar o projeto no eclipse (General > Existing Projects into Workspace).

Fazer o build do projeto dentro do eclipse.

Figura 1: fases do maven providas pelo plugin m2e

Há algumas fases pré-existentes providas pelo plugin, como as da Figura 1. Entretanto, se quisermos personalizar o build, basta ir em:

Run > Run As… > Maven build…

Podemos configurar o build do maven, ajustando os goals ou fases (e.g. clean, package, install, compiler:compile) e definindo opções como modo offline (opção -o), pular testes (opção -Dmaven.test.skip=true), etc.

Para um tutorial mais completo, refira-se a [3], que trata de assuntos como a criação de projetos usando o plugin archetype do maven, importação de projetos (inclusive via checkout de um repositório remoto), etc.

Leitura adicional

[1] Livro Maven by Example
[2] Processo de build com o Maven
[3] Introduction to m2eclipse
[4] Introduction to the POM
[5]Introduction to the Lifecycle

Anúncios

Cmake

Junho 5, 2011

Conforme eu tinha comentado em um post anterior, minha tarefa dessa semana era desenvolver um shader para interfacear com o OSL. A primeira etapa dessa tarefa é permitir a chamada de funções do OSL. Apanhei bastante para fazer isso.

Recentemente o BRL-CAD migrou seu sistema de construção do autotools para o cmake. O pouco que sei sobre cmake, é que ele é um sistema multi-plataforma para compilar um projeto. Você descreve, em um nível mais alto, os CMakeLists.txt em cada diretório do seu projeto. No caso mais básico, executa o comando cmake no diretório raíz e serão gerados os Makefile‘s. Basta então executar make para compilar e make install para instalar.

Eu venho usando o cmake de maneira bem cut and paste no meu mestrado por causa do CGAL. Decidi estudar um pouco mais sobre esse sistema e escrever sobre as principais componentes.

Geração de executável

A principal função presente no cmake é a de gerar executáveis. Dados vários arquivos de código-fonte, por exemplo a.c, b.c e c.c, sendo que o main está em a.c, uma maneira de gerar um executável usando o gcc é o seguinte:

gcc -c b.c c.c
gcc a.c b.o c.o -o executavel

Com o cmake isso simplifica para

add_executable(executavel a.c b.c c.c)

Geração de biblioteca estática

Outra possibilidade é gerar bibliotecas estáticas. Se tivermos funções definidas nos arquivos a.c e b.c e quisermos disponibilizá-las para outros programas utilizarem, podemos fazer:

gcc -c a.c b.c
ar -cvq libab.a a.o b.o

Para que um programa c.c possa usá-la, fazemos a linkagem da seguinte maneira:

gcc c.c -o executavel -L/caminho/para/libab/ -lab

que é equivalente a:

gcc c.c -o executavel /caminho/para/libab/libab.a

No cmake essas tarefas podem ser realizadas pelos seguintes comandos

add_library(libab a.c b.c)
add_executable(executavel c.c)
target_link_libraries(executavel libab)

Uma excelente referência sobre bibliotecas no linux é a do yolinux.com

Dependências

Agora, suponha que queremos linkar o nosso código a uma biblioteca já compilada. Vamos supor que essa biblioteca, chamada libxyz.a, esteja em /home/jose/programa/lib e o cabeçalho com as funções, chamado xyz.h, esteja em /home/jose/programa/include.

Fazendo manualmente ficaríamos com algo do tipo

gcc -I/home/jose/programa/include -L/home/jose/programa/lib -lxyz c.c -o executavel

Uma das maneiras de se encontrar uma biblioteca usando o cmake é da seguinte maneira:

find_library(xyz_library NAMES xyz PATHS /home/jose/programa/lib)

No exemplo acima, xyz_library é um nome qualquer para nos referenciarmos à biblioteca. Em NAMES colocamos o nome da biblioteca sem o prefixo lib e a extensão (por exemplo, libxyz.a vira xyz) em PATHS o caminho para essa biblioteca (PS.: podemos colocar vários caminhos se a priori não sabemos onde a biblioteca libzyx.a está).

Para fazer a ligação basta fazer:

target_link_libraries(executavel ${xyz_library})

Para encontrar o cabeçalho com as funções, usamos

find_path(xyz_include NAMES xyz.h PATHS /home/jose/programa/include)

Para informar ao compilador o caminho de xyz.h, basta fazer

include_directories(${xyz_includes})

Geralmente a busca por bibliotecas externas é modularizada para haver re-uso de código. Para cada biblioteca externa, podemos definir um arquivo com nome descritivo e extensão cmake. Por exemplo, suponha que queremos encontrar a biblioteca Boost. Podemos definir um arquivo FindBOOST.cmake contendo código para encontrar as bibliotecas e os cabeçalhos.

Cada um desses arquivos são postos em um diretório separado, que deverá ser salvo na variável interna do cmake chamada CMAKE_MODULE_PATH. Um modo de fazer isso é:

set(CMAKE_MODULE_PATH "/caminho/para/os/arquivos/Find/")

Para executar os comandos desse arquivo, dentro de qualquer CMakeLists.txt dentro do projeto, basta fazer algo como:

include(FindBOOST)

Sub-diretórios

Em geral cria-se um arquivo CMakeLists.txt por diretório. Para que o cmake processe esses arquivos, os diretórios pais devem informá-lo com o comando add_subdirectories. Se por exemplo quisermos incluir os diretórios sub1 e sub2 filhos do diretório atual, basta incluir as seguintes linhas:

add_subdirectories(sub1)
add_subdirectories(sub2)

Compilação de código

É possível controlar o modo como a compilação é feita com o cmake. Um dos principais meios para se fazer isso é através das variáveis CMAKE_C_FLAGS (para C) e CMAKE_CXX_FLAGS (para C++).

Ao fazer:

set(CMAKE_C_FLAGS -Wall -Werror)

A próxima vez que o um programa C for compilado, essas flags serão adicionadas à compilação. Para verificar isso, uma flag muito interessante a ser setada no comando make, é o VERBOSE. Ao setar essa variável, toda vez que você executar o comando make, ele exibirá os comandos de compilação que estão sendo executados.

make VERBOSE=1

Ele é bastante útil também para “debugar” um arquivo do tipo CMakeLists.txt, para ver se ele está gerando comandos de compilação do jeito que esperamos.

Integração do cmake com o emacs

(Seção adicionada em 6 de Junho de 2011)

Nesse wiki do cmake há dicas para configuração do emacs para colorir corretamente arquivos do tipo CMakeLists.txt e .cmake. Geralmente o emacs vem configurado por padrão, mas em todo caso, basta baixar o arquivo cmake-mode.el e adicionar as seguintes linhas no arquivo de configuração:

; Add cmake listfile names to the mode list.
(setq auto-mode-alist
	  (append
	   '(("CMakeLists\\.txt\\'" . cmake-mode))
	   '(("\\.cmake\\'" . cmake-mode))
	   auto-mode-alist))

(autoload 'cmake-mode "~/Downloads/cmake-mode.el" t)

Troque Downloads pelo local onde você baixou o cmake-mode.el.

Se você usa abas no emacs, ao abrir vários arquivos CMakeLists.txt, eles serão chamados de CMakeLists.txt, CMakeLists.txt<2>, CMakeLists.txt<3> e assim por diante. Isso é bem incômodo, pois você sempre tem que olhar o conteúdo para ver de qual CMakeLists se trata.

Para resolver esse problema, adicione a seguinte rotina no seu arquivo de configurações. Ela adiciona um prefixo contendo o caminho de cada arquivo. Muito útil!

;; uniquify.el is a helper routine to help give buffer names a better unique name.
(when (load "uniquify" 'NOERROR)
  (require 'uniquify)
  (setq uniquify-buffer-name-style 'forward)
  ;(setq uniquify-buffer-name-style 'post-forward)
)

Leitura Complementar

Ao invés de reunir uma série de referências, deixo apenas esse post do stackoverflow para maior abrangência: cmake tutorial


Escrevendo com o emacs

Abril 3, 2011

Desde 2005 uso emacs devido às aulas introdutórias de programação. Nunca estudei muito a fundo esse editor que é capaz de realizar incontáveis tipos de tarefas. Mas aprendi algumas funcionalidades interessantes.

Sátira do xkcd com diversos editores (clique para ampliar)

Editando LaTeX

O auctex é uma extensão que facilita a edição de documentos LaTeX. Para quem usa ubuntu, basta instalar o pacote auctex.

Depois de instalar essa extensão, ao abrir arquivos .tex uma nova interface aparecerá, parecida com a imagem abaixo:

O botão do leão é equivalente a executar o comando latex no arquivo atual. Ele gera um arquivo no formato .dvi. O botão do óculos abre o arquivo .dvi com o programa xdvi. O botão do livrinho executa o comando bibtex sobre o arquivo .aux gerado com o comando latex. O último botão é um preview dentro do próprio emacs, mas eu particularmente não gosto.

Nota: o padrão do auctex é usar o comando latex que gera arquivos dvi. É possível mudar esse padrão para sempre gerar pdf’s usando o comando pdflatex. Para isso, adicione a seguinte linha ao arquivo de configuração do emacs, em ~/.emacs:

(add-hook 'LaTeX-mode-hook 'TeX-PDF-mode)

Observe ainda que existem diferenças entre os comandos latex e pdflatex. Uma delas é que latex só trabalha com imagens no formato .eps enquanto pdflatex trabalha com vários tipos de formatos (pdf, jpg, png), mas não .eps.

Correções ortográficas

O emacs tem uma funcionalidade de indicar erros ortográficos, chamada flyspell. O dicionário padrão é o inglês, mas é possível alterar para português brasileiro. Antes de mais nada, é preciso instalar suporte do aspell a essa linguagem. No ubuntu, basta instalar o pacote aspell-pt-br.

Depois, para trocar o dicionário, abra a linha de comandos no emacs (Alt+X) e digite o seguinte comando:

ispell-change-dictionary

Ele vai pedir o argumento, o qual deve ser:

portugues

Agora, se o seu texto já está escrito e você quer fazer a revisão ortográfica, abra a linha de comandos e digite:

flyspell-buffer

Se você quer começar a fazer a revisão ortográfica conforme vai escrevendo, use

flyspell-mode

As palavras que não estiverem de acordo com o dicionário ficarão destacadas. Clicando com o botão do meio do mouse aparece um menu com sugestões de correção ou opções de ignorar ou adicionar ao dicionário. Para ir para o próximo erro, é possível usar o comando flyspell-goto-next-error ou o atalho

Ctrl+,