Usando o utilitário make

Samuel Dias Neto - sdiasneto@yahoo.com.br

 

O que é o utilitário make
Arquivos make padrão
O conceito de alvos
Executando o Makefile
Alvos frequentemente utilizados pela comunidade
Dependências
Utilizando macros
Utilizando regras de inferência

 

O que é o utilitário make

O utilitário make é uma poderosa ferramenta usada para compilar grandes projetos. Em vez de você mesmo compilar e linkar todos os arquivos, o make pode fazer isto para você com a vantagem de compilar somente o que for necessário. Isto é muito útil no caso de pequenas modificações em um grande projeto. Você pode introduzir os comandos de compilação num arquivo make , assim não há necessidade de decorar as opções de comando que você utilizou para compilar um projeto.

Veremos aqui a utilização básica do utilitário make . Para aprofundar-se dê uma olhada na page do GNU. Na seção de documentação você encontrará um manual completo do make , só que em inglês.

 

Arquivos make padrão

Ao ser invocado, o comando make procura no diretório atual por um dos três arquivos abaixo na seguinte sequência:

  1. GNUmakefile
  2. makefile
  3. Makefile

Ao construir seu arquivo make é recomendado o uso do nome Makefile porque:

 

Alvos

O comando make trabalha com o conceito de alvos. Nosso Makefile deve ter pelo menos um alvo. Crie um Makefile vazio e execute o comando make e você terá uma sessão parecida com esta:

[nerd@aranha teste]$ ls
Makefile
[nerd@aranha teste]$ make
make: *** No targets.  Stop.
[nerd@aranha teste]$

Observe que o comando foi encerrado dizendo que não haviam alvos. Agora vamos editar o Makefile e introduzir alvos:

# Estudando o comando make

alvo1:
        @echo "Exibindo o alvo 1"

alvo2:
        @echo "Exibindo o alvo 2"

alvo3:
        @echo "Exibindo o alvo 3"

As linhas iniciadas com # são linhas de comentários. Um nome de alvo deve sempre iniciar no começo da linha e ser seguido por dois pontos.

 

Executando o Makefile

Para excutar o Makefile invocando um alvo basta entrar com o comando make seguido do nome do alvo na linha de comando. Observe:

[nerd@aranha teste]$ make alvo2
Exibindo o alvo 2
[nerd@aranha teste]$ make alvo3
Exibindo o alvo 3
[nerd@aranha teste]$ make
Exibindo o alvo 1
[nerd@aranha teste]$

Veja que quando foi invocado sem alvo, o comando make assumiu o primeiro alvo do arquivo como o alvo padrão.

Você pode também invocar vários alvos no mesmo comando:

[nerd@aranha teste]$ make alvo2 alvo3 alvo1
Exibindo o alvo 2
Exibindo o alvo 3
Exibindo o alvo 1
[nerd@aranha teste]$

 

Alvos frequentemente utilizados pela comunidade

Existem alguns nomes de alvo que são frequentemente utilizados por todos os programadores. Abaixo segue alguns deles:

 

Dependências

O comando make também tem um conceito de dependência. Quando um alvo A depende de um alvo B, o alvo B será executado primeiro. Para o uso dos alvos frequentemente utilizados que estão descritos acima esteja ciente que:

Vamos introduzir dependências no nosso exemplo de estudo:

# Estudando o comando make

alvo1:
        @echo "Exibindo o alvo 1"

alvo2: alvo1 alvo3
        @echo "Exibindo o alvo 2"

alvo3: alvo2
        @echo "Exibindo o alvo 3"

Agora vamos executar o Makefile invocando alvo2 e ver como funcionam as dependências:

[nerd@aranha teste]$ make alvo2
Exibindo o alvo 1
Exibindo o alvo 3
Exibindo o alvo 2
[nerd@aranha teste]$

Vemos que as dependências devem vir após o nome do alvo e devem estar separadas por um espaço. A primeira dependência também deve estar separada dos dois pontos por um espaço.

 

Macros

O arquivo make também pode utilizar-se de macros para aumentar sua funcionalidade. Observe como abaixo:

# Estudando o comando make

# definindo uma macro
ARQUIVO = teste.c

#usando um nome de alvo frequentemente utilizado para
#definir todo o projeto
all: alvo1 alvo2 alvo3

alvo1:
        @echo "compilando "$(ARQUIVO)

alvo2:
        @echo "linkando "$(ARQUIVO)

alvo3:
        @echo "removendo "$(ARQUIVO)

Nesta implementação do nosso arquivo de estudo introduzimos um nome de alvo normalmente utilizado ( all ) para definir todo o projeto. Veja como seria a execução deste Makefile:

[nerd@aranha teste]$ make
compilando teste.c
linkando teste.c
removendo teste.c
[nerd@aranha teste]$

Como mostrado no novo código do nosso Makefile de estudo, onde quisermos que o nome da macro seja substituído devemos colocar a expressão:

$(NOME_DA_MACRO)

Você ainda pode alterar o valor da macro quando invocando o Makefile. Observe a sessão abaixo:

[nerd@aranha teste]$ make
compilando teste.c
linkando teste.c
removendo teste.c
[nerd@aranha teste]$ make ARQUIVO=main.c
compilando main.c
linkando main.c
removendo main.c
[nerd@aranha teste]$

Veja que usamos o mesmo Makefile para processar outro arquivo através da definição de outro valor para a macro ARQUIVO na linha de comando.

 

Regras de inferência

As regras de inferência são outro grande recurso do comando make . Para utilizá-las o make reconhece os seguintes tipos de arquivos dentre outros:

Abaixo segue um exemplo da definição de uma regra de inferência:

CC = gcc
STD = _GNU_SOURCE

.c.o:
	$(CC) -c -Wall $(CFLAGS) -D$(STD) $< -o $@

Vamos dissecar o trecho de código acima:

  1. As duas primeiras linhas
    CC = gcc
    STD = _GNU_SOURCE
    
    utilizam macros para definir o compilador e o padrão de compilação. Observe que estas macros serão substituídas na linha de ação do alvo .c.o montando, assim, o comando correto para a compilação do arquivo.


  2. O alvo .c.o é um tipo de alvo especial que define a regra de inferência. Ele é composto do sufixo do arquivo inicial ( .c ) seguido do sufixo do arquivo alvo ( .o ). Com esta declaração de alvo o comando make entende que se você tem um alvo que é um arquivo objeto ( .o ), existe um arquivo fonte ( .c ) de entrada e este é mais recente, ele deve excutar os comandos que seguem. É importante observar que numa definição de regra de inferência não deve haver dependência.


  3. O comando para o alvo .c.o é:
    	$(CC) -c -Wall $(CFLAGS) -D$(STD) $< -o $@
    
    O comando deve iniciar com um caractere de tabulação. Para que o comando não seja exibido na saída padrão preceda-o com o caracter @ . Veja que este comando usa amplamente macros. As macros CC e STD já foram vistas acima.

    A macro CFLAGS normalmente é deixada em aberto para lhe dar flexibilidade. Ela pode ser usada para armazenar quaisquer outras opções de compilação não usadas constantemente, como a opção -g para depuração. Isto é feito pela passagem do valor correspondente a CFLAGS na própria linha de comando quando invocando make , como foi mostrado acima na seção macros.


  4. As macros $< e $@ são macros pré-definidas pelo comando make . Juntamente com $* , estas são as macros pré-definidas mais utilizadas e significam:
    $*	o arquivo alvo sem o sufixo
    
    $@	o arquivo alvo
    
    $<	o arquivo dependente. No caso do alvo ser um arquivo objeto( .o ),
            o dependente será um arquivo fonte em linguagem C ( .c )
    

Para um melhor entendimento da utilização de regras de inferência vamos a um exemplo:

Crie o arquivo fonte sam.c com o código abaixo:

#include <stdio.h>

int main()
  {
    printf("testando 1,2,3\n");

    return(0);
  } 

Agora crie o Makefile com o seguinte código:

# Exemplo da utilização de regras de inferência

CC = gcc

.c.o:
        $(CC) -c $< -o $ $@

sam.o:

Observe que o Makefile tem o alvo sam.o . Utilizando as definições da regra de inferência o make procura o arquivo sam.c . Se não encontrar, exibirá uma mensagem de erro, provavelmente parecida com:

[nerd@aranha teste]$ make
make: Nothing to be done for `sam.o'.
[nerd@aranha teste]$

Caso encontre, o alvo será criado:

[nerd@aranha teste]$ ls
Makefile  sam.c
[nerd@aranha teste]$ make
gcc -c sam.c -o sam.o
[nerd@aranha teste]$ ls
Makefile  sam.c  sam.o
[nerd@aranha teste]$

Observe os valores substituídos para as macros $< e $@ .

Aqui encerramos nossa pequena tour pelo comando make , lembrando que vimos apenas o "básico do básico" desta poderosa ferramenta.

Qualquer bizú a mais não esquece de me enviar.

E como dizia o meu querido professor de física Guido, lá no CMR, toda vez que terminava a aula:

" ..... estudem!" (aí jogava o giz na canaleta do quadro negro e saia da sala).