Cmake

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

Os comentários estão fechados.

%d bloggers like this: