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

8 Funciones para transformar texto

Las funciones (function) permiten procesar texto dentro de un makefile. Gracias a ellas se pueden calcular qué archivos hay que procesar o construir las órdenes que se usan en una receta (recipe). Las funciones se utilizan mediante una llamada a función. Se da el nombre de la función y el texto que se quiere que la función procese (a este texto se le llama argumentos); la función realiza su trabajo y su resultado se inserta en el lugar de la llamada. Es exactamente el mismo mecanismo por el que una variable se expande y se sustituye.

8.1 Sintaxis de la llamada a función

Una llamada a función tiene una forma muy parecida a la de una referencia de variable. Puede escribirse en cualquier lugar donde pueda escribirse una referencia de variable, y sus reglas de expansión son las mismas que las de las referencias de variable. Una llamada a función adopta la siguiente forma:

$(function arguments)

O bien, también puede escribirse de esta otra forma:

${function arguments}

Aquí function es el nombre de la función, que será uno de los pocos nombres de función incorporados en make. Por cierto, mediante la función incorporada call también es posible, en la práctica, crear funciones propias.

arguments son los argumentos de la función. Los argumentos se separan del nombre de la función por uno o más espacios o tabulaciones, y si hay varios argumentos, se separan entre sí con comas. Estos espacios y comas de separación no forman parte del valor de los argumentos. Los delimitadores que rodean a la llamada a función pueden ser paréntesis o llaves, pero si dentro de los argumentos se escribe ese mismo tipo de paréntesis o llave, debe estar emparejado. En cambio, el otro tipo de delimitador puede escribirse suelto, sin estar emparejado. Si dentro de los argumentos se incluyen otras llamadas a función o referencias de variable, lo sensato es usar el mismo tipo de delimitador para todas las referencias. Es decir, conviene escribir «$(subst a,b,$(x))», y no mezclarlos como en «$(subst a,b,${x})». Así resulta más claro a la vista, y porque al buscar el final de una referencia solo se coteja un único tipo de delimitador.

Cada argumento se expande por separado antes de que se llame a la función (salvo en los casos en que se indique algo distinto más abajo). La expansión se realiza en el orden en que están colocados los argumentos.

Caracteres especiales

Cuando se quiere usar como argumento de una función un carácter que tiene un significado especial para make, a veces es necesario ocultarlo. GNU make no admite el escape de caracteres mediante barra invertida ni otras secuencias de escape. Sin embargo, como los argumentos se dividen antes de expandirse, los caracteres especiales pueden ocultarse guardándolos dentro de una variable.

Entre los caracteres que quizá haya que ocultar están los siguientes:

Por ejemplo, se pueden definir las variables comma y space, cuyos valores sean únicamente un carácter de coma y un carácter de espacio, e insertar estas variables allí donde se necesiten dichos caracteres. Se escribe así:

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# El valor de bar queda así como 'a,b,c'.

Aquí la función subst sustituye por una coma cada espacio del valor de foo e inserta el resultado.

8.2 Funciones para la sustitución y el análisis de cadenas

A continuación se presentan algunas funciones que manipulan cadenas:

$(subst from,to,text)

Realiza una sustitución de cadenas sobre el texto text. Es decir, cada vez que aparece from lo reemplaza por to. El resultado se inserta en el lugar de la llamada a función. Por ejemplo,

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

produce el valor «fEEt on the strEEt».

$(patsubst pattern,replacement,text)

Busca en text las palabras separadas por espacios en blanco que coincidan con pattern y las reemplaza por replacement. Aquí pattern puede contener un «%», que actúa como comodín y coincide con cualquier número de caracteres cualesquiera dentro de una palabra. Si replacement también contiene un «%», ese «%» se sustituye por el texto que coincidió con el «%» de pattern. Las palabras que no coinciden con el patrón quedan en la salida sin modificar. Solo reciben este tratamiento especial el primer «%» de pattern y el de replacement; cualquier «%» posterior se trata literalmente.

El carácter «%» empleado en una llamada a la función patsubst puede protegerse (quote) anteponiéndole una barra invertida («\»). La propia barra invertida, que cumple la función de proteger el «%», puede a su vez protegerse con otra barra invertida. Las barras invertidas que protegen un carácter «%» u otras barras invertidas se eliminan del patrón antes de que este se coteje con los nombres de archivo o se inserte la raíz (stem). Por el contrario, las barras invertidas que no corren el riesgo de proteger un carácter «%» se dejan intactas. Por ejemplo, en el patrón the\%weird\\%pattern\\, antes del carácter «%» efectivo está «the%weird\», y después le sigue «pattern\\». Las dos barras invertidas finales se dejan tal cual porque no pueden afectar a ningún carácter «%».

Los espacios en blanco entre palabras se condensan en un único carácter de espacio. Además, los espacios en blanco iniciales y finales se descartan.

Por ejemplo,

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

produce el valor «x.c.o bar.o».

Mediante una referencia de sustitución (véase el apartado referencias de sustitución) se obtiene el mismo efecto que con la función patsubst de forma más sencilla:

$(var:pattern=replacement)

equivale a

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

La segunda forma abreviada simplifica uno de los usos más habituales de patsubst: la operación de sustituir el sufijo del final de un nombre de archivo.

$(var:suffix=replacement)

equivale a

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

Por ejemplo, supongamos que se tiene una lista de archivos objeto:

objects = foo.o bar.o baz.o

Para obtener la lista de los archivos fuente correspondientes, en lugar de usar la forma general,

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

basta con escribir simplemente:

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

Elimina los espacios en blanco iniciales y finales de string y reemplaza cada grupo de uno o más caracteres de espacio en blanco consecutivos del interior por un único espacio. Así, el resultado de «$(strip a b c )» es «a b c».

La función strip resulta muy útil combinada con los condicionales. Cuando se usa ifeq o ifneq para comparar algo con la cadena vacía «», es frecuente desear que una cadena formada únicamente por espacios en blanco coincida con la cadena vacía (véase Partes condicionales de los makefiles).

Por eso, el siguiente ejemplo quizá no dé el resultado esperado:

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

Si se reemplaza la referencia de variable «$(needs_made)» de la directiva ifneq por la llamada a función «$(strip $(needs_made))», se vuelve más robusto.

$(findstring find,in)

Busca si find aparece dentro de in. Si aparece, el valor es find. Si no aparece, el valor es vacío. Esta función puede usarse en un condicional para comprobar si una cadena contiene una determinada subcadena. Así, los dos ejemplos siguientes,

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

producen, respectivamente, los valores «a» y «» (la cadena vacía). Para una aplicación práctica de findstring, véase el apartado Condicionales que comprueban indicadores.

$(filter pattern…,text)

Devuelve todas las palabras de text, separadas por espacios en blanco, que coinciden con alguno de los patrones pattern dados, y elimina las palabras que no coinciden. Los patrones se escriben con «%», igual que en la función patsubst vista antes.

La función filter puede usarse para separar los distintos tipos de cadenas (por ejemplo, nombres de archivo) que hay dentro de una variable. Por ejemplo:

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

Este ejemplo expresa que foo depende de foo.c, bar.c, baz.s y ugh.h, pero que a la orden del compilador solo deben pasarse foo.c, bar.c y baz.s.

$(filter-out pattern…,text)

Devuelve todas las palabras de text, separadas por espacios en blanco, que no coinciden con ninguno de los patrones pattern dados, y elimina las palabras que coinciden con uno o más. Es justamente la operación contraria a la de la función filter.

Por ejemplo, dado lo siguiente:

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

la siguiente expresión genera una lista formada solo por los archivos objeto que no están incluidos en «mains»:

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

Ordena las palabras de list en orden lexicográfico y elimina las palabras duplicadas. La salida es una lista de palabras separadas por un único espacio. Así,

$(sort foo bar lose)

devuelve el valor «bar foo lose».

Por cierto, como sort elimina las palabras duplicadas, puede usarse con el único fin de eliminar duplicados incluso cuando no importa el orden de la ordenación.

$(word n,text)

Devuelve la n-ésima palabra de text. Los valores válidos de n empiezan en 1. Si n es mayor que el número de palabras de text, el valor es vacío. Por ejemplo,

$(word 2, foo bar baz)

devuelve «bar».

$(wordlist s,e,text)

Devuelve la lista de palabras de text comprendidas desde la palabra s-ésima hasta la e-ésima (ambas incluidas). Los valores válidos de s empiezan en 1, y e puede empezar en 0. Si s es mayor que el número de palabras de text, el valor es vacío. Si e es mayor que el número de palabras de text, se devuelven las palabras hasta el final de text. Si s es mayor que e, no se devuelve nada. Por ejemplo,

$(wordlist 2, 3, foo bar baz)

devuelve «bar baz».

$(words text)

Devuelve el número de palabras de text. Así, la última palabra de text se obtiene con $(word $(words text),text).

$(firstword names…)

El argumento names se considera una secuencia de nombres separados por espacios en blanco. El valor es el primer nombre de esa secuencia. Los nombres restantes se ignoran.

Por ejemplo,

$(firstword foo bar)

produce el resultado «foo». $(firstword text) es lo mismo que $(word 1,text), pero la función firstword se conserva por concisión.

$(lastword names…)

El argumento names se considera una secuencia de nombres separados por espacios en blanco. El valor es el último nombre de esa secuencia.

Por ejemplo,

$(lastword foo bar)

produce el resultado «bar». $(lastword text) es lo mismo que $(word $(words text),text), pero la función lastword se añadió por concisión y por su mejor rendimiento.

He aquí un ejemplo realista del uso de subst y patsubst. Supongamos que un makefile usa la variable VPATH para especificar la lista de directorios en los que make debe buscar los archivos de los prerrequisitos (prerequisite) (véase VPATH: ruta de búsqueda para todos los prerrequisitos). Este ejemplo muestra cómo indicar al compilador de C que busque también los archivos de cabecera en esa misma lista de directorios.

El valor de VPATH es una lista de directorios separados por dos puntos, por ejemplo «src:../headers». Primero se usa la función subst para cambiar los dos puntos por espacios:

$(subst :, ,$(VPATH))

Esto produce «src ../headers». A continuación se usa patsubst para convertir cada nombre de directorio en un indicador «-I». Lo así obtenido puede añadirse al valor de la variable CFLAGS, que se pasa automáticamente al compilador de C, de esta forma:

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

Su efecto es añadir el texto «-Isrc -I../headers» al valor que ya tuviera CFLAGS. Aquí se usa la directiva override para asegurarse de que el nuevo valor se asigne incluso si el valor anterior de CFLAGS se había especificado mediante un argumento de la línea de órdenes (véase La directiva override).

8.3 Funciones para nombres de archivo

Algunas de las funciones de expansión incorporadas tienen que ver especialmente con la descomposición de nombres de archivo o de listas de nombres de archivo.

Cada una de las funciones siguientes realiza una transformación concreta sobre los nombres de archivo. El argumento de la función se considera una secuencia de nombres de archivo separados por espacios en blanco (los espacios en blanco iniciales y finales se ignoran). Cada nombre de archivo de la secuencia se transforma de la misma manera y los resultados se concatenan con un único espacio.

$(dir names…)

Extrae la parte de directorio de cada nombre de archivo de names. La parte de directorio de un nombre de archivo es todo lo que hay hasta la última barra inclusive (incluida la propia barra). Si el nombre de archivo no contiene ninguna barra, la parte de directorio es la cadena «./». Por ejemplo,

$(dir src/foo.c hacks)

produce el resultado «src/ ./».

$(notdir names…)

Extrae de cada nombre de archivo de names lo que queda tras quitar la parte de directorio. Si el nombre de archivo no contiene ninguna barra, queda sin modificar. Si contiene una barra, se elimina todo hasta la última barra inclusive.

Un nombre de archivo que termina en barra se convierte en la cadena vacía. Esto es un inconveniente, pues significa que el número de nombres de archivo separados por espacios del resultado no coincide necesariamente con el número de argumentos. Sin embargo, no se encontró ninguna otra alternativa razonable.

Por ejemplo,

$(notdir src/foo.c hacks)

produce el resultado «foo.c hacks».

$(suffix names…)

Extrae el sufijo de cada nombre de archivo de names. Si el nombre de archivo contiene un punto, el sufijo es todo lo que empieza desde el último punto. Si no lo contiene, el sufijo es la cadena vacía. Por ello, aunque names no esté vacío, es frecuente que el resultado esté vacío, y aunque names contenga varios nombres de archivo, el número de nombres de archivo del resultado puede ser menor.

Por ejemplo,

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

produce el resultado «.c .c».

$(basename names…)

Extrae de cada nombre de archivo de names lo que queda tras quitar el sufijo. Si el nombre de archivo contiene un punto, el basename es todo lo que hay hasta antes del último punto (sin incluir el propio punto). Los puntos que estén en la parte de directorio se ignoran. Si no hay ningún punto, el basename es el nombre de archivo completo. Por ejemplo,

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

produce el resultado «src/foo src-1.0/bar hacks».

$(addsuffix suffix,names…)

El argumento names se considera una secuencia de nombres separados por espacios en blanco. suffix se trata como una sola unidad. El valor de suffix se añade al final de cada uno de los nombres, y los nombres así alargados se concatenan con un único espacio. Por ejemplo,

$(addsuffix .c,foo bar)

produce el resultado «foo.c bar.c».

$(addprefix prefix,names…)

El argumento names se considera una secuencia de nombres separados por espacios en blanco. prefix se trata como una sola unidad. El valor de prefix se antepone a cada uno de los nombres, y los nombres así alargados se concatenan con un único espacio. Por ejemplo,

$(addprefix src/,foo bar)

produce el resultado «src/foo src/bar».

$(join list1,list2)

Concatena los dos argumentos palabra por palabra. Es decir, la concatenación de las dos primeras palabras (una de cada argumento) es la primera palabra del resultado, la concatenación de las dos segundas palabras es la segunda palabra del resultado, y así sucesivamente. Dicho de otro modo, la n-ésima palabra del resultado se forma a partir de la n-ésima palabra de cada argumento. Si uno de los argumentos tiene más palabras que el otro, las palabras sobrantes se copian sin modificar al resultado.

Por ejemplo, «$(join a b,.c .o)» produce «a.c b.o».

Los espacios en blanco entre las palabras de las listas no se conservan, sino que se reemplazan por un único espacio.

Esta función puede usarse para combinar los resultados de las funciones dir y notdir y reconstruir la lista de archivos original que se pasó a esas funciones.

$(wildcard pattern)

El argumento pattern es un patrón de nombre de archivo que, típicamente, contiene caracteres comodín (igual que un patrón de nombre de archivo del shell). El resultado de wildcard es una lista, separada por espacios, de los nombres de los archivos existentes que coinciden con ese patrón. Véase el apartado Uso de caracteres comodín en los nombres de archivo.

$(realpath names…)

Devuelve, para cada nombre de archivo de names, su nombre absoluto canónico. Un nombre canónico no contiene componentes . ni .., ni separadores de ruta (/) duplicados, ni enlaces simbólicos. En caso de fallo, se devuelve la cadena vacía. Para una lista de las posibles causas de fallo, consulte la documentación de realpath(3).

$(abspath names…)

Devuelve, para cada nombre de archivo de names, su nombre absoluto sin componentes . ni .., ni separadores de ruta (/) duplicados. Tenga en cuenta que, a diferencia de la función realpath, abspath no resuelve los enlaces simbólicos, ni es necesario que el nombre de archivo apunte a un archivo o directorio existente. Si desea comprobar si existe, use la función wildcard.

8.4 Funciones para condicionales

Hay cuatro funciones que realizan una expansión condicional. Una característica importante de estas funciones es que no todos sus argumentos se expanden desde el principio. Solo se expanden los argumentos que sea necesario expandir.

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

La función if ofrece una expansión condicional en forma de función (a diferencia de los condicionales de los makefiles de GNU make, como ifeq; véase Sintaxis de los condicionales).

Al primer argumento, condition, se le quitan primero todos los espacios en blanco iniciales y finales, y luego se expande. Si el resultado de la expansión es una cadena no vacía, la condición se considera verdadera. Si se expande a la cadena vacía, la condición se considera falsa.

Si la condición es verdadera, se evalúa el segundo argumento, then-part, y este se usa como resultado de la evaluación del conjunto de la función if.

Si la condición es falsa, se evalúa el tercer argumento, else-part, y este se convierte en el resultado de la función if. Si no hay tercer argumento, la función if no produce nada (queda como la cadena vacía).

Tenga en cuenta que solo se evalúa uno de los dos, then-part o else-part, y que nunca se evalúan ambos. Por ello, cualquiera de los dos puede contener efectos colaterales (como una llamada a la función shell).

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

La función or proporciona una operación OR con «cortocircuito» (short-circuiting). Cada argumento se expande por orden. En cuanto un argumento se expande a una cadena no vacía, el proceso se detiene y ese resultado de la expansión es el valor de retorno. Si tras expandir todos los argumentos todos resultan falsos (vacíos), el resultado de la expansión es la cadena vacía.

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

La función and proporciona una operación AND con «cortocircuito» (short-circuiting). Cada argumento se expande por orden. En cuanto un argumento se expande a la cadena vacía, el proceso se detiene y el resultado de la expansión es la cadena vacía. Si todos los argumentos se expanden a cadenas no vacías, el resultado de la expansión es la expansión del último argumento.

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

La función intcmp permite comparar numéricamente enteros. Esta función no tiene equivalente entre los condicionales de los makefiles de GNU make.

El lado izquierdo, lhs, y el derecho, rhs, se expanden y se analizan como enteros en base decimal. La expansión de los argumentos restantes se controla según cómo se compare numéricamente el lado izquierdo con el derecho.

Si no hay más argumentos, se expande a vacío cuando el lado izquierdo y el derecho son distintos, y se expande a ese valor numérico cuando son iguales.

De lo contrario: si el lado izquierdo es estrictamente menor que el derecho, la función intcmp se evalúa a la expansión del tercer argumento, lt-part. Si ambos lados son iguales, la función intcmp se evalúa a la expansión del cuarto argumento, eq-part. Si el lado izquierdo es estrictamente mayor que el derecho, la función intcmp se evalúa a la expansión del quinto argumento, gt-part.

Si se omite gt-part, su valor predeterminado es eq-part. Si se omite eq-part, su valor predeterminado es la cadena vacía. Así, tanto «$(intcmp 9,7,hello)» como «$(intcmp 9,7,hello,world,)» se evalúan a la cadena vacía, pero «$(intcmp 9,7,hello,world)» (observe que no hay coma después de world) se evalúa a «world».

8.5 La función let

La función let proporciona un medio para limitar el ámbito (scope) de las variables. Una asignación a una variable nombrada dentro de una expresión let solo es válida dentro del texto que proporciona esa expresión let, y dicha asignación no afecta a una variable del mismo nombre que esté en un ámbito exterior.

Además, la función let permite «desempaquetar» una lista asignando todos los valores no asignados a la variable del último nombre.

La sintaxis de la función let es la siguiente:

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

Los dos primeros argumentos, var y list, se expanden antes que ninguna otra cosa. Tenga en cuenta que el último argumento, text, no se expande al mismo tiempo. A continuación, cada palabra del valor expandido de list se vincula por orden a cada uno de los nombres de variable var, y al último nombre de variable se le vincula todo el resto de list tras la expansión. Dicho de otro modo: la primera palabra de list se vincula a la primera variable var, la segunda palabra a la segunda variable var, y así sucesivamente.

Si el número de nombres de variable var es mayor que el número de palabras de list, a los nombres de variable var sobrantes se les asigna la cadena vacía. Si el número de var es menor que el número de palabras de list, al último var se le asignan todas las palabras restantes de list.

Cada variable var se asigna como variable de expansión simple durante la ejecución de let. Véase Los dos tipos de variables.

Una vez vinculadas todas las variables de esta forma, se expande text, y eso constituye el resultado de la función let.

Por ejemplo, la siguiente macro invierte el orden de las palabras de la lista que se le da como primer argumento:

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

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

Esto muestra a b c d. En la primera llamada, let expande $1 a d c b a. Luego asigna a first el valor d y a rest el valor c b a. A continuación expande la sentencia if, donde, como $(rest) no está vacío, se llama recursivamente a la función reverse con el valor de rest, que ahora es c b a. El let llamado recursivamente asigna a first el valor c y a rest el valor b a. Esta recursión continúa hasta que se llama a let con un único valor, a. En ese momento first es a y rest está vacío, de modo que no hay más recursión y simplemente se expande $(first) a a y se regresa; a eso se le añade b, y así sucesivamente.

Una vez terminada la llamada a reverse, las variables first y rest ya no están definidas. Aunque hubieran existido previamente variables con esos nombres, no se ven afectadas por la expansión de la macro reverse.

8.6 La función foreach

La función foreach se parece a la función let, pero difiere bastante de las demás funciones. Usa un mismo texto repetidamente y, cada vez, le aplica una sustitución distinta. La función foreach se parece mucho a la orden for del shell sh y a la orden foreach del shell de C, csh.

La sintaxis de la función foreach es la siguiente:

$(foreach var,list,text)

Los dos primeros argumentos, var y list, se expanden antes que ninguna otra cosa. Tenga en cuenta que el último argumento, text, no se expande al mismo tiempo. Después, para cada palabra del valor expandido de list, se asigna esa palabra a la variable cuyo nombre indica el valor expandido de var y se expande text. Como text probablemente contiene referencias a esa variable, el resultado de la expansión será distinto cada vez.

Como resultado, text se expande tantas veces como palabras separadas por espacios en blanco haya en list. Las múltiples expansiones de text se concatenan, intercalando espacios entre ellas, y eso constituye el resultado de foreach.

El siguiente ejemplo sencillo asigna a la variable «files» la lista de todos los archivos que hay en cada uno de los directorios de la lista «dirs»:

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

Aquí text es «$(wildcard $(dir)/*)». En la primera iteración se encuentra «a» como valor de dir, por lo que produce el mismo resultado que «$(wildcard a/*)». La segunda iteración produce el resultado de «$(wildcard b/*)» y la tercera el de «$(wildcard c/*)».

Este ejemplo da el mismo resultado (salvo por el hecho de asignar «dirs») que el siguiente:

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

Cuando text es complejo, se puede mejorar la legibilidad dándole un nombre mediante una variable adicional:

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

Aquí se usa así la variable find_files. Se la define como variable de expansión recursiva mediante un «=» simple para que su valor contenga la llamada a función real que debe reexpandirse bajo el control de foreach. Una variable de expansión simple no serviría, pues en ese caso wildcard se llamaría una sola vez, en el momento de definir find_files.

Al igual que la función let, la función foreach no tiene ningún efecto permanente sobre la variable var. El valor y el tipo (flavor) de var tras la llamada a la función foreach son los mismos que antes de la llamada. Los demás valores tomados de list solo son válidos temporalmente, durante la ejecución de foreach. La variable var es una variable de expansión simple durante la ejecución de foreach. Si var estaba indefinida antes de la llamada a la función foreach, sigue indefinida después de la llamada. Véase Los dos tipos de variables.

Hay que tener cuidado al usar expresiones de variable complejas que generan nombres de variable, pues muchas cadenas extrañas pueden ser nombres de variable válidos, lo cual probablemente no sea lo que se pretendía. Por ejemplo,

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

podría ser útil si el valor de find_files hace referencia a la variable cuyo nombre es «Esta-escrito-en-espanol!» (es un nombre bastante largo, ¿no?), pero lo más probable es que sea un error.

8.7 La función file

La función file permite escribir en archivos desde un makefile y leer de archivos. Para la escritura se admiten dos modos. Uno es «sobrescribir», en el que el texto se escribe desde el principio del archivo y se pierde todo el contenido existente. El otro es «añadir», en el que el texto se escribe al final del archivo y se conserva el contenido existente. En ambos casos, si el archivo no existe, se crea. Si no se puede abrir el archivo para escritura, o si la operación de escritura falla, se produce un error fatal. Al escribir en un archivo, la función file se expande a la cadena vacía.

Al leer de un archivo, la función file se expande al contenido tal cual de ese archivo (eliminando, eso sí, el último salto de línea si lo hay). Si se intenta leer de un archivo que no existe, se expande a la cadena vacía.

La sintaxis de la función file es la siguiente:

$(file op filename[,text])

Cuando se evalúa la función file, primero se expanden todos sus argumentos y luego se abre el archivo indicado por filename en el modo que describe op.

El operador op puede ser >, que indica sobrescribir el archivo con un nuevo contenido; >>, que indica añadir al contenido actual del archivo; o <, que indica leer el contenido del archivo. filename especifica el archivo de destino de la escritura o el origen de la lectura. Entre el operador y el nombre de archivo se pueden poner espacios en blanco.

Es un error proporcionar un valor para text cuando se lee de un archivo.

Al escribir en un archivo, text se escribe en el archivo. Si text no termina ya en un salto de línea, al final se escribe un salto de línea (incluso si text es la cadena vacía). Si no se proporciona ningún argumento text, no se escribe nada.

Por ejemplo, la función file es útil cuando el sistema de construcción tiene un límite en la longitud de la línea de órdenes y la orden que se ejecuta en la receta puede recibir sus argumentos desde un archivo. Muchas órdenes siguen la convención de que un argumento precedido de @ apunta a un archivo que contiene más argumentos. En ese caso, la receta puede escribirse así:

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

Si la orden exige que cada argumento esté en una línea distinta del archivo de entrada, la receta puede escribirse así:

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

8.8 La función call

La función call es única en el sentido de que puede usarse para crear nuevas funciones parametrizadas. Se escribe una expresión compleja como valor de una variable y, mediante call, se la puede expandir con valores distintos.

La sintaxis de la función call es la siguiente:

$(call variable,param,param,…)

Cuando make expande esta función, asigna cada param a las variables temporales $(1), $(2), etc. La variable $(0) contiene variable. No hay un límite superior para el número de argumentos de parámetro. Tampoco hay un límite inferior, pero no tiene sentido usar call sin parámetros.

Entonces variable se expande como variable de make bajo esas asignaciones temporales. Por lo tanto, una referencia a $(1) dentro del valor de variable se resuelve al primer param de esa llamada a call.

Tenga en cuenta que variable es el nombre de la variable, no una referencia a ella. Por ello, al escribirlo no se suelen usar ni «$» ni paréntesis (aunque sí se puede usar una referencia de variable dentro del nombre si no se quiere que este sea constante).

Si variable es el nombre de una función incorporada, siempre se llama a la función incorporada, aun cuando exista una variable de make con el mismo nombre.

La función call expande los argumentos param antes de asignarlos a las variables temporales. Por ello, un valor de variable que contenga referencias a funciones incorporadas con reglas de expansión especiales, como foreach o if, puede no funcionar como se espera.

Con unos cuantos ejemplos se entenderá mejor.

La siguiente macro simplemente invierte el orden de sus argumentos:

reverse = $(2) $(1)

foo = $(call reverse,a,b)

Aquí foo contiene «b a».

Este otro es un poco más interesante. Define una macro que busca dónde se encuentra por primera vez un programa dentro de PATH:

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

LS := $(call pathsearch,ls)

Con esto la variable LS contiene /bin/ls o algo similar.

La función call puede anidarse. Cada llamada recursiva tiene sus propios valores locales de $(1), etc., que ocultan los valores de la call de nivel superior. Por ejemplo, lo siguiente es la implementación de una función map:

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

Con esto, una función que normalmente toma un solo argumento (por ejemplo, origin) puede aplicarse con map a varios valores a la vez:

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

Como resultado, o contiene algo como «file file default».

Una última advertencia. Tenga cuidado al añadir espacios en blanco a los argumentos de call. Como en las demás funciones, los espacios en blanco que contengan el segundo argumento y los siguientes se conservan, y esto puede provocar resultados extraños. En general, lo más seguro al pasar parámetros a call es eliminar todos los espacios en blanco superfluos.

8.9 La función value

La función value proporciona un medio para usar el valor de una variable sin expandirlo. Eso sí, tenga en cuenta que no deshace una expansión que ya se haya realizado. Por ejemplo, si se crea una variable de expansión simple, su valor se expande en el momento de la definición, de modo que en ese caso la función value devuelve el mismo resultado que usar la variable directamente.

La sintaxis de la función value es la siguiente:

$(value variable)

Tenga en cuenta que variable es el nombre de la variable, no una referencia a ella. Por ello, al escribirlo no se suelen usar ni «$» ni paréntesis (aunque sí se puede usar una referencia de variable dentro del nombre si no se quiere que este sea constante).

El resultado de esta función es una cadena que contiene el valor de variable tal cual, sin realizar ninguna expansión. Por ejemplo, en el siguiente makefile:

FOO = $PATH

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

La salida de la primera línea es ATH. Esto se debe a que «$P» se expande como variable de make. La salida de la segunda línea, en cambio, es el valor actual de su variable de entorno $PATH. Esto se debe a que la función value evitó la expansión.

La función value se usa la mayoría de las veces combinada con la función eval (véase La función eval).

8.10 La función eval

La función eval es muy especial. Permite definir nuevos elementos de sintaxis de makefile no constantes, es decir, elementos que resultan de evaluar otras variables y funciones. El argumento de la función eval se expande primero y, a continuación, el resultado de esa expansión se analiza como sintaxis de makefile. El resultado de la expansión puede definir nuevas variables de make, objetivos, reglas implícitas o explícitas, etc.

El resultado de la función eval es siempre la cadena vacía. Por ello, puede colocarse casi en cualquier lugar de un makefile sin provocar errores de sintaxis.

Aquí lo importante es comprender que el argumento de eval se expande dos veces: la primera, por la propia función eval, y la segunda, cuando el resultado de esa expansión se analiza como sintaxis de makefile y se vuelve a expandir. Es decir, al usar eval puede ser necesario añadir niveles adicionales de escape por culpa del carácter «$». En esas situaciones, la función value (véase La función value) puede resultar útil para evitar una expansión no deseada.

He aquí un ejemplo de cómo puede usarse eval. Este ejemplo combina varios conceptos y otras funciones. En él, usar eval en lugar de escribir las reglas directamente puede parecer exagerado, pero piense en dos cosas. Primero, la definición de la plantilla (dentro de PROGRAM_template) podría tener que ser mucho más compleja que la que se muestra aquí. Segundo, la parte compleja y «genérica» de este ejemplo puede ponerse en otro makefile e incluirse en todos los makefiles individuales. De ese modo, cada makefile individual queda muy limpio.

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

# A partir de aquí todo es la 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 La función origin

La función origin, a diferencia de la mayoría de las demás funciones, no manipula el valor de una variable. En su lugar, informa acerca de una variable. Concretamente, indica de dónde proviene esa variable.

La sintaxis de la función origin es la siguiente:

$(origin variable)

Tenga en cuenta que variable es el nombre de la variable que se consulta, no una referencia a ella. Por ello, al escribirlo no se suelen usar ni «$» ni paréntesis (aunque sí se puede usar una referencia de variable dentro del nombre si no se quiere que este sea constante).

El resultado de esta función es una cadena que indica cómo se definió la variable variable:

«undefined»

Si variable no se ha definido nunca.

«default»

Si variable tiene una definición predeterminada, como CC y otras. Véase Variables usadas por las reglas implícitas. Tenga en cuenta que, si se redefine una variable predeterminada, la función origin devuelve el origen de la definición posterior.

«environment»

Si variable se ha heredado del entorno proporcionado a make.

«environment override»

Si variable se ha heredado del entorno proporcionado a make y, como consecuencia de la opción «-e» (véase Resumen de las opciones), anula la definición de variable hecha dentro del makefile.

«file»

Si variable se ha definido dentro de un makefile.

«command line»

Si variable se ha definido en la línea de órdenes.

«override»

Si variable se ha definido con una directiva override dentro de un makefile (véase La directiva override).

«automatic»

Si variable es una variable automática definida para la ejecución de la receta de cada regla (véase Variables automáticas).

Esta información (dejando aparte la mera curiosidad) sirve, sobre todo, para decidir si se puede confiar en el valor de una variable. Por ejemplo, supongamos que se tiene un makefile foo que incluye otro makefile bar. Al ejecutar la orden «make -f bar», se desea que la variable bletch se defina dentro de bar aunque el entorno contenga una definición de bletch. Sin embargo, si foo ya había definido bletch antes de incluir bar, no se desea anular esa definición. Esto podría lograrse usando una directiva override dentro de foo, dando a esa definición prioridad sobre la definición posterior de bar. Por desgracia, la directiva override anularía también las definiciones de la línea de órdenes. Por eso, convendría incluir en bar lo siguiente:

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

Si bletch se había definido desde el entorno, esto la redefine.

Si lo que se quiere es anular una definición anterior de bletch que provenga del entorno incluso bajo «-e», se puede escribir, en su lugar, lo siguiente:

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

Aquí la redefinición se produce si «$(origin bletch)» devuelve «environment» o «environment override». Véase Funciones para la sustitución y el análisis de cadenas.

8.12 La función flavor

La función flavor, al igual que la función origin, no manipula el valor de una variable, sino que informa acerca de ella. Concretamente, indica el tipo (flavor) de la variable (véase Los dos tipos de variables).

La sintaxis de la función flavor es la siguiente:

$(flavor variable)

Tenga en cuenta que variable es el nombre de la variable que se consulta, no una referencia a ella. Por ello, al escribirlo no se suelen usar ni «$» ni paréntesis (aunque sí se puede usar una referencia de variable dentro del nombre si no se quiere que este sea constante).

El resultado de esta función es una cadena que indica el tipo de la variable variable:

«undefined»

Si variable no se ha definido nunca.

«recursive»

Si variable es una variable de expansión recursiva.

«simple»

Si variable es una variable de expansión simple.

8.13 Funciones que controlan make

Estas funciones controlan el modo en que actúa make. En general, se usan para dar información al usuario del makefile o para detener make cuando se detecta algún error en el entorno.

$(error text…)

Genera un error fatal cuyo mensaje es text. Tenga en cuenta que el error se genera siempre que se evalúa esta función. Por ello, si se coloca dentro de una receta o en el lado derecho de la asignación de una variable de expansión recursiva, se evaluará más tarde. text se expande antes de generar el error.

Por ejemplo,

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

genera un error fatal durante la lectura del makefile si la variable de make ERROR1 está definida. O bien,

ERR = $(error found an error!)

.PHONY: err
err: ; $(ERR)

genera un error fatal durante la ejecución de make si se invoca el objetivo err.

$(warning text…)

Esta función actúa igual que la función error de arriba, con la diferencia de que make no termina. En su lugar, se expande text y se muestra el mensaje resultante, pero el procesamiento del makefile continúa.

El resultado de la expansión de esta función es la cadena vacía.

$(info text…)

Esta función no hace nada salvo mostrar su argumento (ya expandido) por la salida estándar. No se añaden el nombre del makefile ni el número de línea. El resultado de la expansión de esta función es la cadena vacía.

8.14 La función shell

La función shell, salvo por la función wildcard (véase La función wildcard), se diferencia de todas las demás funciones. La razón es que interactúa con el mundo exterior a make.

La función shell proporciona a make la misma funcionalidad que las comillas invertidas («`») de la mayoría de los shells, es decir, realiza una expansión de orden. Esto significa que toma como argumento una orden del shell y se expande a la salida de esa orden. Lo único que make hace con ese resultado es convertir cada salto de línea (o cada combinación de retorno de carro y salto de línea) en un único espacio. Si hay un (retorno de carro y) salto de línea al final, simplemente se elimina.

La orden ejecutada por una llamada a la función shell se ejecuta en el momento en que se expande esa llamada a función (véase Cómo lee make un makefile). Como esta función conlleva el arranque de un nuevo shell, conviene sopesar bien el impacto en el rendimiento de usar la función shell dentro de una variable de expansión recursiva frente a usarla dentro de una variable de expansión simple (véase Los dos tipos de variables).

Como alternativa a la función shell existe el operador de asignación «!=». Tiene un comportamiento parecido, pero con diferencias sutiles (véase Asignación de variables). El operador de asignación «!=» está incluido en el estándar POSIX más reciente.

Tras usar la función shell o el operador de asignación «!=», su estado de salida se guarda en la variable .SHELLSTATUS.

He aquí algunos ejemplos de uso de la función shell:

contents := $(shell cat foo)

Esto asigna a contents el contenido del archivo foo, con cada línea separada por un espacio (en lugar de por un salto de línea).

files := $(shell echo *.c)

Esto asigna a files el resultado de la expansión de «*.c». A menos que make esté usando un shell muy peculiar, esto da el mismo resultado que «$(wildcard *.c)» (siempre que exista al menos un archivo «.c»).

Todas las variables marcadas para export se pasan también al shell que arranca la función shell. Esto puede dar lugar a un bucle de expansión de variables. Considere el siguiente makefile:

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

Cuando make intenta ejecutar la receta, debe añadir la variable HI al entorno. Para ello necesita expandir HI. El valor de esta variable requiere una llamada a la función shell, y para invocarla hay que construir su entorno. Como HI está exportada, construir ese entorno requiere expandir HI. Y así indefinidamente. En este caso confuso, make, en lugar de caer en un bucle o producir un error, usa el valor de esa variable del entorno proporcionado a make, y la cadena vacía si no existe. Esto suele ser lo que se desea. Por ejemplo:

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

No obstante, en este caso sería más sencillo y eficiente usar desde el principio una variable de expansión simple («:=»).

8.15 La función guile

Si GNU make se ha compilado con soporte para GNU Guile como lenguaje de extensión incorporado, está disponible la función guile. La función guile toma un argumento que make expande primero de la forma habitual y que luego se pasa al evaluador de GNU Guile. El resultado del evaluador se convierte en una cadena y se usa como resultado de la expansión de la función guile dentro del makefile. Para más detalles sobre cómo escribir extensiones de make en Guile, véase Integración con GNU Guile.

Se puede determinar si el soporte para GNU Guile está disponible comprobando si la palabra «guile» está presente en la variable .FEATURES.


Anterior | Siguiente | Índice | Original en inglés (gnu.org)