Si desea ejecutar o actualizar una tarea cuando se actualicen ciertos archivos, el make
utilidad puede ser útil. El make
La utilidad requiere un archivo, Makefile
(o makefile
), que define el conjunto de tareas a ejecutar. Puede que hayas usado make
para compilar un programa a partir del código fuente. La mayoría de los proyectos de código abierto usan make
para compilar un binario ejecutable final, que luego se puede instalar usando make install
.
En este artículo, exploraremos make
y Makefile
utilizando ejemplos básicos y avanzados. Antes de comenzar, asegúrese de que make
está instalado en su sistema.
Ejemplos básicos
Empecemos imprimiendo el clásico "Hello World" en el terminal. Crea un directorio vacío myproject
que contiene un archivo Makefile
con este contenido:
say_hello:
echo "Hello World"
Ahora ejecute el archivo escribiendo make
dentro del directorio myproject
. La salida será:
$ make
echo "Hello World"
Hello World
En el ejemplo anterior, say_hello
se comporta como un nombre de función, como en cualquier lenguaje de programación. Esto se llama el objetivo . Los requisitos previos o dependencias seguir el objetivo. En aras de la simplicidad, no hemos definido ningún requisito previo en este ejemplo. El comando echo "Hello World"
se llama la receta . La receta usa requisitos previos para hacer un objetivo . El objetivo, los requisitos previos y las recetas forman una regla .
Para resumir, a continuación se muestra la sintaxis de una regla típica:
target: prerequisites
<TAB> recipe
Como ejemplo, un destino podría ser un archivo binario que depende de los requisitos previos (archivos de origen). Por otro lado, un requisito previo también puede ser un destino que depende de otras dependencias:
final_target: sub_target final_target.c
Recipe_to_create_final_target
sub_target: sub_target.c
Recipe_to_create_sub_target
No es necesario que el destino sea un archivo; podría ser solo un nombre para la receta, como en nuestro ejemplo. Los llamamos "objetivos falsos".
Volviendo al ejemplo anterior, cuando make
fue ejecutado, el comando completo echo "Hello World"
fue mostrado, seguido por la salida del comando actual. A menudo no queremos eso. Para suprimir el eco del comando real, debemos iniciar echo
con @
:
say_hello:
@echo "Hello World"
Ahora intente ejecutar make
otra vez. La salida debe mostrar solo esto:
$ make
Hello World
Agreguemos algunos objetivos falsos más:generate
y clean
al Makefile
:
say_hello:
@echo "Hello World"
generate:
@echo "Creating empty text files..."
touch file-{1..10}.txt
clean:
@echo "Cleaning up..."
rm *.txt
Si intentamos ejecutar make
después de los cambios, solo el objetivo say_hello
será ejecutado. Esto se debe a que solo el primer destino del archivo MAKE es el destino predeterminado. A menudo llamado el objetivo predeterminado , esta es la razón por la que verás all
como el primer objetivo en la mayoría de los proyectos. Es responsabilidad de all
para llamar a otros objetivos. Podemos anular este comportamiento usando un objetivo falso especial llamado .DEFAULT_GOAL
.
Incluyamos eso al comienzo de nuestro archivo MAKE:
.DEFAULT_GOAL := generate
Esto ejecutará el objetivo generate
como predeterminado:
$ make
Creating empty text files...
touch file-{1..10}.txt
Como sugiere el nombre, el objetivo falso .DEFAULT_GOAL
solo puede ejecutar un objetivo a la vez. Esta es la razón por la que la mayoría de los archivos MAKE incluyen all
como un objetivo que puede llamar a tantos objetivos como sea necesario.
Incluyamos el objetivo falso all
y elimina .DEFAULT_GOAL
:
all: say_hello generate
say_hello:
@echo "Hello World"
generate:
@echo "Creating empty text files..."
touch file-{1..10}.txt
clean:
@echo "Cleaning up..."
rm *.txt
Antes de ejecutar make
, incluyamos otro objetivo falso especial, .PHONY
, donde definimos todos los objetivos que no son archivos. make
ejecutará su receta independientemente de si existe un archivo con ese nombre o cuál es su hora de última modificación. Aquí está el archivo MAKE completo:
.PHONY: all say_hello generate clean
all: say_hello generate
say_hello:
@echo "Hello World"
generate:
@echo "Creating empty text files..."
touch file-{1..10}.txt
clean:
@echo "Cleaning up..."
rm *.txt
El make
debería llamar a say_hello
y generate
:
$ make
Hello World
Creating empty text files...
touch file-{1..10}.txt
Es una buena práctica no llamar a clean
en all
o ponerlo como primer objetivo. clean
debe llamarse manualmente cuando se necesita limpieza como primer argumento para make
:
$ make clean
Cleaning up...
rm *.txt
Ahora que tiene una idea de cómo funciona un archivo MAKE básico y cómo escribir un archivo MAKE simple, veamos algunos ejemplos más avanzados.
Ejemplos avanzados
Variables
Más recursos de Linux
- Hoja de trucos de los comandos de Linux
- Hoja de trucos de comandos avanzados de Linux
- Curso en línea gratuito:Descripción general técnica de RHEL
- Hoja de trucos de red de Linux
- Hoja de trucos de SELinux
- Hoja de trucos de los comandos comunes de Linux
- ¿Qué son los contenedores de Linux?
- Nuestros últimos artículos sobre Linux
En el ejemplo anterior, la mayoría de los valores objetivo y de requisitos previos están codificados, pero en proyectos reales, estos se reemplazan con variables y patrones.
La forma más sencilla de definir una variable en un archivo MAKE es usar el =
operador. Por ejemplo, para asignar el comando gcc
a una variable CC
:
CC = gcc
Esto también se llama una variable expandida recursiva , y se usa en una regla como se muestra a continuación:
hello: hello.c
${CC} hello.c -o hello
Como habrás adivinado, la receta se expande de la siguiente manera cuando se pasa a la terminal:
gcc hello.c -o hello
Ambos ${CC}
y $(CC)
son referencias válidas para llamar a gcc
. Pero si uno intenta reasignarse una variable a sí mismo, provocará un bucle infinito. Verifiquemos esto:
CC = gcc
CC = ${CC}
all:
@echo ${CC}
Ejecutando make
dará como resultado:
$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually). Stop.
Para evitar este escenario, podemos usar el :=
operador (esto también se llama la variable simplemente expandida ). No deberíamos tener problemas para ejecutar el archivo MAKE a continuación:
CC := gcc
CC := ${CC}
all:
@echo ${CC}
Patrones y funciones
El siguiente archivo MAKE puede compilar todos los programas en C mediante el uso de variables, patrones y funciones. Exploremos línea por línea:
# Usage:
# make # compile all binary
# make clean # remove ALL binaries and objects
.PHONY = all clean
CC = gcc # compiler to use
LINKERFLAG = -lm
SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)
all: ${BINS}
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@
%.o: %.c
@echo "Creating object.."
${CC} -c $<
clean:
@echo "Cleaning up..."
rm -rvf *.o ${BINS}
-
Líneas que comienzan con
#
son comentarios. -
Línea
.PHONY = all clean
define objetivos falsosall
yclean
. -
Variable
LINKERFLAG
define indicadores que se utilizarán congcc
en una receta. -
SRCS := $(wildcard *.c)
:$(wildcard pattern)
es una de las funciones para nombres de archivo . En este caso, todos los archivos con.c
la extensión se almacenará en una variableSRCS
. -
BINS := $(SRCS:%.c=%)
:Esto se llama como referencia de sustitución . En este caso, siSRCS
tiene valores'foo.c bar.c'
,BINS
tendrá'foo bar'
. -
Línea
all: ${BINS}
:El objetivo falsoall
llama valores en${BINS}
como objetivos individuales. -
Regla:
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@Veamos un ejemplo para entender esta regla. Supongamos que
foo
es uno de los valores en${BINS}
. Entonces%
coincidirá confoo
(%
puede coincidir con cualquier nombre de objetivo). A continuación se muestra la regla en su forma expandida:foo: foo.o
@echo "Checking.."
gcc -lm foo.o -o fooComo se muestra,
%
se reemplaza porfoo
.$<
se reemplaza porfoo.o
.$<
está diseñado para cumplir con los requisitos previos y$@
coincide con el objetivo. Esta regla se llamará para cada valor en${BINS}
-
Regla:
%.o: %.c
@echo "Creating object.."
${CC} -c $<Cada requisito previo de la regla anterior se considera un objetivo para esta regla. A continuación se muestra la regla en su forma expandida:
foo.o: foo.c
@echo "Creating object.."
gcc -c foo.c -
Finalmente, eliminamos todos los binarios y archivos de objetos en el objetivo
clean
.
A continuación se muestra la reescritura del archivo MAKE anterior, suponiendo que esté ubicado en el directorio que tiene un solo archivo foo.c:
# Usage:
# make # compile all binary
# make clean # remove ALL binaries and objects
.PHONY = all clean
CC = gcc # compiler to use
LINKERFLAG = -lm
SRCS := foo.c
BINS := foo
all: foo
foo: foo.o
@echo "Checking.."
gcc -lm foo.o -o foo
foo.o: foo.c
@echo "Creating object.."
gcc -c foo.c
clean:
@echo "Cleaning up..."
rm -rvf foo.o foo
Para obtener más información sobre los archivos MAKE, consulte el manual GNU Make, que ofrece una referencia completa y ejemplos.
También puede leer nuestra Introducción a GNU Autotools para aprender cómo automatizar la generación de un archivo MAKE para su proyecto de codificación.