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

7 Partes condicionais dos Makefiles

Uma diretiva condicional faz com que parte de um makefile seja executada ou ignorada conforme os valores das variáveis. As condicionais podem comparar o valor de uma variável com o de outra, ou o valor de uma variável com uma cadeia de caracteres constante. As condicionais controlam o que o make realmente "vê" no makefile, de modo que não podem ser usadas para controlar as receitas (recipe) no momento da execução.

7.1 Exemplo de uma condicional

O exemplo de condicional a seguir instrui o make a usar um conjunto de bibliotecas caso a variável CC seja "gcc", e um conjunto diferente de bibliotecas em caso contrário. Isso funciona controlando qual das duas linhas de receita será usada para a regra. O resultado é que "CC=gcc", passado como argumento ao make, altera não apenas qual compilador é usado, mas também quais bibliotecas são vinculadas.

libs_for_gcc = -lgnu
normal_libs =

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

Esta condicional usa três diretivas: ifeq, else e endif.

A diretiva ifeq marca o início da condicional e especifica a condição. Ela recebe dois argumentos, separados por uma vírgula e envolvidos por parênteses. A substituição de variáveis é realizada em ambos os argumentos e, em seguida, eles são comparados. As linhas do makefile que vêm após o ifeq são executadas se os dois argumentos coincidirem; caso contrário, são ignoradas.

A diretiva else faz com que as linhas seguintes sejam executadas se a condição anterior falhou. No exemplo acima, isso significa que o segundo comando de vinculação alternativo é usado sempre que o primeiro não é usado. Incluir um else em uma condicional é opcional.

A diretiva endif encerra a condicional. Toda condicional deve terminar com um endif. Depois dele, segue o texto do makefile não sujeito à condição.

Como este exemplo ilustra, as condicionais atuam no nível do texto: as linhas da condicional são tratadas como parte do makefile, ou ignoradas, de acordo com a condição. É por isso que unidades sintáticas maiores do makefile, como as regras, podem atravessar o início ou o fim da condicional.

Quando a variável CC tem o valor "gcc", o exemplo acima tem este efeito:

foo: $(objects)
        $(CC) -o foo $(objects) $(libs_for_gcc)

Quando a variável CC tem qualquer outro valor, o efeito é este:

foo: $(objects)
        $(CC) -o foo $(objects) $(normal_libs)

Resultados equivalentes podem ser obtidos de outra maneira: tornando condicional a própria atribuição da variável e, depois, usando a variável de forma incondicional:

libs_for_gcc = -lgnu
normal_libs =

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

foo: $(objects)
        $(CC) -o foo $(objects) $(libs)

7.2 Sintaxe das condicionais

A sintaxe de uma condicional simples, sem else, é a seguinte:

conditional-directive
text-if-true
endif

O text-if-true pode ser qualquer conjunto de linhas de texto, a ser considerado parte do makefile se a condição for verdadeira. Se a condição for falsa, nenhum texto é usado em seu lugar.

A sintaxe de uma condicional complexa é a seguinte:

conditional-directive
text-if-true
else
text-if-false
endif

ou:

conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif

Pode haver tantas cláusulas "else conditional-directive" quantas forem necessárias. Assim que uma dada condição se torna verdadeira, o seu text-if-true é usado e nenhuma outra cláusula é usada; se nenhuma condição for verdadeira, então o text-if-false é usado. O text-if-true e o text-if-false podem ter qualquer número de linhas de texto.

A sintaxe da conditional-directive é a mesma, quer a condicional seja simples ou complexa, e tanto depois de um else quanto não. Há quatro diretivas diferentes que testam condições distintas. Eis uma tabela delas:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

Expande todas as referências a variáveis em arg1 e arg2 e as compara. Se forem idênticas, o text-if-true entra em vigor; caso contrário, o text-if-false, se houver, entra em vigor.

Muitas vezes você quer testar se uma variável tem um valor não vazio. Quando o valor resulta de expansões complexas de variáveis e funções, expansões que você consideraria vazias podem na verdade conter caracteres de espaço em branco e, assim, não são vistas como vazias. No entanto, você pode usar a função strip (consulte Funções para substituição e análise de cadeias de caracteres) para evitar interpretar o espaço em branco como um valor não vazio. Por exemplo:

ifeq ($(strip $(foo)),)
text-if-empty
endif

avaliará o text-if-empty mesmo que a expansão de $(foo) contenha caracteres de espaço em branco.

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"

Expande todas as referências a variáveis em arg1 e arg2 e as compara. Se forem diferentes, o text-if-true entra em vigor; caso contrário, o text-if-false, se houver, entra em vigor.

ifdef variable-name

A forma ifdef recebe como argumento o nome de uma variável, e não uma referência a uma variável. Se o valor dessa variável for um valor não vazio, o text-if-true entra em vigor; caso contrário, o text-if-false, se houver, entra em vigor. Variáveis que nunca foram definidas têm um valor vazio. O texto variable-name é expandido, de modo que pode ser uma variável ou função que se expande para o nome de uma variável. Por exemplo:

bar = true
foo = bar
ifdef $(foo)
frobozz = yes
endif

A referência de variável $(foo) é expandida, resultando em bar, que é considerado o nome de uma variável. A variável bar não é expandida, mas o seu valor é examinado para determinar se é não vazio.

Observe que ifdef apenas testa se uma variável tem um valor. Ela não expande a variável para verificar se esse valor é não vazio. Em consequência, os testes que usam ifdef retornam verdadeiro para todas as definições, exceto aquelas como foo =. Para testar um valor vazio, use ifeq ($(foo),). Por exemplo, ao escrever:

bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif

"frobozz" recebe o valor "yes". Já ao escrever:

foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif

"frobozz" recebe o valor "no".

ifndef variable-name

Se a variável variable-name tiver um valor vazio, o text-if-true entra em vigor; caso contrário, o text-if-false, se houver, entra em vigor. As regras de expansão e teste de variable-name são idênticas às da diretiva ifdef.

Espaços extras são permitidos e ignorados no início da linha da diretiva condicional, mas uma tabulação não é permitida. (Se a linha começa com uma tabulação, ela será considerada parte da receita de uma regra.) Fora isso, espaços ou tabulações extras podem ser inseridos sem nenhum efeito em qualquer lugar, exceto dentro do nome da diretiva ou dentro de um argumento. Um comentário começando com "#" pode aparecer no fim da linha.

As outras duas diretivas que desempenham um papel em uma condicional são else e endif. Cada uma dessas diretivas é escrita como uma única palavra, sem argumentos. Espaços extras são permitidos e ignorados no início da linha, e espaços ou tabulações no fim. Um comentário começando com "#" pode aparecer no fim da linha.

As condicionais afetam quais linhas do makefile o make usa. Se a condição for verdadeira, o make lê as linhas do text-if-true como parte do makefile; se a condição for falsa, o make ignora essas linhas por completo. Disso decorre que unidades sintáticas do makefile, como as regras, podem ser divididas com segurança através do início ou do fim da condicional.

O make avalia as condicionais quando lê um makefile. Em consequência, você não pode usar variáveis automáticas nos testes das condicionais, pois elas só são definidas quando as receitas são executadas (consulte Variáveis automáticas).

Para evitar uma confusão intolerável, não é permitido iniciar uma condicional em um makefile e terminá-la em outro. No entanto, você pode escrever uma diretiva include dentro de uma condicional, desde que não tente encerrar a condicional dentro do arquivo incluído.

7.3 Condicionais que testam sinalizadores

Você pode escrever uma condicional que testa sinalizadores de comando do make, como "-t", usando a variável MAKEFLAGS em conjunto com a função findstring (consulte Funções para substituição e análise de cadeias de caracteres). Isso é útil quando touch não basta para fazer um arquivo parecer atualizado.

Lembre-se de que MAKEFLAGS coloca todas as opções de uma única letra (como "-t") na primeira palavra, e essa palavra ficará vazia se nenhuma opção de uma única letra tiver sido dada. Para lidar com isso, é útil acrescentar um valor no início para garantir que haja uma palavra: por exemplo, "-$(MAKEFLAGS)".

A função findstring determina se uma cadeia de caracteres aparece como subcadeia de outra. Se você quiser testar o sinalizador "-t", use "t" como a primeira cadeia e a primeira palavra de MAKEFLAGS como a outra.

Por exemplo, eis como providenciar o uso de "ranlib -t" para concluir a marcação de um arquivo de arquivo (archive) como atualizado:

archive.a: …
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

O prefixo "+" marca essas linhas de receita como "recursivas", de modo que elas serão executadas apesar do uso do sinalizador "-t". Consulte Uso recursivo do make.


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