C e o Makefile

Introdução

Este tutorial tem o propósito de ajudar os programadores de C que se estão a iniciar agora a aprender a trabalhar com Makefiles. Não pretendo que isto seja nenhuma bíblia de Makefiles, é apenas para facilitar a vida a que se depara com árduas tarefas de compilação e recompilação de programas e desconhece esta ferramenta.

Por isso não vou, aprofundar muito a questão, fica apenas o essencial… depois é explorar.

Vamos então começar

O Make é um programa de computador que tem o intuito de automatizar a compilação de programas que usam diversos ficheiros. As instruções que o Make executa estão todas dentro de um ficheiro chamado Makefile (ou makefile).

Esta é uma ferramenta (na minha opinião) indispensável para qualquer programa que se faça, pois a (re)compilação é das partes mais chatas do desenvolvimento de um programa.

Um pouco de história: (Wikipedia)

Make was originally created by Dr. Stuart I. Feldman in 1977. Dr. Feldman was working at Bell Labs at the time. Since it is old, many derived tools have appeared that work better.
Among these are BSD make, GNU make and A-A-P. In 2003 Dr. Feldman received the ACM Software System Award for the invention of this important tool.

Modo de Funcionamento

O programa Make é a Makefile na directoria em que se está a trabalhar (por defeito, se não for passado nenhum nome ao comando Make, este vai procurar um ficheiro chamado makefile, e caso não exista vai procurar Makefile (tudo isto na directoria em que se está a trabalhar). É de referir que estes ficheiros podem estar ocultos.

O Make apenas compila/recompila ficheiros que precisam de ser (re)compilados, por exemplo, ficheiros que não foram modificados desde a última compilação não serão recompilados. O que torna o processo de (re)compilação muito mais simples e rápido, visto que muitas vezes existem programas que são formados por muitas dezenas de ficheiros.

Exemplo de uma Makefile

foo.o: foo.c foo.h
	gcc -o foo.o foo.c

O que se passa aqui?

Bem, nesta Makefile o que se passa é o seguinte:

  • (1ª linha) Se os ficheiros foo.c ou foo.h forem mais recentes que o ficheiro foo.o, então…
  • (2ª linha) o ficheiro foo.o é recompilado usando o comando gcc -o foo.o foo.c. Neste exemplo diz-se que o ficheiro foo.o depende dos ficheiros foo.c e foo.h.

Há pontos importantes a serem respeitados na sintaxe das Makefiles. Na 2ª linha do exemplo o primeiro caracter é um TAB, isto é essencial. É o que precede um comando a realizar.

Na 1ª linha de exemplo está o ficheiro de saída (chamemos-lo assim), depois : seguido das suas dependências. Na linha seguinte o comando a realizar caso seja necessário (questão dos ficheiros terem sido ou não modificados).

Comentários em Makefiles

Não sei se parece esquisito comentários em Makefiles, mas podem crer que por vezes dá muito jeito. Os comentários em Makefile são feitos precedendo o texto do comentário por um caracter #, isto em cada linha individualmente.

# teste
foo.o: foo.c foo.h
	gcc -o foo.o foo.c

Expressões Condicionais

Por vezes é útil a compilação condicional de alguns targets, para isso são usadas expressões condicionais… if, else.

libs_for_gcc = -lgnu
normal_llibs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif

Nesta Makefile é testado se o compilador é o gcc, em caso afirmativo são usadas libraries específicas deste, caso contrário são usadas outras. Estas libraries são indicadas através de variáveis.

As directivas condicionais usadas são:

O ifeq que inicia a condição e especifica-a. Contém dois argumentos os quais são comparados para verificar se são iguais. Nesta directiva é possível o uso de variáveis definidas à priori. Se os dois argumentos forem iguais então são executadas as linhas abaixo desta directiva, caso contrário, são ignoradas.

O else faz com que sejam executadas as linhas a seguir a este, caso a condição não se tenha verificado. Verifica-se então que esta directiva é opcional.

O endif termina a condição, todas as condições têm de terminar com o endif.