Diferencia entre revisiones de «Make»

De Taquiones
mSin resumen de edición
Línea 133: Línea 133:
# y ahora queremos excluir algunos
# y ahora queremos excluir algunos
ARCHIVOS_FINALES=$(filter-out %.bak %.orig %.ptkdb, $(TODOS_LOS_ARCHIVOS))
ARCHIVOS_FINALES=$(filter-out %.bak %.orig %.ptkdb, $(TODOS_LOS_ARCHIVOS))

=== Procedencia de las variables ===

* Ref: Managing Projects with GNU/Make: 3.6 Where Variables Come From

Las variables pueden proceder de las siguientes fuentes:

; Archivos
: Bien definidas directamente en el archivo fuente o bien incorporadas desde otros empleando la directiva <code>include</code>.
; Línea de órdenes
: Las variables pueden ser definidas o redefinidas directamente en la línea de órdenes del programa ''make'' (<code>make CFLAGS=-g</code> por ejemplo) empleando para ello el símbolo igual (''='') entre el nombre y el valor.
: Estas asignaciones '''tienen prioridad''' sobre el entorno del proceso y las definiciones del fuente. Para contrarrestarlo existe la directiva <code>override</code>.
; Entorno del proceso
: Todas las variables de entorno del proceso son creadas automáticamente como variables ''make'' con muy baja precedencia, aunque se puede invertir esta característica -consiguiendo que prevalezcan sobre las definidas en el fuente- empleando el parámetro <code>-e</code> del programa ''make''.
; De la ejecución de make
: Antes de ejecutar los programas de una regla ''make'' asigna automáticamente un buen número de valores a un grupo de variables que pueden emplearse en la llamada a dichos programas.

=== Destinos falsos (''phony targets'') ===
Un destino falso es aquél que no es el nombre de un archivo. Es únicamente un nombre para que algunos programas se ejecuten cuando se solicite explícitamente.

Entran dentro de esa categoría aquellas reglas que no crean archivos, como el siguiente ejemplo para limpiar arhchivos de trabajo
clean:
rm -f tmp/* *.o
y que por tanto serían ejecutadas '''siempre''' que el destino surja para ser reconstruído. Por un lado sería necesario invocar al programa con algo como <code>make clean</code> para que se efectúe la limpieza, pero por otra parte si se crease un archivo llamado ''clean'' en el directorio este mecanismo sería saltado en cada ocasión.

Para evitarlo existe un palabra clave llamada (<code>.PHONY</code>) con la que se puede marcar el objetivo de la regla y así mejorar el rendimiento puesto que el programa ''make'' ya sabe que debe ignorar esas reglas al buscar archivos que se reconstruyan en otro sitio.
.PHONY: clean
clean:
rm -f tmp/* *.o

== Recetario ==

=== Asignación de valores a variables ===

* Ref: Managing Projects with GNU/Make: 3.2 Variable Types

La siguiente tabla muestra un pequeño resumen de los tipos de asignación de valores a variables dentro de un archivo ''make'':
{| class="wikitable"
|+
!Tipo de asignación
!Operador
!Ejemplo
!Descripción
|-
!Simple
|:=
|
MAKE_DEPEND := $(CC) -M
|La asignación simple consiste en evaluar el lado derecho de la expresión en el momento de encontrárselo en el archivo fuente y asignárselo una sola vez a la variable. Si en el lado derecho se hace referencia a algo no definido en ese punto quedará asignado como un valor vacío a la variable.

Si en el ejemplo la variable ''CC'' no tuviese ningún valor expandiría a un valor vacío y la variable ''MAKE_DEPEND'' quedaría como <code>ESPACIO-M</code> ya que no se eliminan los espacios en blanco de la expresión resultante.
|-
!Recursiva
|=
|
MAKE_DEPEND = $(CC) -M
|La asignación recursiva se llama así porque cada vez que se hace referencia al lado izquierdo de la asignación (la variable ''MAKE_DEPEND'' en este caso) se vuelve a evaluar el lado derecho, por lo que en esta expresión se pueden hacer referencias a variables que aún no tengan un valor.

Conviene tener en cuenta este tipo de comportamientos si se le asigna a una variable la ejecución de un programa porque éste será invocado cada vez que la expresión sea evaluada, y esto puede tener efectos colaterales inesperados o bajadas de rendimiento.
|-
!Condicional
|?=
|
PERL_DEBUGGER ?= /usr/bin/perl -d:ptkdb
|La asignación se produce únicamente si la variable '''no tiene''' ya un valor asignado. Este mecanismo funciona muy bien con variables de entorno.
|-
!Anexada
| +=
|
PERL_DEBUGGER += $(PROGRAM_PARAMS)
|Este operador sirve para añadir contenido a una variable evitando los bucles infinitos cuando se emplean expresiones de asignación recursivas.

En concreto si se usa algo como lo siguiente:
RECURSIVA = Valor inicial
RECURSIVA = $(RECURSIVA) Otros valores
obtendremos un error como el que sigue:
makefile:2: *** Recursive variable `RECURSIVA' references itself (eventually). Stop.
|}

=== Objetivos múltiples ===
Una regla con varios objetivos a cumplir es equivalente a escribir varias reglas con un único objetivo e idénticos prerrequisitos.
OBJETIVO OBJETIVO ... OBJETIVO : PRERREQUISITOS
[PROGRAMA $@ ]
...
Como se pueden emplear funciones y variables en los programas a utilizar, los resultados pueden variar de uno a otro según el objetivo a cumplir.

Como ejemplo tenemos:
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
Si lo que necesitamos son listas de requisitos variables debemos emplear patrones estáticos.

=== Reglas de patrones estáticos ===
Estas reglas se caracterizan por especificar objetivos múltiples y por construir los nombres de los prerrequisitos para cada objetivo basándose en el nombre del mismo.
LISTA_DE_OBJETIVOS ... : PATRON_DE_OBJETIVOS : PATRON_DE_PRERREQUISITOS
PROGRAMA
...
''LISTA_DE_OBJETIVOS'' describe los objetivos a los que se aplica la regla y puede contener caracteres comodín. ''PATRON_DE_OBJETIVOS'' y ''PATRON_DE_PRERREQUISITOS'' son expresiones que indican cómo calcular los prerequisitos para cada objetivo.

Cada objetivo se empareja con el patrón de objetivos y se extrae una parte del nombre llamada ''raíz'' y representada por el carácter porcentaje (<code>%</code>). Esta raíz se sustituye entonces en cada uno de los patrones de prerrequisitos de la regla por el mismo carácter porcentaje para crear la lista de prerrequisitos, aunque es válido que no exista dicha sustitución y los prerrequisitos se tomen literalmente.

Ejemplo de manual para compilar dos archivos fuente:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
cc -c $< -o $@
La variable <code>$<</code> se sustituye por el nombre del prerrequisito mientras que la variable <code>$@</code> contiene el nombre del objetivo. El resultado son estas dos invocaciones:
cc -c foo.c -o foo.o
cc -c bar.c -o bar.o
Es decir, por cada uno de los objetivos de la regla hacemos lo siguiente:

[[Categoría:Programa]]
[[Categoría:Programa]]
[[Categoría:Referencias]]
[[Categoría:Referencias]]

Revisión del 11:22 19 ene 2024

Enlaces y referencias

  • Ref: GNU make manual
  • Ref: Managing Projects with GNU make
  • Ref: These are not static pattern rules ...

Conceptos básicos

Reglas

Una regla indica cuándo y cómo construir ciertos archivos.

OBJETIVOS : PRERREQUISITOS
    PROGRAMA
    ...

Los OBJETIVOS son los archivos que se deben construir si no existen o si los archivos listados en los PRERREQUISITOS a su vez tampoco existen o han sido modificados con fecha posterior a la de los OBJETIVOS. La construcción se lleva a cabo ejecutando cada uno de los programas mencionados en líneas independientes.

Variables automáticas

  • Sus valores se calculan de nuevo en cada regla ejecutada.
  • Estos valores sólo pueden emplearse en la ejecución de programas (command script).
  • No pueden usarse en la lista de objetivos de una regla.
  • Tampoco pueden emplearse en la lista de prerrequisitos de una regla.
Variable Descripción Comentarios
$@ Nombre de archivo del objetivo de la regla
  • Si el objetivo es un miembro de un archivo esta variable contendrá el nombre de dicho archivo.
  • Si la regla tiene objetivos múltiples la variable contendrá el nombre de cualquier objetivo que motivó que se ejecutasen los programas de la regla.
$% Nombre del miembro objetivo cuando el objetivo es un miembro de un archivo
$< Nombre del primer prerrequisito.
$? Nombres de todos los prerrequisitos que son más nuevos que el objetivo (separados por espacios en blanco).
$^ Nombre de todos los prerrequisitos (separados por espacios en blanco).
  • La lista de nombres no incluye duplicados.
$+ Igual que el anterior ($^) pero se incluyen todos los nombres en el orden en el que aparecen en el archivo Makefile.
$| Nombre de todos los prerrequisitos
$* Raíz con la que una regla implícita coincide.

Ejemplo:

#  Objetivo de la regla     Prerequisito
#      ------v-------        ----v----
$(HOME)/.myarchivo.conf:   myarchivo.conf
    install $? $@

Funciones

  • Ref: Builting functions

A menos que se diga lo contrario toda lista está formada por textos separados por espacios en blanco.

Análisis y substitución en textos
Función Descripción
$(subst BUS,REEM,DONDE) Busca BUS en DONDE y lo reemplaza por REEM.
$(filter PATRONES, TEXTO) Encuentra todas las palabras (separadas por espacios en blanco) en TEXTO que emparejan con alguno de los PATRONES y eliminando aquellas palabras que no lo hacen.

Los patrones se escriben empleando el carácter porcentaje (%).

$(filter-out PATRONES, TEXTO) Devuelve todas las palabras encontradas en TEXTO que no emparejan con algún PATRON, eliminando de TEXTO aquellas que lo hacen con uno o más de ellos.

Es la función opuesta a la función $(filter ... ).

Nombres de archivos
Función Descripción
$(dir RUTAS) Extrae la parte correspondiente al directorio de cada una de las RUTAS de archivo recibidas.
$(notdir RUTAS) Extrae la parte correspondiente al nombre de cada una de las RUTAS de archivo recibidas.
$(basename RUTAS) Extrae el nombre y la ruta de cada una de las RUTAS de archivos recibidas dejando fuera la extensión de los mismos.
$(addsuffix SUFIJO,NOMBRES) Añade el texto SUFIJO al final de cada uno de los nombres recibidos.
$(addprefix PREFIJO,NOMBRES) Añade el texto PREFIJO al comienzo de cada uno de los nombres recibidos.
$(wildcard PATRON) Crea una lista de nombres de archivos empleando PATRON como expresión de

búsqueda.

Ejecutando programas
Función Descripción
$(shell TEXTO) Esta función toma un TEXTO como una línea de órdenes, la pasa a un shell externo y recupera su salida para utilizarla como producto de su llamada.

Ejemplos

Estos son algunos ejemplos de las funciones arriba citadas:

# Queremos encontrar todos los archivos de un directorio
TODOS_LOS_ARCHIVOS=$(wildcard mydir/*)

# y ahora queremos excluir algunos 
ARCHIVOS_FINALES=$(filter-out %.bak %.orig %.ptkdb, $(TODOS_LOS_ARCHIVOS))

Procedencia de las variables

  • Ref: Managing Projects with GNU/Make: 3.6 Where Variables Come From

Las variables pueden proceder de las siguientes fuentes:

Archivos
Bien definidas directamente en el archivo fuente o bien incorporadas desde otros empleando la directiva include.
Línea de órdenes
Las variables pueden ser definidas o redefinidas directamente en la línea de órdenes del programa make (make CFLAGS=-g por ejemplo) empleando para ello el símbolo igual (=) entre el nombre y el valor.
Estas asignaciones tienen prioridad sobre el entorno del proceso y las definiciones del fuente. Para contrarrestarlo existe la directiva override.
Entorno del proceso
Todas las variables de entorno del proceso son creadas automáticamente como variables make con muy baja precedencia, aunque se puede invertir esta característica -consiguiendo que prevalezcan sobre las definidas en el fuente- empleando el parámetro -e del programa make.
De la ejecución de make
Antes de ejecutar los programas de una regla make asigna automáticamente un buen número de valores a un grupo de variables que pueden emplearse en la llamada a dichos programas.

Destinos falsos (phony targets)

Un destino falso es aquél que no es el nombre de un archivo. Es únicamente un nombre para que algunos programas se ejecuten cuando se solicite explícitamente.

Entran dentro de esa categoría aquellas reglas que no crean archivos, como el siguiente ejemplo para limpiar arhchivos de trabajo

clean: 
   rm -f tmp/* *.o

y que por tanto serían ejecutadas siempre que el destino surja para ser reconstruído. Por un lado sería necesario invocar al programa con algo como make clean para que se efectúe la limpieza, pero por otra parte si se crease un archivo llamado clean en el directorio este mecanismo sería saltado en cada ocasión.

Para evitarlo existe un palabra clave llamada (.PHONY) con la que se puede marcar el objetivo de la regla y así mejorar el rendimiento puesto que el programa make ya sabe que debe ignorar esas reglas al buscar archivos que se reconstruyan en otro sitio.

.PHONY: clean
clean: 
   rm -f tmp/* *.o

Recetario

Asignación de valores a variables

  • Ref: Managing Projects with GNU/Make: 3.2 Variable Types

La siguiente tabla muestra un pequeño resumen de los tipos de asignación de valores a variables dentro de un archivo make:

Tipo de asignación Operador Ejemplo Descripción
Simple :=
MAKE_DEPEND := $(CC) -M
La asignación simple consiste en evaluar el lado derecho de la expresión en el momento de encontrárselo en el archivo fuente y asignárselo una sola vez a la variable. Si en el lado derecho se hace referencia a algo no definido en ese punto quedará asignado como un valor vacío a la variable.

Si en el ejemplo la variable CC no tuviese ningún valor expandiría a un valor vacío y la variable MAKE_DEPEND quedaría como ESPACIO-M ya que no se eliminan los espacios en blanco de la expresión resultante.

Recursiva =
MAKE_DEPEND = $(CC) -M
La asignación recursiva se llama así porque cada vez que se hace referencia al lado izquierdo de la asignación (la variable MAKE_DEPEND en este caso) se vuelve a evaluar el lado derecho, por lo que en esta expresión se pueden hacer referencias a variables que aún no tengan un valor.

Conviene tener en cuenta este tipo de comportamientos si se le asigna a una variable la ejecución de un programa porque éste será invocado cada vez que la expresión sea evaluada, y esto puede tener efectos colaterales inesperados o bajadas de rendimiento.

Condicional ?=
PERL_DEBUGGER ?= /usr/bin/perl -d:ptkdb
La asignación se produce únicamente si la variable no tiene ya un valor asignado. Este mecanismo funciona muy bien con variables de entorno.
Anexada +=
PERL_DEBUGGER += $(PROGRAM_PARAMS)
Este operador sirve para añadir contenido a una variable evitando los bucles infinitos cuando se emplean expresiones de asignación recursivas.

En concreto si se usa algo como lo siguiente:

RECURSIVA = Valor inicial
RECURSIVA = $(RECURSIVA) Otros valores 

obtendremos un error como el que sigue:

makefile:2: *** Recursive variable `RECURSIVA' references itself (eventually).  Stop.

Objetivos múltiples

Una regla con varios objetivos a cumplir es equivalente a escribir varias reglas con un único objetivo e idénticos prerrequisitos.

OBJETIVO OBJETIVO ... OBJETIVO : PRERREQUISITOS 
    [PROGRAMA $@ ]
    ... 

Como se pueden emplear funciones y variables en los programas a utilizar, los resultados pueden variar de uno a otro según el objetivo a cumplir.

Como ejemplo tenemos:

bigoutput littleoutput : text.g
    generate text.g -$(subst output,,$@) > $@

Si lo que necesitamos son listas de requisitos variables debemos emplear patrones estáticos.

Reglas de patrones estáticos

Estas reglas se caracterizan por especificar objetivos múltiples y por construir los nombres de los prerrequisitos para cada objetivo basándose en el nombre del mismo.

LISTA_DE_OBJETIVOS ... : PATRON_DE_OBJETIVOS : PATRON_DE_PRERREQUISITOS 
   PROGRAMA 
   ...

LISTA_DE_OBJETIVOS describe los objetivos a los que se aplica la regla y puede contener caracteres comodín. PATRON_DE_OBJETIVOS y PATRON_DE_PRERREQUISITOS son expresiones que indican cómo calcular los prerequisitos para cada objetivo.

Cada objetivo se empareja con el patrón de objetivos y se extrae una parte del nombre llamada raíz y representada por el carácter porcentaje (%). Esta raíz se sustituye entonces en cada uno de los patrones de prerrequisitos de la regla por el mismo carácter porcentaje para crear la lista de prerrequisitos, aunque es válido que no exista dicha sustitución y los prerrequisitos se tomen literalmente.

Ejemplo de manual para compilar dos archivos fuente:

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
    cc -c $< -o $@

La variable $< se sustituye por el nombre del prerrequisito mientras que la variable $@ contiene el nombre del objetivo. El resultado son estas dos invocaciones:

cc -c foo.c -o foo.o
cc -c bar.c -o bar.o

Es decir, por cada uno de los objetivos de la regla hacemos lo siguiente: