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 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.
Ao ser invocado, o comando make procura no diretório atual por um dos três arquivos abaixo na seguinte sequência:
- GNUmakefile
- makefile
- Makefile
Ao construir seu arquivo make é recomendado o uso do nome Makefile porque:
- A própria documentação do GNU diz que deve-se evitar o uso do nome GNUmakefile pois este só deve ser empregado quando usamos os recursos suportados pelo comando make do GNU. Quando usamos os recursos do comando make UNIX padrão devemos sempre usar o nome Makefile.
- Se, no futuro, você quiser testar alguma modificação no Makefile sem mexer no original você pode copiá-lo com o nome de makefile , fazer as alterações necessárias e executar pois makefile é escolhido primeiro pelo comando make.
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.
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:
- all - utilizado como o primeiro nome de alvo do arquivo e, portanto, o nome de alvo padrão. A utilização deste alvo define o projeto inteiro.
- install -utilizado para instalar um projeto.
- clean - utilizado para remoção dos arquivo objeto e arquivos temporários criados no processo de instalação.
- clobber - utilizado para remover os alvos que são construídos (normalmente executáveis e bibliotecas). Você remove estes alvos para reconstruir o projeto do zero.
- distclean - usado para remover tudo, exceto os arquivos originais que foram distribuídos pela internet. Este alvo vai além do clobber pois remove também os arquivos de configuração.
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:
- install depende de all
- clobber depende de clean
- distclean depende de clobber
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:
- .c - arquivo fonte em linguagem C
- .h - arquivo de cabeçalho em linguagem C
- .cc - arquivo fonte em linguagem C++
- .o - arquivo objeto compilado
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:
- 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. - 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.
- 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. - 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.