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

8 Funções para Transformar Texto

As funções (function) permitem realizar processamento de texto dentro do makefile. Com isso, você pode calcular os arquivos a serem processados ou montar os comandos usados nas receitas (recipe). As funções são utilizadas na forma de uma chamada de função. Quando você fornece o nome da função e o texto que ela deve processar (chamado de argumentos), a função realiza o processamento e o resultado é inserido no ponto da chamada. Esse é exatamente o mesmo mecanismo pelo qual uma variável é expandida e substituída.

8.1 Sintaxe da chamada de função

A chamada de função tem uma forma bastante parecida com a de uma referência de variável. Ela pode aparecer em qualquer lugar onde uma referência de variável possa aparecer, e as regras de expansão são as mesmas das referências de variável. Uma chamada de função tem a seguinte forma:

$(function arguments)

ou, então, também pode ser escrita assim:

${function arguments}

Aqui, function é o nome da função, um dos poucos nomes de função que fazem parte do make. Note ainda que, usando a função embutida call, você pode, na prática, criar suas próprias funções.

arguments são os argumentos da função. Os argumentos são separados do nome da função por um ou mais espaços ou tabulações e, quando há mais de um argumento, eles são separados entre si por vírgulas. Esses espaços em branco e vírgulas de separação não fazem parte do valor do argumento. Os delimitadores que envolvem a chamada de função podem ser tanto parênteses quanto chaves, mas, se você escrever esse tipo de parêntese ou chave dentro de um argumento, eles precisam estar em pares correspondentes. Já o outro tipo de delimitador pode aparecer sozinho, sem par. Quando um argumento contém ainda outras chamadas de função ou referências de variável, é mais sensato usar o mesmo tipo de delimitador para todas as referências. Ou seja, deve-se escrever «$(subst a,b,$(x))», e não misturá-los como em «$(subst a,b,${x})». Isso fica mais claro de ler e, além disso, ao procurar o fim da referência, apenas um tipo de delimitador é comparado.

Cada argumento é expandido antes de a função ser invocada (exceto quando indicado de outra forma a seguir). A expansão é feita na ordem em que os argumentos aparecem.

Caracteres especiais

Quando você quer usar como argumento de função caracteres que têm significado especial para o make, pode ser necessário ocultá-los. O GNU make não oferece suporte para escapar caracteres com barras invertidas ou outras sequências de escape. Entretanto, como os argumentos são divididos antes de serem expandidos, é possível ocultar caracteres especiais colocando-os dentro de variáveis.

Entre os caracteres que talvez seja preciso ocultar estão:

Por exemplo, você pode definir as variáveis comma e space, cujos valores são apenas um caractere de vírgula e um caractere de espaço, respectivamente, e então inserir essas variáveis nos pontos em que tais caracteres são necessários. Escreve-se assim:

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# Com isso, o valor de bar passa a ser 'a,b,c'.

Aqui, a função subst substitui cada espaço no valor de foo por uma vírgula e insere o resultado.

8.2 Funções para substituição e análise de cadeias

A seguir, apresentamos algumas funções que operam sobre cadeias de caracteres:

$(subst from,to,text)

Realiza uma substituição textual sobre o texto text: ou seja, cada ocorrência de from é substituída por to. O resultado é inserido no ponto da chamada de função. Por exemplo,

$(subst ee,EE,feet on the street)

produz o valor «fEEt on the strEEt».

$(patsubst pattern,replacement,text)

Procura, dentro de text, as palavras separadas por espaço em branco que correspondem a pattern e as substitui por replacement. Aqui, pattern pode conter um «%», que funciona como curinga e corresponde a qualquer quantidade de quaisquer caracteres dentro de uma palavra. Se replacement também contiver um «%», esse «%» é substituído pelo texto que correspondeu ao «%» de pattern. As palavras que não correspondem ao padrão permanecem inalteradas na saída. Apenas o primeiro «%» de pattern e de replacement é tratado dessa maneira especial; qualquer «%» seguinte é mantido tal como está.

Os caracteres «%» usados nas chamadas da função patsubst podem ser citados (quoted) colocando-se uma barra invertida («\») imediatamente antes. A própria barra invertida que cita o «%» pode, por sua vez, ser citada acrescentando-se mais barras invertidas. As barras invertidas que citam caracteres «%» ou outras barras invertidas são removidas do padrão antes que ele seja comparado com nomes de arquivo ou que um radical (stem) seja inserido nele. Por outro lado, as barras invertidas que não correm o risco de citar um caractere «%» permanecem intactas. Por exemplo, no padrão the\%weird\\%pattern\\, há «the%weird\» antes do «%» efetivo, seguido de «pattern\\». As duas últimas barras invertidas são mantidas tal como estão, pois não podem afetar nenhum caractere «%».

O espaço em branco entre palavras é reduzido a um único caractere de espaço; o espaço em branco no início e no fim é descartado.

Por exemplo,

$(patsubst %.c,%.o,x.c.c bar.c)

produz o valor «x.c.o bar.o».

As referências de substituição (consulte Referências de substituição) são uma forma mais simples de obter o mesmo efeito da função patsubst:

$(var:pattern=replacement)

é equivalente a

$(patsubst pattern,replacement,$(var))

A segunda forma abreviada simplifica um dos usos mais comuns do patsubst: a substituição do sufixo no fim de nomes de arquivo.

$(var:suffix=replacement)

é equivalente a

$(patsubst %suffix,%replacement,$(var))

Por exemplo, suponha que você tenha uma lista de arquivos objeto:

objects = foo.o bar.o baz.o

Se quiser obter a lista dos arquivos fonte correspondentes, em vez de usar a forma geral,

$(patsubst %.o,%.c,$(objects))

você pode simplesmente escrever:

$(objects:.o=.c)
$(strip string)

Remove o espaço em branco do início e do fim de string e substitui cada sequência interna de um ou mais caracteres de espaço em branco por um único espaço. Assim, «$(strip a b c )» resulta em «a b c».

A função strip é muito útil quando usada em conjunto com condicionais. Ao comparar algo com a cadeia vazia «» usando ifeq ou ifneq, é comum querer que uma cadeia formada apenas por espaços em branco corresponda à cadeia vazia (consulte Partes condicionais dos makefiles).

Por isso, o exemplo a seguir pode não produzir o resultado desejado:

.PHONY: all
ifneq   "$(needs_made)" ""
all: $(needs_made)
else
all:;@echo 'Nothing to make!'
endif

Substituir a referência de variável «$(needs_made)» pela chamada de função «$(strip $(needs_made))» dentro da diretiva ifneq tornaria o código mais robusto.

$(findstring find,in)

Procura por uma ocorrência de find dentro de in. Se ela ocorrer, o valor é find; caso contrário, o valor é vazio. Você pode usar essa função em uma condicional para testar a presença de uma subcadeia específica dentro de uma dada cadeia. Assim, os dois exemplos,

$(findstring a,a b c)
$(findstring a,b c)

produzem, respectivamente, os valores «a» e «» (a cadeia vazia). Para uma aplicação prática de findstring, consulte Condicionais que testam sinalizadores.

$(filter pattern…,text)

Retorna todas as palavras separadas por espaço em branco em text que correspondem a qualquer uma das palavras de pattern, removendo as palavras que não correspondem. Os padrões são escritos com «%», exatamente como nos padrões usados na função patsubst vista acima.

A função filter pode ser usada para separar diferentes tipos de cadeias (como nomes de arquivo) dentro de uma variável. Por exemplo:

sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
        cc $(filter %.c %.s,$(sources)) -o foo

Este exemplo diz que foo depende de foo.c, bar.c, baz.s e ugh.h, mas que apenas foo.c, bar.c e baz.s devem ser passados no comando ao compilador.

$(filter-out pattern…,text)

Retorna todas as palavras separadas por espaço em branco em text que não correspondem a nenhuma das palavras de pattern, removendo as palavras que correspondem a uma ou mais. Esse é exatamente o comportamento oposto ao da função filter.

Por exemplo, dado o seguinte:

objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o

a expressão a seguir gera uma lista contendo apenas os arquivos objeto que não estão em «mains»:

$(filter-out $(mains),$(objects))
$(sort list)

Ordena as palavras de list em ordem lexicográfica, removendo as palavras duplicadas. A saída é uma lista de palavras separadas por um único espaço. Assim,

$(sort foo bar lose)

retorna o valor «bar foo lose».

A propósito, como sort remove palavras duplicadas, você pode usá-la apenas para esse fim, mesmo quando a ordem de classificação não importa.

$(word n,text)

Retorna a n-ésima palavra de text. Os valores válidos de n começam em 1. Se n for maior do que o número de palavras em text, o valor é vazio. Por exemplo,

$(word 2, foo bar baz)

retorna «bar».

$(wordlist s,e,text)

Retorna a lista das palavras de text que vão da s-ésima palavra até a e-ésima palavra (ambas inclusive). Os valores válidos de s começam em 1; e pode começar em 0. Se s for maior do que o número de palavras em text, o valor é vazio. Se e for maior do que o número de palavras em text, são retornadas as palavras até o fim de text. Se s for maior do que e, nada é retornado. Por exemplo,

$(wordlist 2, 3, foo bar baz)

retorna «bar baz».

$(words text)

Retorna o número de palavras em text. Assim, a última palavra de text é obtida com $(word $(words text),text).

$(firstword names…)

O argumento names é considerado uma série de nomes separados por espaço em branco. O valor é o primeiro nome dessa série. Os demais nomes são ignorados.

Por exemplo,

$(firstword foo bar)

produz o resultado «foo». Embora $(firstword text) seja o mesmo que $(word 1,text), a função firstword foi mantida por sua simplicidade.

$(lastword names…)

O argumento names é considerado uma série de nomes separados por espaço em branco. O valor é o último nome dessa série.

Por exemplo,

$(lastword foo bar)

produz o resultado «bar». Embora $(lastword text) seja o mesmo que $(word $(words text),text), a função lastword foi acrescentada por sua simplicidade e melhor desempenho.

A seguir, um exemplo realista de uso de subst e patsubst. Suponha que um makefile use a variável VPATH para especificar uma lista de diretórios em que o make deve procurar os arquivos de pré-requisito (prerequisite) (consulte Caminho de busca VPATH para todos os pré-requisitos). Este exemplo mostra como instruir o compilador C a procurar os arquivos de cabeçalho na mesma lista de diretórios.

O valor de VPATH é uma lista de diretórios separados por dois-pontos, como, por exemplo, «src:../headers». Primeiro, usa-se a função subst para trocar os dois-pontos por espaços:

$(subst :, ,$(VPATH))

Isso produz «src ../headers». Em seguida, usa-se patsubst para transformar cada nome de diretório em um sinalizador «-I». O resultado obtido pode ser acrescentado ao valor da variável CFLAGS, que é passada automaticamente ao compilador C, da seguinte forma:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

O efeito é acrescentar o texto «-Isrc -I../headers» ao valor previamente dado a CFLAGS. A diretiva override é usada aqui para que o novo valor seja atribuído com segurança, mesmo que o valor anterior de CFLAGS tenha sido especificado por um argumento da linha de comando (consulte A diretiva override).

8.3 Funções para nomes de arquivo

Algumas das funções de expansão embutidas estão especificamente relacionadas a decompor nomes de arquivo ou listas de nomes de arquivo.

Cada uma das funções a seguir realiza uma transformação específica sobre nomes de arquivo. O argumento da função é considerado uma série de nomes de arquivo separados por espaço em branco (o espaço em branco no início e no fim é ignorado). Cada nome de arquivo da série é transformado da mesma maneira e os resultados são concatenados com um único espaço entre eles.

$(dir names…)

Extrai a parte de diretório de cada nome de arquivo em names. A parte de diretório de um nome de arquivo é tudo até a última barra (inclusive). Se o nome de arquivo não contiver nenhuma barra, a parte de diretório é a cadeia «./». Por exemplo,

$(dir src/foo.c hacks)

produz o resultado «src/ ./».

$(notdir names…)

Extrai tudo, exceto a parte de diretório, de cada nome de arquivo em names. Se o nome de arquivo não contiver nenhuma barra, ele é mantido inalterado. Se contiver, tudo até a última barra é removido.

Um nome de arquivo que termina com uma barra torna-se a cadeia vazia. Isso é lamentável, pois significa que o número de nomes de arquivo separados por espaço no resultado nem sempre coincide com o número de argumentos; mas não vimos nenhuma outra alternativa válida.

Por exemplo,

$(notdir src/foo.c hacks)

produz o resultado «foo.c hacks».

$(suffix names…)

Extrai o sufixo de cada nome de arquivo em names. Se o nome de arquivo contiver um ponto, o sufixo é tudo a partir do último ponto. Caso contrário, o sufixo é a cadeia vazia. Por isso, é frequente que o resultado seja vazio mesmo quando names não é vazio; e, se names contiver vários nomes de arquivo, o resultado pode conter menos nomes de arquivo.

Por exemplo,

$(suffix src/foo.c src-1.0/bar.c hacks)

produz o resultado «.c .c».

$(basename names…)

Extrai tudo, exceto o sufixo, de cada nome de arquivo em names. Se o nome de arquivo contiver um ponto, o basename é tudo até (mas sem incluir) o último ponto. Os pontos na parte de diretório são ignorados. Se não houver nenhum ponto, o basename é o nome de arquivo inteiro. Por exemplo,

$(basename src/foo.c src-1.0/bar hacks)

produz o resultado «src/foo src-1.0/bar hacks».

$(addsuffix suffix,names…)

O argumento names é considerado uma série de nomes separados por espaço em branco. suffix é tratado como uma unidade. O valor de suffix é acrescentado ao fim de cada nome individual, e os nomes maiores assim formados são concatenados com um único espaço entre eles. Por exemplo,

$(addsuffix .c,foo bar)

produz o resultado «foo.c bar.c».

$(addprefix prefix,names…)

O argumento names é considerado uma série de nomes separados por espaço em branco. prefix é tratado como uma unidade. O valor de prefix é anteposto ao início de cada nome individual, e os nomes maiores assim formados são concatenados com um único espaço entre eles. Por exemplo,

$(addprefix src/,foo bar)

produz o resultado «src/foo src/bar».

$(join list1,list2)

Concatena os dois argumentos palavra por palavra: as duas primeiras palavras (uma de cada argumento) concatenadas formam a primeira palavra do resultado, as duas segundas palavras formam a segunda palavra do resultado, e assim por diante. Ou seja, a n-ésima palavra do resultado vem da n-ésima palavra de cada argumento. Se um dos argumentos tiver mais palavras do que o outro, as palavras excedentes são copiadas inalteradas para o resultado.

Por exemplo, «$(join a b,.c .o)» produz «a.c b.o».

O espaço em branco entre as palavras das listas não é preservado; ele é substituído por um único espaço.

Esta função pode unir os resultados das funções dir e notdir, recompondo a lista original de arquivos que foi dada a essas duas funções.

$(wildcard pattern)

O argumento pattern é um padrão de nome de arquivo, que tipicamente contém caracteres curinga (como nos padrões de nome de arquivo do shell). O resultado de wildcard é uma lista, separada por espaços, com os nomes dos arquivos existentes que correspondem ao padrão. Consulte Uso de caracteres curinga em nomes de arquivo.

$(realpath names…)

Para cada nome de arquivo em names, retorna o nome absoluto canônico. Um nome canônico não contém componentes . ou .., nem separadores de caminho duplicados (/), nem links simbólicos. Em caso de falha, a cadeia vazia é retornada. Consulte a documentação de realpath(3) para uma lista das possíveis causas de falha.

$(abspath names…)

Para cada nome de arquivo em names, retorna um nome absoluto que não contém componentes . ou .., nem separadores de caminho duplicados (/). Note que, ao contrário da função realpath, abspath não resolve links simbólicos, nem exige que o nome de arquivo aponte para um arquivo ou diretório existente. Para verificar a existência, use a função wildcard.

8.4 Funções para condicionais

Há quatro funções que realizam expansão condicional. Uma característica importante dessas funções é que nem todos os argumentos são expandidos desde o início. Apenas os argumentos que precisam ser expandidos é que são expandidos.

$(if condition,then-part[,else-part])

A função if oferece suporte à expansão condicional na forma de função (em oposição às condicionais de makefile do GNU make, como ifeq (consulte Sintaxe das condicionais)).

O primeiro argumento, condition, tem primeiro todo o espaço em branco do início e do fim removido e, em seguida, é expandido. Se ele se expandir para qualquer cadeia não vazia, a condição é considerada verdadeira. Se se expandir para a cadeia vazia, a condição é considerada falsa.

Se a condição for verdadeira, o segundo argumento, then-part, é avaliado e usado como resultado da avaliação de toda a função if.

Se a condição for falsa, o terceiro argumento, else-part, é avaliado e este é o resultado da função if. Se não houver terceiro argumento, a função if não produz nada (a cadeia vazia).

Observe que apenas then-part ou else-part é avaliado, nunca os dois. Por isso, qualquer um deles pode conter efeitos colaterais (como chamadas à função shell, etc.).

$(or condition1[,condition2[,condition3…]])

A função or fornece uma operação OR com «curto-circuito (short-circuiting)». Cada argumento é expandido, em ordem. No momento em que um argumento se expande para uma cadeia não vazia, o processamento para e o resultado dessa expansão é o valor de retorno. Se, depois de todos os argumentos terem sido expandidos, todos eles forem falsos (vazios), o resultado da expansão é a cadeia vazia.

$(and condition1[,condition2[,condition3…]])

A função and fornece uma operação AND com «curto-circuito (short-circuiting)». Cada argumento é expandido, em ordem. No momento em que um argumento se expande para a cadeia vazia, o processamento para e o resultado da expansão é a cadeia vazia. Se todos os argumentos se expandirem para uma cadeia não vazia, o resultado da expansão é a expansão do último argumento.

$(intcmp lhs,rhs[,lt-part[,eq-part[,gt-part]]])

A função intcmp oferece suporte à comparação numérica de inteiros. Esta função não tem equivalente entre as condicionais de makefile do GNU make.

O lado esquerdo, lhs, e o lado direito, rhs, são expandidos e interpretados como números inteiros na base 10. A expansão dos argumentos restantes é controlada pela forma como o lado esquerdo numérico se compara ao lado direito numérico.

Se não houver mais argumentos, a função se expande para vazio caso o lado esquerdo e o lado direito não sejam iguais, ou para o valor numérico deles caso sejam iguais.

Caso contrário, se o lado esquerdo for estritamente menor que o lado direito, a função intcmp avalia para a expansão do terceiro argumento, lt-part. Se ambos os lados forem iguais, a função intcmp avalia para a expansão do quarto argumento, eq-part. Se o lado esquerdo for estritamente maior que o lado direito, a função intcmp avalia para a expansão do quinto argumento, gt-part.

Se gt-part for omitido, seu valor padrão é eq-part. Se eq-part for omitido, seu valor padrão é a cadeia vazia. Assim, tanto «$(intcmp 9,7,hello)» quanto «$(intcmp 9,7,hello,world,)» avaliam para a cadeia vazia, ao passo que «$(intcmp 9,7,hello,world)» (note a ausência de vírgula após world) avalia para «world».

8.5 A função let

A função let oferece um meio de limitar o escopo de uma variável. A atribuição às variáveis nomeadas em uma expressão let tem efeito apenas dentro do texto fornecido por essa expressão let, e essa atribuição não afeta a variável de mesmo nome em nenhum escopo externo.

Além disso, a função let permite o «desempacotamento» de listas, atribuindo todos os valores não atribuídos à última variável nomeada.

A sintaxe da função let é a seguinte:

$(let var [var ...],[list],text)

Os dois primeiros argumentos, var e list, são expandidos antes de qualquer outra coisa; observe que o último argumento, text, não é expandido ao mesmo tempo. Em seguida, cada palavra do valor expandido de list é vinculada a cada um dos nomes de variável var, em ordem, com o último nome de variável sendo vinculado a todo o restante do list expandido. Em outras palavras, a primeira palavra de list é vinculada à primeira variável var, a segunda palavra à segunda variável var, e assim por diante.

Se houver mais nomes de variável em var do que palavras em list, os nomes de variável var excedentes são definidos com a cadeia vazia. Se houver menos var do que palavras em list, a última var é definida com todas as palavras restantes de list.

Cada variável em var é atribuída como variável de expansão simples durante a execução de let. Consulte Os dois tipos de variável.

Depois que todas as variáveis estão assim vinculadas, text é expandido, e isso constitui o resultado da função let.

Por exemplo, a macro a seguir inverte a ordem das palavras da lista dada como seu primeiro argumento:

reverse = $(let first rest,$1,\
            $(if $(rest),$(call reverse,$(rest)) )$(first))

all: ; @echo $(call reverse,d c b a)

Isso imprime a b c d. Na primeira chamada, let expande $1 para d c b a. Depois, atribui d a first e c b a a rest. Em seguida, expande a instrução if; aqui, como $(rest) não é vazio, invocamos recursivamente a função reverse com o valor de rest, que agora é c b a. A invocação recursiva de let atribui c a first e b a a rest. A recursão continua até que let seja chamado com um único valor, a. Nesse ponto, first é a e rest é vazio, então não recorremos mais e simplesmente expandimos $(first) para a e retornamos, ao que se acrescenta b, e assim por diante.

Após a chamada de reverse ser concluída, as variáveis first e rest deixam de estar definidas. Caso já existissem variáveis com esses nomes anteriormente, elas não são afetadas pela expansão da macro reverse.

8.6 A função foreach

A função foreach é parecida com a função let, mas muito diferente das demais funções. Ela faz com que um mesmo trecho de texto seja usado repetidamente, aplicando-se a cada vez uma substituição diferente sobre ele. A função foreach é bem parecida com o comando for do shell sh e com o comando foreach do C-shell csh.

A sintaxe da função foreach é a seguinte:

$(foreach var,list,text)

Os dois primeiros argumentos, var e list, são expandidos antes de qualquer outra coisa; observe que o último argumento, text, não é expandido ao mesmo tempo. Então, para cada palavra do valor expandido de list, a variável cujo nome é dado pelo valor expandido de var é definida com essa palavra, e text é expandido. Como text provavelmente contém referências a essa variável, sua expansão será diferente a cada vez.

O resultado é que text é expandido tantas vezes quantas forem as palavras separadas por espaço em branco em list. As múltiplas expansões de text são concatenadas, com espaços entre elas, formando o resultado de foreach.

Este exemplo simples define a variável «files» com a lista de todos os arquivos nos diretórios da lista «dirs»:

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

Aqui, text é «$(wildcard $(dir)/*)». Na primeira repetição, encontra-se o valor «a» para dir, de modo que se produz o mesmo resultado de «$(wildcard a/*)»; a segunda repetição produz o resultado de «$(wildcard b/*)»; e a terceira, o de «$(wildcard c/*)».

Este exemplo tem o mesmo resultado (exceto pela definição de «dirs») do exemplo a seguir:

files := $(wildcard a/* b/* c/* d/*)

Quando text é complexo, você pode melhorar a legibilidade dando-lhe um nome por meio de uma variável adicional:

find_files = $(wildcard $(dir)/*)
dirs := a b c d
files := $(foreach dir,$(dirs),$(find_files))

Aqui usamos a variável find_files dessa maneira. Definimos com um simples «=», como variável de expansão recursiva, para que o seu valor contenha uma chamada de função efetiva, a ser reexpandida sob o controle de foreach. Uma variável de expansão simples não serviria, pois, nesse caso, wildcard seria chamada apenas uma vez, no momento da definição de find_files.

Assim como a função let, a função foreach não tem efeito permanente sobre a variável var. O valor e o tipo de var após a chamada da função foreach são os mesmos de antes da chamada. Os demais valores tomados de list têm efeito apenas temporariamente, durante a execução de foreach. Durante a execução de foreach, a variável var é uma variável de expansão simples. Se var estava indefinida antes da chamada da função foreach, ela permanece indefinida depois da chamada. Consulte Os dois tipos de variável.

É preciso ter cuidado ao usar expressões de variável complexas que produzem nomes de variável, pois muitas cadeias estranhas podem ser nomes de variável válidos, e provavelmente não é isso que você pretendia. Por exemplo,

files := $(foreach Esta-escrito-en-espanol!,b c ch,$(find_files))

Se o valor de find_files fizer referência a uma variável com o nome «Esta-escrito-en-espanol!» (es un nombre bastante largo, no?), isso pode ser útil. Mas, na maioria das vezes, será um erro.

8.7 A função file

A função file permite que o makefile escreva em um arquivo ou leia de um arquivo. Há suporte a dois modos de escrita. Um é o «sobrescrever», em que o texto é escrito a partir do início do arquivo e todo o conteúdo existente é perdido. O outro é o «acrescentar (append)», em que o texto é escrito no fim do arquivo, preservando o conteúdo existente. Em ambos os casos, o arquivo é criado caso não exista. Se o arquivo não puder ser aberto para escrita, ou se a operação de escrita falhar, ocorre um erro fatal. Ao escrever em um arquivo, a função file se expande para a cadeia vazia.

Ao ler de um arquivo, a função file se expande para o conteúdo do arquivo tal como está (exceto que a última quebra de linha, se houver, é removida). A tentativa de leitura de um arquivo inexistente se expande para a cadeia vazia.

A sintaxe da função file é a seguinte:

$(file op filename[,text])

Quando a função file é avaliada, primeiro todos os seus argumentos são expandidos e, então, o arquivo indicado por filename é aberto no modo descrito por op.

O operador op pode ser >, para indicar que o arquivo será sobrescrito com o novo conteúdo; >>, para indicar que o conteúdo atual do arquivo será acrescentado; ou <, para indicar que o conteúdo do arquivo será lido. filename especifica o arquivo a ser escrito ou lido. Pode haver, opcionalmente, espaço em branco entre o operador e o nome do arquivo.

Ao ler arquivos, é um erro fornecer um valor de text.

Ao escrever em arquivos, text é escrito no arquivo. Se text ainda não terminar com uma quebra de linha, uma quebra de linha final é escrita (mesmo que text seja a cadeia vazia). Se o argumento text não for fornecido de modo algum, nada é escrito.

Por exemplo, a função file é útil quando o seu sistema de compilação tem um limite no tamanho da linha de comando e a receita executa um comando que também pode receber argumentos a partir de um arquivo. Muitos comandos adotam a convenção de que um argumento prefixado por @ aponta para um arquivo que contém outros argumentos. Nesse caso, você poderia escrever a receita assim:

program: $(OBJECTS)
        $(file >$@.in,$^)
        $(CMD) $(CMDFLAGS) @$@.in
        @rm $@.in

Se o comando exigir que cada argumento esteja em uma linha separada do arquivo de entrada, você poderia escrever a receita assim:

program: $(OBJECTS)
        $(file >$@.in) $(foreach O,$^,$(file >>$@.in,$O))
        $(CMD) $(CMDFLAGS) @$@.in
        @rm $@.in

8.8 A função call

A função call é singular por poder ser usada para criar novas funções parametrizadas. Você pode escrever uma expressão complexa como valor de uma variável e, então, usar call para expandi-la com valores diferentes.

A sintaxe da função call é a seguinte:

$(call variable,param,param,…)

Quando o make expande essa função, ele atribui cada param às variáveis temporárias $(1), $(2), etc. A variável $(0) conterá variable. Não há número máximo de argumentos de parâmetro. Também não há mínimo, mas não faz sentido usar call sem nenhum parâmetro.

Então variable é expandida como variável do make no contexto dessas atribuições temporárias. Assim, qualquer referência a $(1) no valor de variable será resolvida para o primeiro param dessa chamada de call.

Observe que variable é o nome da variável, e não uma referência a ela. Portanto, normalmente não se usam «$» ou parênteses ao escrevê-la (porém, caso você não queira que o nome seja constante, pode usar uma referência de variável dentro do nome).

Se variable for o nome de uma função embutida, a função embutida é sempre invocada (mesmo que também exista uma variável do make com esse nome).

A função call expande os argumentos param antes de atribuí-los às variáveis temporárias. Por isso, valores de variable que contêm referências a funções embutidas com regras de expansão especiais, como foreach ou if, podem não funcionar como você espera.

Alguns exemplos ajudarão a esclarecer.

A macro a seguir simplesmente inverte seus argumentos:

reverse = $(2) $(1)

foo = $(call reverse,a,b)

Aqui, foo conterá «b a».

Este é um pouco mais interessante: define uma macro que procura a primeira ocorrência de um programa em PATH:

pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH)))))

LS := $(call pathsearch,ls)

Agora a variável LS contém /bin/ls ou algo semelhante.

A função call pode ser aninhada. Cada invocação recursiva tem seus próprios valores locais de $(1), etc., que mascaram os valores de um call de nível superior. Por exemplo, eis uma implementação de uma função map:

map = $(foreach a,$(2),$(call $(1),$(a)))

Com isso, você pode aplicar com map uma função que normalmente recebe apenas um argumento (como origin) a vários valores de uma só vez:

o = $(call map,origin,o map MAKE)

Como resultado, o contém algo como «file file default».

Uma última advertência: tenha cuidado ao acrescentar espaço em branco aos argumentos de call. Como nas demais funções, o espaço em branco contido no segundo argumento e nos seguintes é preservado, o que pode causar efeitos estranhos. Ao passar parâmetros para call, em geral é mais seguro remover todo o espaço em branco supérfluo.

8.9 A função value

A função value oferece um meio de usar o valor de uma variável sem que ele seja expandido. Observe, porém, que isso não desfaz expansões que já tenham ocorrido. Por exemplo, ao criar uma variável de expansão simples, seu valor é expandido no momento da definição; nesse caso, a função value retorna o mesmo resultado de usar a variável diretamente.

A sintaxe da função value é a seguinte:

$(value variable)

Observe que variable é o nome da variável, e não uma referência a ela. Portanto, normalmente não se usam «$» ou parênteses ao escrevê-la (porém, caso você não queira que o nome seja constante, pode usar uma referência de variável dentro do nome).

O resultado dessa função é uma cadeia que contém o valor de variable tal como está, sem realizar nenhuma expansão. Por exemplo, no makefile a seguir:

FOO = $PATH

all:
        @echo $(FOO)
        @echo $(value FOO)

A saída da primeira linha é ATH, porque «$P» é expandido como variável do make. Já a saída da segunda linha é o valor atual da sua variável de ambiente $PATH, pois a função value evitou a expansão.

A função value é usada com mais frequência em conjunto com a função eval (consulte A função eval).

8.10 A função eval

A função eval é muito especial. Com ela, você pode definir novos elementos de sintaxe de makefile que não são constantes — elementos resultantes da avaliação de outras variáveis e funções. O argumento da função eval é primeiro expandido, e o resultado dessa expansão é interpretado como sintaxe de makefile. O resultado da expansão pode definir novas variáveis do make, alvos, regras implícitas ou explícitas, etc.

O resultado da função eval é sempre a cadeia vazia. Por isso, ela pode ser colocada em praticamente qualquer lugar do makefile sem causar erro de sintaxe.

Aqui é importante entender que o argumento de eval é expandido duas vezes: a primeira pela função eval e a segunda quando o resultado dessa expansão é interpretado como sintaxe de makefile, sendo novamente expandido. Ou seja, ao usar eval, às vezes é preciso acrescentar mais um nível de escape para o caractere «$». Nessas situações, a função value (consulte A função value) pode ajudar a evitar expansões indesejadas.

Eis um exemplo de como eval pode ser usado. Este exemplo combina vários conceitos e outras funções. Pode parecer exagero usar eval em vez de escrever as regras diretamente, mas considere duas coisas. Primeiro, a definição do template (dentro de PROGRAM_template) talvez precise ser muito mais complexa do que a mostrada aqui. Segundo, você pode colocar a parte complexa e «genérica» deste exemplo em outro makefile e incluí-la em todos os makefiles individuais. Assim, cada makefile individual fica bem mais enxuto.

PROGRAMS    = server client

server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol

client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol

# Tudo daqui em diante é a parte genérica

.PHONY: all
all: $(PROGRAMS)

define PROGRAM_template =
 $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
 ALL_OBJS   += $$($(1)_OBJS)
endef

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))

$(PROGRAMS):
        $(LINK.o) $^ $(LDLIBS) -o $@

clean:
        rm -f $(ALL_OBJS) $(PROGRAMS)

8.11 A função origin

A função origin, ao contrário da maioria das outras funções, não manipula o valor de uma variável. Em vez disso, ela informa algo sobre a variável. Especificamente, ela informa de onde a variável veio.

A sintaxe da função origin é a seguinte:

$(origin variable)

Observe que variable é o nome da variável a ser consultada, e não uma referência a ela. Portanto, normalmente não se usam «$» ou parênteses ao escrevê-la (porém, caso você não queira que o nome seja constante, pode usar uma referência de variável dentro do nome).

O resultado dessa função é uma cadeia que informa como a variável variable foi definida:

«undefined»

Caso variable nunca tenha sido definida.

«default»

Caso variable tenha uma definição padrão, como ocorre com CC, etc. Consulte Variáveis usadas pelas regras implícitas. Note que, se você redefinir uma variável padrão, a função origin retorna a origem da definição posterior.

«environment»

Caso variable tenha sido herdada do ambiente dado ao make.

«environment override»

Caso variable tenha sido herdada do ambiente dado ao make e, em razão da opção «-e» (consulte Resumo das opções), esteja sobrepondo a definição de variable feita no makefile.

«file»

Caso variable tenha sido definida dentro do makefile.

«command line»

Caso variable tenha sido definida na linha de comando.

«override»

Caso variable tenha sido definida por uma diretiva override no makefile (consulte A diretiva override).

«automatic»

Caso variable seja uma variável automática definida para a execução da receita de cada regra (consulte Variáveis automáticas).

Essa informação serve (deixando de lado a mera curiosidade) sobretudo para decidir se é seguro confiar no valor de uma variável. Por exemplo, suponha que você tenha um makefile foo que inclui outro makefile bar. Ao executar o comando «make -f bar», você quer que a variável bletch seja definida em bar, mesmo que o ambiente contenha uma definição de bletch. No entanto, se foo já tiver definido bletch antes de incluir bar, você não quer que essa definição seja sobreposta. Isso poderia ser feito usando uma diretiva override em foo, dando a essa definição prioridade sobre a definição posterior em bar. Infelizmente, porém, a diretiva override sobrepõe até mesmo as definições da linha de comando. Então, seria melhor incluir em bar o seguinte:

ifdef bletch
ifeq "$(origin bletch)" "environment"
bletch = barf, gag, etc.
endif
endif

Caso bletch tenha sido definida a partir do ambiente, isso a redefine.

Se você quiser sobrepor a definição anterior de bletch mesmo que ela tenha vindo do ambiente, ainda que sob «-e», pode escrever, em vez disso, o seguinte:

ifneq "$(findstring environment,$(origin bletch))" ""
bletch = barf, gag, etc.
endif

Aqui, a redefinição ocorre caso «$(origin bletch)» retorne «environment» ou «environment override». Consulte Funções para substituição e análise de cadeias.

8.12 A função flavor

A função flavor, assim como a função origin, não manipula o valor de uma variável, mas informa algo sobre a variável. Especificamente, ela informa o tipo (flavor) da variável (consulte Os dois tipos de variável).

A sintaxe da função flavor é a seguinte:

$(flavor variable)

Observe que variable é o nome da variável a ser consultada, e não uma referência a ela. Portanto, normalmente não se usam «$» ou parênteses ao escrevê-la (porém, caso você não queira que o nome seja constante, pode usar uma referência de variável dentro do nome).

O resultado dessa função é uma cadeia que indica o tipo da variável variable:

«undefined»

Caso variable nunca tenha sido definida.

«recursive»

Caso variable seja uma variável de expansão recursiva.

«simple»

Caso variable seja uma variável de expansão simples.

8.13 Funções que controlam o make

Essas funções controlam o funcionamento do make. Em geral, são usadas para fornecer informações ao usuário do makefile ou para interromper o make quando algum erro de ambiente é detectado.

$(error text…)

Gera um erro fatal cuja mensagem é text. Note que o erro é gerado sempre que essa função é avaliada. Portanto, se você a colocar dentro de uma receita ou no lado direito de uma atribuição a uma variável de expansão recursiva, a avaliação ocorrerá apenas mais tarde. text é expandido antes de o erro ser gerado.

Por exemplo,

ifdef ERROR1
$(error error is $(ERROR1))
endif

gera um erro fatal durante a leitura do makefile, caso a variável ERROR1 do make esteja definida. Já,

ERR = $(error found an error!)

.PHONY: err
err: ; $(ERR)

gera um erro fatal durante a execução do make, caso o alvo err seja invocado.

$(warning text…)

Esta função funciona como a função error acima, com a diferença de que o make não termina. Em vez disso, text é expandido e a mensagem resultante é exibida, mas o processamento do makefile continua.

O resultado da expansão dessa função é a cadeia vazia.

$(info text…)

Esta função não faz nada além de exibir seus argumentos (já expandidos) na saída padrão. Nenhum nome de makefile ou número de linha é adicionado. O resultado da expansão dessa função é a cadeia vazia.

8.14 A função shell

A função shell, à exceção da função wildcard (consulte A função wildcard), é diferente de todas as outras funções, pois interage com o mundo fora do make.

A função shell oferece ao make o mesmo recurso das crases («`») na maioria dos shells, isto é, realiza expansão de comando. Isso significa que ela recebe um comando do shell como argumento e se expande para a saída desse comando. O único processamento que o make faz sobre esse resultado é converter cada quebra de linha (ou par de retorno de carro e quebra de linha) em um único espaço. Se houver uma quebra de linha (e retorno de carro) no final, ela é simplesmente removida.

O comando executado por uma chamada da função shell é executado quando essa chamada de função é expandida (consulte Como o make lê um Makefile). Como essa função implica a inicialização de um novo shell, vale considerar com atenção o impacto no desempenho ao usar a função shell dentro de uma variável de expansão recursiva, em comparação com seu uso dentro de uma variável de expansão simples (consulte Os dois tipos de variável).

Como alternativa à função shell, há o operador de atribuição «!=». Ele tem comportamento semelhante, mas com diferenças sutis (consulte Definição de variáveis). O operador de atribuição «!=» faz parte do padrão POSIX mais recente.

Depois que a função shell ou o operador de atribuição «!=» é usado, o status de saída correspondente é armazenado na variável .SHELLSTATUS.

Eis alguns exemplos de uso da função shell:

contents := $(shell cat foo)

Isso define contents com o conteúdo do arquivo foo, com cada linha separada por um espaço (em vez de quebra de linha).

files := $(shell echo *.c)

Isso define files com o resultado da expansão de «*.c». A menos que o make esteja usando um shell bastante peculiar, isso produz o mesmo resultado de «$(wildcard *.c)» (desde que exista pelo menos um arquivo «.c»).

Todas as variáveis marcadas para export também são passadas ao shell iniciado pela função shell. Isso pode dar origem a um laço de expansão de variável. Considere o seguinte makefile:

export HI = $(shell echo hi)
all: ; @echo $$HI

Quando o make vai executar a receita, ele precisa acrescentar a variável HI ao ambiente. Para isso, é preciso expandir HI. O valor dessa variável exige uma chamada à função shell, e para invocá-la é preciso construir o seu ambiente. Como HI está exportada, construir esse ambiente exige expandir HI. E isso prossegue indefinidamente. Nesse caso obscuro, em vez de entrar em laço ou emitir um erro, o make usa o valor dessa variável presente no ambiente dado ao make e, se não houver, usa a cadeia vazia. Isso geralmente é o que você deseja. Por exemplo:

export PATH = $(shell echo /usr/local/bin:$$PATH)

Ainda assim, nesse caso, usar desde o início uma variável de expansão simples («:=») seria mais simples e eficiente.

8.15 A função guile

Se o GNU make tiver sido compilado com suporte ao GNU Guile como linguagem de extensão embutida, a função guile fica disponível. A função guile recebe um argumento, que é primeiro expandido normalmente pelo make e, então, passado ao avaliador do GNU Guile. O resultado do avaliador é convertido em cadeia e usado como resultado da expansão da função guile no makefile. Para detalhes sobre como escrever extensões do make em Guile, consulte Integração com o GNU Guile.

Para saber se o suporte ao GNU Guile está disponível, verifique se há a palavra «guile» na variável .FEATURES.


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