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 cleandefine objetivos falsosallyclean. -
Variable
LINKERFLAGdefine indicadores que se utilizarán congccen una receta. -
SRCS := $(wildcard *.c):$(wildcard pattern)es una de las funciones para nombres de archivo . En este caso, todos los archivos con.cla extensión se almacenará en una variableSRCS. -
BINS := $(SRCS:%.c=%):Esto se llama como referencia de sustitución . En este caso, siSRCStiene valores'foo.c bar.c',BINStendrá'foo bar'. -
Línea
all: ${BINS}:El objetivo falsoallllama valores en${BINS}como objetivos individuales. -
Regla:
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@Veamos un ejemplo para entender esta regla. Supongamos que
fooes 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.