Idioma: 日本語 | Español | Français | Português | 中文 | English
Anterior | Próximo | Índice | Original em inglês (gnu.org)

2 Introdução aos Makefiles

Para dizer ao make o que você quer que ele faça, é preciso ter um arquivo chamado makefile. Na maioria das vezes, o makefile cumpre o papel de dizer ao make como compilar e ligar um programa.

Neste capítulo, usaremos como exemplo um makefile simples que descreve como compilar e ligar um editor de texto composto por 8 arquivos fonte em C e 3 arquivos de cabeçalho. No makefile também é possível registrar outros comandos que só são executados quando explicitamente solicitados (por exemplo, tarefas como remover certos arquivos a título de limpeza). Se você quiser ver um exemplo de makefile mais complexo, consulte Exemplo de Makefile Complexo.

Quando o make recompila esse editor, cada arquivo fonte em C que foi alterado precisa ser recompilado. Se um arquivo de cabeçalho foi alterado, por segurança é preciso recompilar todos os arquivos fonte em C que fazem #include desse cabeçalho. A cada compilação, é gerado um arquivo objeto a partir do arquivo fonte correspondente. E, por fim, se ao menos um dos arquivos fonte foi recompilado, todos os arquivos objeto — tanto os recém-criados quanto os que restaram de compilações anteriores — precisam ser ligados entre si para produzir o novo editor executável.

2.1 Como é uma regra

Um makefile simples é um conjunto de "regras" com a seguinte forma:

target … : prerequisitesrecipe
        …
        …

Um alvo (target) é, em geral, o nome de um arquivo gerado por um programa. Exemplos de alvos são arquivos executáveis ou arquivos objeto. Um alvo também pode ser o nome de uma ação a executar, como clean (consulte Alvos falsos (Phony)).

Um pré-requisito (prerequisite, antigo nome: dependência) é um arquivo usado como entrada para criar o alvo. É comum um único alvo depender de vários arquivos.

Uma receita (recipe, o conteúdo a executar de uma regra) é uma ação que o make realiza. Uma receita pode conter vários comandos, dispostos na mesma linha ou cada um em sua própria linha. [Importante] Cada linha de uma receita deve sempre ter um caractere de tabulação no início da linha! Isso é pouco evidente e é uma armadilha para os desavisados. Se você preferir iniciar as receitas com um caractere diferente da tabulação, pode definir outro caractere na variável .RECIPEPREFIX (consulte Outras variáveis especiais).

Normalmente, uma receita está dentro de uma regra que tem pré-requisitos e serve para recriar o arquivo alvo quando algum dos pré-requisitos muda. No entanto, a regra que especifica a receita de um alvo não precisa necessariamente ter pré-requisitos. Por exemplo, a regra que contém o comando de exclusão associado ao alvo clean não tem pré-requisitos.

Ou seja, uma regra explica como e quando recriar certos arquivos que são os alvos daquela regra. O make executa a receita sobre os pré-requisitos para criar ou atualizar o alvo. Uma regra também pode explicar como e quando executar uma ação. Consulte Como escrever regras.

Um makefile pode conter outros textos além de regras, mas, para um makefile simples, regras bastam. Regras reais às vezes parecem um pouco mais complicadas do que este modelo, mas todas se encaixam mais ou menos nesse padrão.

2.2 Um Makefile simples

Aqui está um makefile que descreve, de forma direta, a relação em que um arquivo executável chamado edit depende de 8 arquivos objeto, os quais, por sua vez, dependem de 8 arquivos fonte em C e 3 arquivos de cabeçalho.

Neste exemplo, todos os arquivos C fazem #include de defs.h, mas só fazem #include de command.h os arquivos que definem os comandos de edição, e só fazem #include de buffer.h os arquivos de baixo nível que alteram o buffer do editor.

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

Aqui dividimos cada linha longa em duas com uma barra invertida e uma quebra de linha. Isso equivale a escrever uma única linha longa, mas fica mais legível dessa forma. Consulte Dividindo linhas longas.

Para usar este makefile e criar o arquivo executável chamado edit, digite:

make

Para usar este makefile e remover do diretório o arquivo executável e todos os arquivos objeto, digite:

make clean

No makefile deste exemplo, os alvos incluem o arquivo executável edit e os arquivos objeto main.o, kbd.o etc. Os pré-requisitos são arquivos como main.c e defs.h. Na verdade, cada arquivo .o é, ao mesmo tempo, um alvo e um pré-requisito. As receitas incluem cc -c main.c e cc -c kbd.c.

Quando o alvo é um arquivo, ele precisa ser recompilado ou religado se algum de seus pré-requisitos mudar. Além disso, os pré-requisitos que são, eles próprios, gerados automaticamente precisam ser atualizados antes. Neste exemplo, edit depende de cada um dos 8 arquivos objeto, e o arquivo objeto main.o depende do arquivo fonte main.c e do arquivo de cabeçalho defs.h.

Depois de cada linha que contém um alvo e seus pré-requisitos, pode vir uma receita. Essas receitas indicam como atualizar o arquivo alvo. Para distinguir as receitas das demais linhas do makefile, cada linha de receita deve ter no início um caractere de tabulação (ou o caractere especificado pela variável .RECIPEPREFIX; consulte Outras variáveis especiais). (Tenha em mente que o make não sabe nada sobre como as receitas funcionam. É responsabilidade sua fornecer receitas que atualizem corretamente o arquivo alvo. Tudo o que o make faz é executar a receita que você especificou quando o arquivo alvo precisa ser atualizado.)

O alvo clean não é um arquivo, mas apenas o nome de uma ação. Como você normalmente não quer executar a ação dessa regra, clean não é pré-requisito de nenhuma outra regra. Por isso, o make nunca processa essa regra a menos que você o instrua explicitamente. Observe que essa regra não só não é pré-requisito de outra como também não tem nenhum pré-requisito; ou seja, o único propósito dela é executar a receita especificada. Alvos que não se referem a arquivos, mas são apenas ações, são chamados de alvos falsos (phony). Para informações sobre esse tipo de alvo, consulte Alvos falsos (Phony); e para saber como fazer o make ignorar erros gerados por rm ou outros comandos, consulte Erros em receitas.

2.3 Como o make processa um Makefile

Por padrão, o make começa pelo primeiro alvo (exceto alvos cujos nomes começam com ., a menos que também contenham um ou mais /). Isso é chamado de meta padrão (default goal). (Uma meta é o alvo que o make se empenha em atualizar em última instância.) Esse comportamento pode ser alterado pela linha de comando (consulte Argumentos para especificar as metas) ou pela variável especial .DEFAULT_GOAL (consulte Outras variáveis especiais).

No exemplo simples da seção anterior, a meta padrão é atualizar o programa executável edit. Foi justamente por isso que colocamos aquela regra no início.

Assim, quando você fornece o seguinte comando:

make

o make lê o makefile do diretório atual e começa a processar a partir da primeira regra. Neste exemplo, a primeira regra é a que religa edit. Mas, antes de o make processar completamente essa regra, ele precisa processar primeiro as regras dos arquivos de que edit depende — neste caso, os arquivos objeto. Cada um desses arquivos é processado conforme a sua própria regra. Essas regras determinam que cada arquivo .o seja atualizado pela compilação do seu arquivo fonte. A recompilação é feita quando o arquivo fonte, ou algum dos arquivos de cabeçalho listados como pré-requisitos, é mais recente que o arquivo objeto, ou quando o arquivo objeto não existe.

As demais regras são processadas porque seus alvos aparecem como pré-requisitos da meta. Se alguma regra não for dependência da meta (ou daquilo de que a meta depende, e assim por diante…), essa regra não é processada — a menos que você o instrua explicitamente, com um comando como make clean.

Antes de recompilar um arquivo objeto, o make também considera atualizar os seus pré-requisitos, os arquivos fonte e os arquivos de cabeçalho. Este makefile não especifica nada a fazer com eles — os arquivos .c e .h não são alvos de nenhuma regra —, então o make não faz nada com esses arquivos. Mas, no caso de programas em C gerados automaticamente, como os produzidos por Bison ou Yacc, o make os atualizaria nesta etapa conforme as suas próprias regras.

Depois de recompilar os arquivos objeto que precisavam disso, o make decide se deve religar edit. A religação é feita quando o arquivo edit não existe ou quando algum dos arquivos objeto é mais recente que ele. Se algum arquivo objeto acabou de ser recompilado, ele agora é mais recente que edit, de modo que edit é religado.

Assim, se você alterar o arquivo insert.c e executar o make, o make compilará esse arquivo para atualizar insert.o e, em seguida, ligará edit. Se você alterar o arquivo command.h e executar o make, o make recompilará os arquivos objeto kbd.o, command.o e files.o e, em seguida, ligará o arquivo edit.

2.4 Variáveis deixam o Makefile mais enxuto

No exemplo anterior, tivemos que listar todos os arquivos objeto duas vezes na regra de edit (reproduzida aqui):

edit : main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

Esse tipo de duplicação é uma fonte de erros. Ao acrescentar um novo arquivo objeto ao sistema, você pode incluí-lo em uma das listas e esquecer de incluí-lo na outra. Com uma variável, é possível eliminar esse risco e deixar o makefile mais enxuto. Uma variável permite definir uma cadeia uma única vez e inseri-la em vários lugares depois (consulte Como usar variáveis).

É prática padrão todo makefile ter uma variável chamada objects, OBJECTS, objs, OBJS, obj ou OBJ que reúna os nomes de todos os arquivos objeto. Uma variável objects assim seria definida escrevendo no makefile uma linha como esta:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

Então, em cada lugar onde você quiser colocar a lista de nomes dos arquivos objeto, basta escrever $(objects) para inserir o valor dessa variável (consulte Como usar variáveis).

Usando uma variável para os arquivos objeto, o makefile simples anterior fica assim por inteiro:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit $(objects)

2.5 Deixar o make deduzir as receitas

Não é necessário escrever, uma a uma, as receitas para compilar cada arquivo fonte em C, pois o make consegue deduzi-las por conta própria. O make tem uma regra implícita para atualizar um arquivo .o a partir de um arquivo .c de nome correspondente usando o comando cc -c. Por exemplo, o make usa a receita cc -c main.c -o main.o para compilar main.c em main.o. Portanto, podemos omitir as receitas das regras dos arquivos objeto. Consulte Como usar regras implícitas.

Quando um arquivo .c é usado automaticamente dessa forma, esse arquivo .c também é acrescentado automaticamente à lista de pré-requisitos. Por isso, se omitirmos a receita, podemos também omitir o arquivo .c dos pré-requisitos.

Aplicando as duas alterações acima e usando ainda a variável objects proposta antes, o exemplo fica assim por inteiro:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
        rm edit $(objects)

Na prática real, é assim que escrevemos o makefile. (As complicações envolvendo clean são explicadas em outro ponto. Consulte Alvos falsos (Phony) e Erros em receitas.)

As regras implícitas são tão convenientes que constituem um recurso importante. Você verá que são usadas com frequência daqui em diante.

2.6 Outro estilo de Makefile

Quando os objetos de um makefile são criados apenas por regras implícitas, é possível adotar outro estilo de escrita. Nesse estilo, as entradas são agrupadas por pré-requisito, em vez de por alvo. Escrito de fato, fica assim:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

Aqui, defs.h é dado como pré-requisito de todos os arquivos objeto, e command.h e buffer.h são pré-requisitos dos arquivos objeto específicos listados para cada um.

Se isso é melhor ou não é uma questão de gosto: é mais conciso, mas algumas pessoas não gostam desse estilo, por acharem mais claro reunir num único lugar todas as informações sobre cada alvo.

2.7 Regras para limpar o diretório

Compilar um programa não é a única situação em que você pode querer escrever regras. Nos makefiles, é comum descrever, além de como compilar um programa, como realizar algumas outras tarefas. Por exemplo, como remover todos os arquivos objeto e o arquivo executável para deixar o diretório clean (em estado limpo).

A regra do make para limpar o nosso editor de exemplo poderia ser escrita assim:

clean:
        rm edit $(objects)

Na prática, você vai querer escrevê-la de forma um pouco mais elaborada, para dar conta de situações inesperadas. Nesse caso, faça assim:

.PHONY : clean
clean :
        -rm edit $(objects)

Escrevendo assim, evita-se que o make se confunda caso exista um arquivo de verdade chamado clean, e o processamento continua mesmo que rm gere um erro. (Consulte Alvos falsos (Phony) e Erros em receitas.)

Uma regra como essa não deve ser colocada no início do makefile, pois não queremos que ela seja executada por padrão! Por isso, no makefile de exemplo, queremos que a regra de edit, que recompila o editor, continue sendo a meta padrão.

Como clean não é pré-requisito de edit, executar o comando make sem argumentos não dispara essa regra de modo algum. Para executá-la, é preciso digitar make clean. Consulte Como executar o make.


Anterior | Próximo | Índice | Original em inglês (gnu.org)