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: , , , ,