terça-feira, 21 de agosto de 2007

Mais sobre GNU Autotools (parte IV)

Continuando a superficial explanação acerca dos detalhes de definição de um arquivo configure.ac, que definirá como exatamente o build do nosso sistema em questão será gerado, e por quais bibliotecas precisará gerar dependências, veremos mais alguns exemplos de chamada de macros M4 que são de uso mais frequente.

As definições de arquivos configure.ac começam com chamadas de configuração ao autoconf. Para diferenciá-las em relação às macros comuns, remetesse à notação das macros, que assumem o prefixo AC. Por exemplo, a macro AC_CHECK_FUNCS realiza uma busca pelos nomes de funções passadas como parâmetro (listas de parâmetro em M4 são representadas não por vírgulas, mas por sequências de nomes de argumentos de função, separados por espaço e tendo por limites os símbolos especiais '[' e ']'). Após serem encontrados as origens dos nomes de função, o respectivo arquivo de biblioteca e adicionado como parâmetro de compilação dos fontes do sistema. Na chamada abaixo, as funções especificadas são verificadas e seus respectivos headers e binários de bibliotecas são referenciados para serem usados depois, através de variáveis execíficas, como CFLAGS e LDFLAGS:


AC_CHECK_FUNCS([memset socket stime strstr strtoul gethostname inet_ntoa localtime_r select strrchr localtime strptime])


Essa chamada em M4 é especialmente genérica, e pode ser usada de forma incondicional para todo nome de função que se suspeite estar presente no ambiente atual de desenvolvimento. Mas tem-se definidas outras chamadas mais particulares, que buscam por chamadas bem específicas de funções:


# Checks for library functions.
AC_FUNC_FORK
AC_FUNC_STRFTIME
AC_FUNC_MALLOC
AC_FUNC_MKTIME
AC_FUNC_VPRINTF


Todas as funções acima (fork, strftime, malloc, mktime, vprintf) são buscadas no ambiente atual, e adicionadas por padrão aos campos CFLAGS e LDFLAGS (quando necessário).

Pode-se definir parâmetros que são passados ao autoconf para que ele instrua de forma adequada a geração dos Makefiles. No código abaixo usa-se a macro AC_ARG_ENABLE para verificar se o parâmetro de linha de comando --enable-coverage foi aplicado, no caso do desenvolvedor necessitar da funcionalidade de compilação usando os recursos de teste de cobertura (coverage):


AC_ARG_ENABLE(coverage,
AS_HELP_STRING([--enable-coverage],
[enable coverage (default = no)]),
[
if test $enableval = no; then
USE_COVERAGE=no;
else
USE_COVERAGE=yes;
fi
],[
USE_COVERAGE=no;
]
)

if [ test $USE_COVERAGE = yes ]; then
CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage -O0"
LDFLAGS="$LDFLAGS -lgcov"
fi


Caso o parâmetro --enable-coverage seja passado, a variável $USE_COVERAGE será definida com valor "yes", e serão adicionados os parâmetros necessários como atributos de compilação do gcc.

A chamada abaixo sinaliza ao utilitário automake que seja executado. Ele processará todos os arquivos texto com terminação ".am", e criará a partir deles os arquivos Makefile apropriados:


AM_INIT_AUTOMAKE($PACKAGE, $VERSION)


Tendo o arquivo configure.ac definido corretamente, além dos respectivos Makefile.am (que definirão os processos de compilação e geração das builds, rodando em cima do utilitário make), a execução de autogen.sh (um script de uso bastante difundido, apesar de não vir em distibuição padrão das principais GNU autotools - quase todo projeto autoconf usa um modelo similar de autogen.sh, que simplesmente irá executar os comandos autoconf, automake, libtool, etc. numa ordem pré-estabelecida e funcional).


# ./autogen.sh


O qual terminada a execução, pode vir acompanhado da chamada ao make para que o sistema de software seja compilado e instalado:


# make all install

Marcadores: , , , , ,

quinta-feira, 16 de agosto de 2007

GNU Autotools (parte III) - Configure.ac

Chegamos à etapa mais interessante dessa rápida introdução aos recursos de deployment e geração de builds com autotools. Iniciaremos essa exposição detalhando a estrutura do arquivo de configuração configure.ac e as macros M4 úteis na sua composição. Relembrando, o configure.ac é um arquivo útil para se configurar as variáveis de ambiente, onde quer que o aplicativo em desenvovimento precisar ser empacotado. Dentro do configure.ac, declararemos coisas como o nome da distro, números de versão (minor e major versions), as bibliotecas que precisam se verificadas quanto da sua existência, passagem de parâmetros de compilação para os fontes, opções de debug e teste automatizado, etc. Abaixo temos um trecho útil do configure.ac:


# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.50)

AC_INIT([gmyth],[0.3])

AC_CONFIG_SRCDIR([src/gmyth.h])
AC_CONFIG_HEADER(config.h)

dnl when going to/from release please set the nano (fourth number) right !
dnl releases only do Wall, SVN and prerelease does Werror too
AS_VERSION(gmyth, GMYTH, 0, 3, 0, 0, GMYTH_SVN="no", GMYTH_SVN="yes")
GMYTH_MAJORMINOR=$GMYTH_MAJOR_VERSION.$GMYTH_MINOR_VERSION
AC_SUBST(GMYTH_MAJORMINOR)

(...)



Logo no início do arquivo configure.ac, temos uma chamada à macro M4 AC_PREREQ, que irá definir como obrigatório o uso da versão 2.50 da ferramenta autoconf sobre esse arquivo específico de distribuição. Caso o desenvolvedor que baixe a versão da sua biblioteca no SVN da sua empresa, por exemplo, tentar gerar os builds automáticos e os arquivos Makefile, usando uma versão anterior a 2.50 do autoconf, o procedimento de build terminará com uma mensagem de erro ao usuário, informando a necessidade de uso de uma versão mais atual. A macro AC_INIT atribui um nome para a distro, além de uma versão inicial: isso será de extrema valia quando sua equipe de desenvolvimento estiver gerenciando o lançamento de novas releases do sistema, porque a versão presente nessa tag marcará a forma como funcionará o esquema de versionamento para todos os builds subsequentes. Logo abaixo dessa chamada, temos a AS_VERSION, que detalha a versão do build atual em seções de MAJOR e MINOR version, o que aumenta a precisão com que as releases do seu sistema de software em fase de deployment será lançado, o que costuma tornar mais fácil a comunicação com outras equipes importantes no processo de software, porque cria uma linguagem comum, onde uma equipe de teste, por exemplo, poderá discernir, através da numeração de 4 partes que compõe a versão, se o build representa uma nightly-build, ou um release M1 para usuário final, por exemplo.

A macro AC_SUBST é bastante comum, e representa num arquivo configure.ac a operação de declaração de variáveis, que poderão ser partilhadas entre inúmeros arquivos Makefile.am. Como, a priori, podemos supor que exista um arquivo Makefile.am para cada módulo que compôe o sistema, a importância dessa funcionalidade atribuída por AC_SUBST fica clara, pois aí teremos uma forma de espalhar entre os módulos dados que são de necessidade comum a todos eles (como nesse caso, os números de versão MINOR e MAJOR, que poderão ser usadas por diretivas de compilação em código, denotando compilação condicional de determinados trechos, baseados em versão de build).

Veremos depois mais macros M4 úteis. Mas a boa notícia, que serve sobremaneira na tarefa de gerar o arquivo configure.ac, e a ferramenta autoscan. Para usá-la, simplesmente rode-a dentro do seu diretório de projeto:


# autoscan


Essa ferramenta tem função dupla: caso você não tenha criado uma versão inicial do configure.ac, a ferramenta autoscan irá gerar automaticamente, baseado em inspeção do código da sua aplicação, um arquivo configure.ac exemplo, chamado configure.scan (basta renomeá-lo depois para configure.ac, caso ele atenda às suas necessidades). E caso contrário, você já tenha criado seu texto plenamente funcional para o configure.ac, mas deseja verificá-lo quanto à correção, e caso você não tenha esquecido de verificar alguma dependência, a autoscan poderá gerar um relatório de advertências, que poderão servir como dicas para que você melhore o seu configure.ac:


configure.ac: warning: missing AC_CHECK_FUNCS([gethostbyname]) wanted by: src/gmyth_vlc.c:227
configure.ac: warning: missing AC_CHECK_HEADERS([arpa/inet.h]) wanted by: gmyth_file_transfer.c:50
configure.ac: warning: missing AC_CHECK_HEADERS([netdb.h]) wanted by: gmyth_file_transfer.c:53
configure.ac: warning: missing AC_CHECK_HEADERS([netinet/in.h]) wanted by: src/gmyth_socket.c:52
configure.ac: warning: missing AC_CHECK_HEADERS([sys/param.h]) wanted by: src/gmyth_socket.c:44
configure.ac: warning: missing AC_CHECK_HEADERS([sys/time.h]) wanted by: src/gmyth_util.c:40
configure.ac: warning: missing AC_FUNC_REALLOC wanted by: src/gmyth_http.c:965
configure.ac: warning: missing AC_PROG_RANLIB wanted by: ltmain.sh:6817
configure.ac: warning: missing AC_TYPE_SIZE_T wanted by: src/gmyth_http.c:960


No log da autoscan acima, ela aconselha que eu faça a verificação de dependências de biblioteca para a minha aplicação, usando a macro AC_CHECK_FUNCS, que é bastante útil, e realiza uma busca nas bibliotecas (.so, .a, shared ou static libraries, etc.) por um determinado nome de função. Caso o nome de função solicitado seja encontrado, a biblioteca que o encapsula será incluída como parâmetro para que o GCC ou outro compilador a inclua como parâmetro de biblioteca.

Mais detalhes sobre a geração dos builds propriamente ditos no próximo post...

Marcadores: , , , , ,

terça-feira, 14 de agosto de 2007

GNU Autotools (parte II) - Utilitários

Na continuação da apresentação dos conceitos básicos de funcionamento das Autotools, conjunto de ferramentas usadas para automatização de geração de builds de software, veremos os arquivos fundamentais presentes numa distribuição que se aproveita da infraestrutura makefile. Observando um projeto típico que usa das autoconfs, alguns arquivos são característicos, e muitos deles são de presença mandatória:


data/
debian/
m4/
samples/
src/
tests/
AUTHORS
COPYING
ChangeLog
INSTALL
Makefile.am
NEWS
README
aminclude.am
autogen.sh
configure.ac
doxygen.cfg
gmyth.pc.in


A estrutura de diretórios acima foi obtida de: http://gmyth.svn.sourceforge.net/viewvc/gmyth/trunk/gmyth/. Os arquivos com nomes em maiúsculas são documentos texto, caracteres no formato ASCII, e são descritivos do projeto, contém metadados com informações importantes, como o nome dos autores do projeto (AUTHORS) e a licença de cópia e distribuição do código (COPYING). Eles são obrigatórios, e em alguns passos de processamento, é possível que ferramentas como autoconf retornem com erro, caso não encontrem tais arquivos, apesar da aparente função de baixa prioridade que seria a de informação sobre releases de software e nomes de autores.

A seguir, por ordem de importância, temos o Makefile.am. Esse arquivo é uma espécie de Makefile, tal como visto no último post, porém em estágio de pré-processamento, ou seja, contém estruturas e macros que ainda devem ser processadas, e utiliza de variáveis definidas na sequência de montagem de build comandada pelo arquivo configure.ac (que por sua vez gera o configure). O trecho seguinte de Makefile.am apresenta algumas estruturas fundamentais:

SUBDIRS= src samples tests

(...)

if HAVE_INDENT
indent:
cd src; \
make indent; \
cd ..
endif

include aminclude.am

EXTRA_DIST = \
autogen.sh \
gmyth.pc.in \
AUTHORS \
COPYING \
README

Esse arquivo Makefile.am será utilizado como entrada no utilitário automake, as macros chamadas a partir dele serão processadas como devido, e o arquivo texto resultante será transformado no arquivo Makefile, com estruturas semelhantes àquelas vistas no post passado, porém bem mais detalhada e complexa, Por sinal, os resultados de processamento das autotools costumam ser de leitura BEM difícil para quem não estiver habituado a Makefiles e programação em Shell Script, mas depurar uma distribuição com autoconf algumas vezes necessita-se inspecionar arquivos Makefile e configure auto-gerados a fim de identificar inconsistência ou erros (como um path de biblioteca inválido, ou variável de ambiente mal definida).

Ao rodar em cima desse arquivo Makefile.am, o automake definirá, na ordem, uma variável SUBDIRS, que listará os subdiretórios do projeto que contém outras definições Makefile.am. A variável SUBDIRS é padrão, e deve sempre ser definida quando se lida com projetos mais complexos, com vários Makefiles, porque é através dele que será determinada a abrangência e a forma como o build vai ser gerado: um diretório faltando pode prejudicar a release final, bem como a inclusão de um módulo em SUBDIRS que não seja possível de ser compilado em determinada arquitetura, pode gerar problemas na execução em alguma etapa de processamento das autotools... Durante a execução, serão processados, respectivamente, arquivos Makefile.am nos subdiretórios src, samples e tests.

Pode-se, dentro de um Makefile.am, determinar a criação condicional de targets, ou adição de qualquer trecho de código, usando variáveis definidas numa etapa anterior do automake, como o uso de variáveis definidas no configure.ac (autoconf). Aqui temos que a variável HAVE_INDENT é verificada com relação ao seu valor verdade e, caso a avaliação seja positiva, o target indent é criado. Essa variável será definida TRUE caso a opção de identação de código tenha sido informada em linha de comando (durante a chamada inicial aos autotools), bem como se a ferramenta indent foi encontrada na distribuição alvo. Abaixo, vemos o trecho de código do configure.ac que cria a variável HAVE_INDENT com o valor apropriado:

# checks if the indent tool can be found
AC_CHECK_PROG(HAVE_INDENT, indent, [Found!])
if test -n "$HAVE_INDENT"; then
AC_DEFINE(HAVE_INDENT, 1, [AutoIndent found!])
fi

AM_CONDITIONAL(HAVE_INDENT, test -n "$HAVE_INDENT")

A macro M4 "AC_CHECK_PROG" funciona da seguinte forma: a variável HAVE_INDENT será declarada caso o aplicativo indent possa ser executado com sucesso em prompt de comando, imprimindo uma mensagem "Found!" em caso afirmativo. A macro AM_CONDITIONAL faz com que a variável HAVE_INDENT seja visível em escopo de arquivos de entrada Automake (Makefile.am), o que a torna passível de ser usada em expressões condicionais, como o trecho de avaliação do if no Makefile.am.

Na sequência, e voltando ao arquivo Makefile.am, temos a definição da macro EXTRA_DIST, que irá informar ao utilitário automake quais os arquivos que devem ser disponibilizados quando da geração do build de distribuição (aquele que fará parte do pacote de distribuição ao usuário do sistema de software). No caso da biblioteca GMyth acima, os arquivos distribuídos são aqueles que incorporam meta-dados de informação ao pacote (AUTHORS, COPYING), além de scripts úteis à geração do build (autogen.sh) e um arquivo de configuração para resolução de dependências (gmyth.pc.in), que serve como arquivo de entrada para o utilitário pkg-config, que procura resolver as dependências de biblioteca no ambiente constante à distribuição. Detalharemos depois o uso dessas outras ferramentas.

No próximo post, detalharei mais a respeito do processamento do configure.ac através do autotools...

Marcadores: , , ,

quinta-feira, 9 de agosto de 2007

Iniciando com GNU Autotools

Muita gente procura em sites para desenvolvedores informações sobre como usar as ferramentas da GNU para gerenciamento de compilação de código-fonte, library linking com os binários, geração de bibliotecas e distribuição de código executável. Essas ferramentas são amplamenta conhecidas como "autotools" (automake, autoconf, libtool, etc.), e são usadas com frequência em projetos de software livre. E encontrar tais informações de uso, de forma simples e resumida, é tarefa que demanda a busca em inúmeros sites, bem como experimentações em linha de comando. Pra piorar, o mecanismo de funcionamento das autotools não é simples. Portanto, qual a real necessidade delas? Definindo da forma mais simples possível, autotools são aplicativos e scripts de linha de comando que facilitam o trabalho do desenvolvedor de aplicativos e bibliotecas no trabalho de gerar e linkar um código binário, por dinamizar a forma como esse produto vai ser disponibilizado em produção. Tal recurso provê meios de, automaticamente, buscar pela versão de uma biblioteca necessária para um determinado módulo do aplicativo, por exemplo. Isso é particularmente importante quando o projeto de software é uma atividade desenvolvida através da participação de membros em ambientes heterogêneos de desenvolvimento, facilitando o trabalho do programador no momento em que ele se vê, por exemplo, com uma dependência recém adicionada de biblioteca.

Vou tentar mostrar aqui como iniciar o uso dos Makefiles. A princípio, não abordarei como se cria um Makefile, porque esse vem sendo continuamente um trabalho menos necessária, uma vez que ferramentas como o automake/autoconf já facilitam muita coisa, através de inúmeras macros de automação pré-definidas em M4, uma espécie de meta-linguagem funcional usada para adicionar recursos de configuração e busca aos Makefiles, tal como a macro M4 chamada AC_CHECK_LIB, que checa no sistema de execução por uma biblioteca especificada, necessária para a distribuição. Me limitarei aqui a detalhar como começar um projeto em autoconf, que abstrai muitas das demandas de projetos complexos em Makefile.

Um arquivo de configuração GNU Make (Makefile) simplesmente especifica regras (tasks, ou tarefas) de compilação e geração de código com bibliotecas. Por exemplo, no arquivo Makefile abaixo:


%.o: %.c
$(CXX) -c $(CFLAGS) $(INCLUDES) $< -o $@


OBJ_FILES = gmu_dragwin.o gmu_dragwin_module.o


TARGET = gmu_dragwin.so


build: $(OBJ_FILES)


$(CXX) $(LDFLAGS) $(INCLUDES) $(LIBSPATH) $(OBJ_FILES) $(LIBS) -o $(TARGET)



all: build


Aí vemos alguns conceitos importantes na definição de Makefiles: o conceito fundamental é o de tasks (tarefas) de manutenção, que são especificadas no texto acima por identificadores alfanuméricos seguidos de dois pontos. Daí temos a definição de 2 tasks básicas: build e all. A task build é simplesmente a que define a geração do produto específico do Makefile, a meta básica decorrente da execução deste. Tanto que é ela que vai especificar como parâmetro último na geração a variável $(TARGET), que está definida mais acima como sendo o nome de arquivo de biblioteca, "gmu_dragwin.so", que é o nome do nosso componente. A task (ou target) "all" é simplesmente uma conveniência do Makefile para que, caso não seja especificada taks alguma em momento de execução do procedimento de make, a taks build será a chamada default. A linha seguinte ao símbolo ":" de definição do target é chamada de pré-requisito, porque a realização dessa task depende da execução sem erros de cada um dos pré-requisitos determinados. Portanto, digitar em linha de comando o comando make, no mesmo nível de diretório em que se encontra o arquivo Makefile acima, irá resultar na geração dos códigos objetos e o arquivo gmu_dragwin.so, que se refere à biblioteca:


# cd drawin_library
# ls
Makefile gmu_dragwin.c gmu_dragwin_module.c
# make
# ls
Makefile gmu_dragwin.c gmu_dragwin_module.c
gmu_dragwin.o gmu_dragwin_module.o gmu_dragwin.so


A chamada a make sem task nenhuma, faz com que a task all seja buscada como a padrão, que por sua vez em nosso Makefile redireciona a tarefa para a build. Na chamada a make, poderia-se ter especificado a task desejada diretamente, tal como em: make build. O resultado seria exatamente o mesmo.

A terceira task no Makefile exemplo do texto acima é mais aprimorada, e admite um critério dinâmico para a necessidade de recompilação dos códigos-objeto dos arquivos fontes em linguagem C. A task %.o diz que toda referência no texto a qualquer pré-requisito, em qualquer target, que tenha um nome definido por uma sequência qualquer de caracteres alfanuméricos, seguido por uma string ".o" (por sinal, uma extensão de arquivo objeto), fará com que seja automaticamente chamada essa task, que por sua vez verificará a efetividade de cada um dos pré-requisitos. O primeiro pré-requisito será encontrar um arquivo de código-fonte com o mesmo nome do target, mas com extensão ".c":

%.o: %.c
$(CXX) -c $(CFLAGS) $(INCLUDES) $< -o $@

No pré-requisito seguinte, será chamado o compilador GCC (cujo caminho no sistema de arquivos é apontado pela variável $(CXX)) com os parâmetros detalhados no target acima como sendo $<, que se refere ao nome do arquivo-fonte (por exemplo, gmu_dragwin.c); e a variável $@ aponta para o valor corrente do nome do target (p.e., gmu_dragwin.o). Esses recursos de substituição são amplamente utilizados, uma vez que permitem que o Makefile defina dinamicamente suas próprias regras, e são chamados de "regras de substituição" (pattern rules).

Uma vez tendo fundamentado os conceitos básicos de Makefile, veremos na sequência os detalhes de como as ferramentas de autotools criam tudo isso de forma mais prática, permitindo a definição de regras mais avançadas de configuração.

Marcadores: , , , ,