GNU/Linux >> Tutoriales Linux >  >> Linux

¿Qué es un Makefile y cómo funciona?

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 falsos all y clean .

  • Variable LINKERFLAG define indicadores que se utilizarán con gcc 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 variable SRCS .

  • BINS := $(SRCS:%.c=%) :Esto se llama como referencia de sustitución . En este caso, si SRCS tiene valores 'foo.c bar.c' , BINS tendrá 'foo bar' .

  • Línea all: ${BINS} :El objetivo falso all llama valores en ${BINS} como objetivos individuales.

  • Regla:

    %: %.o
      @echo "Checking.."
      ${CC} ${LINKERFLAG} $&lt; -o $@

    Veamos un ejemplo para entender esta regla. Supongamos que foo es uno de los valores en ${BINS} . Entonces % coincidirá con foo (% 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 foo

    Como se muestra, % se reemplaza por foo . $< se reemplaza por foo.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 $&lt;

    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.


Linux
  1. ¿Qué es NGINX? ¿Como funciona?

  2. ¿Qué es un servidor web y cómo funciona un servidor web?

  3. ¿Cómo funciona Awk '!a[$0]++'?

  4. ¿Cómo funciona la punta adhesiva?

  5. ¿El propósito de .bashrc y cómo funciona?

Comando de archivo de Linux:qué hace y cómo usarlo

¿Qué es Docker? ¿Como funciona?

¿Qué es el comando fuente en Linux y cómo funciona?

¿Qué es el comando Grep en Linux? ¿Por qué se usa y cómo funciona?

¿Cómo funciona el intercambio de memoria en Linux?

¿Cómo funciona un balanceador de carga? ¿Qué es el equilibrio de carga?