Make es un software que crea automáticamente el archivo , a menudo ejecutable , o bibliotecas a partir de elementos básicos como el código fuente . Utiliza archivos llamados makefiles que especifican cómo construir los archivos de destino. A diferencia de un script de shell simple , make ejecuta comandos solo si son necesarios. El objetivo es lograr un resultado (software compilado o instalado, documentación creada, etc.) sin tener que rehacer necesariamente todos los pasos. make se utiliza especialmente en plataformas UNIX .
En la década de 1970, la compilación de programas se volvió cada vez más larga y compleja, requiriendo muchos pasos interrelacionados. La mayoría de los sistemas que se utilizan en ese momento se basan en scripts de shell , lo que requiere repetir todos los pasos para la más mínima corrección. Es en este contexto que Make fue desarrollado por el Doctor Stuart Feldman (en) , en 1977 mientras trabajaba para Bell Labs . Al administrar las dependencias entre los archivos fuente y los archivos compilados, Make le permite compilar solo lo necesario después de la modificación de un archivo fuente.
Desde el desarrollo original, el programa ha experimentado muchas variaciones. Los más conocidos son el de BSD y el del Proyecto GNU , utilizado por defecto en los sistemas Linux . Estas variantes proporcionan nuevas funcionalidades y, por lo general, no son compatibles entre sí. Por ejemplo, los scripts destinados a GNU Make pueden no funcionar con BSD Make.
Posteriormente, aparecieron otras herramientas que permiten la generación automática de archivos de configuración (Makefile) utilizados por make. Estas herramientas permiten analizar las dependencias ( automake ) o la configuración del sistema ( autoconf ) para generar Makefiles complejos y específicamente adaptados al entorno en el que se ejecutan las acciones de compilación.
Desde entonces, Make ha inspirado una variedad de software de gestión de compilación, específico para ciertas plataformas ( rake , ant ), o generalista como Ninja en la década de 2010.
En 2003, la D r Feldman fue galardonado con el precio de ACM para el desarrollo de la marca.
El proceso de compilación se divide en reglas elementales como "El objetivo A depende de B y C. Para fabricar A, debes ejecutar una secuencia determinada de comandos" . Todas estas reglas se colocan en un archivo comúnmente llamado Makefile. Los comandos consisten en acciones elementales de compilación , edición de enlaces , generación de código .
Las dependencias corresponden a archivos, estos pueden ser archivos de origen o resultados intermedios en el proceso de compilación. El objetivo suele ser un archivo, pero también puede ser abstracto.
La construcción de A se lleva a cabo mediante el comando:
make ALuego, Make verificará que los archivos B y C estén disponibles y actualizados, es decir, que no hayan cambiado las dependencias después de su creación, y Make De lo contrario comenzará construyendo recursivamente B y C. Make luego aplicará el comandos de creación de A tan pronto como no exista ningún archivo con este nombre, o que el archivo A sea más antiguo que B o C.
Es habitual utilizar un objetivo total que resuma todo el proyecto, teniendo este objetivo abstracto como dependencia todos los archivos a construir. Otros destinos son de uso común: instalar para ejecutar los comandos de instalación del proyecto, limpiar para borrar todos los archivos generados producidos.
Make permite el uso de reglas de dependencia explícitas, correspondientes a nombres de archivos, o implícitas, correspondientes a patrones de archivos; por ejemplo, cualquier archivo con una extensión .o se puede construir a partir de un archivo con el mismo nombre con una extensión .c mediante un comando de compilación.
Make representa el proyecto como un árbol de dependencias , y algunas variantes del programa permiten la construcción de múltiples objetivos en paralelo cuando no tienen dependencias entre ellos.
Realice búsquedas en el directorio actual para utilizar el archivo MAKE . Por ejemplo, GNU realiza búsquedas en orden para un GNUmakefile , makefile , Makefile , luego ejecuta los destinos especificados (o predeterminados) solo para ese archivo.
El lenguaje utilizado en el archivo MAKE es programación declarativa . A diferencia de la programación imperativa , esto significa que el orden en el que se deben ejecutar las instrucciones no importa.
Un archivo MAKE se compone de reglas . La forma más simple de regla es:
cible [cible ...]: [composant ...] [tabulation] commande 1 . . . [tabulation] commande nEl "objetivo" suele ser un archivo para compilar, pero también puede definir una acción (eliminar, compilar, etc.). Los “componentes” son prerrequisitos necesarios para llevar a cabo la acción definida por la regla. En otras palabras, los "componentes" son los objetivos de otras reglas que deben cumplirse antes de que esta regla pueda cumplirse. La regla define una acción mediante una serie de "comandos". Estos comandos definen cómo utilizar los componentes para producir el objetivo. Cada comando debe estar precedido por un carácter de tabulación.
Los comandos son ejecutados por un shell separado o por un intérprete de línea de comandos .
Aquí hay un ejemplo de un archivo MAKE:
all: cible1 cible2 echo ''all : ok'' cible1: echo ''cible1 : ok'' cible2: echo ''cible2 : ok''Cuando este archivo MAKE se ejecuta a través del comando make all o el comando make , se ejecuta la regla 'all'. Requiere la creación de los componentes 'target1' y 'target2' que están asociados con las reglas 'target1' y 'target2'. Por lo tanto, estas reglas se ejecutarán automáticamente antes de la regla "todos". Por otro lado, el comando make target1 solo ejecutará la regla 'target1'.
Para resolver el orden en el que se deben ejecutar las reglas, make utiliza una ordenación topológica .
Tenga en cuenta que una regla no incluye necesariamente un comando.
Los componentes no siempre son objetivos de otras reglas, también pueden ser archivos necesarios para crear el archivo de destino:
sortie.txt: fichier1.txt fichier2.txt cat fichier1.txt fichier2.txt > sortie.txtLa regla de ejemplo anterior construye el archivo output.txt utilizando los archivos file1.txt y file2.txt. Al ejecutar el archivo MAKE, compruebe si el archivo output.txt existe y, si no, lo compilará utilizando el comando definido en la regla.
Las líneas de comando pueden tener uno o más de los siguientes tres prefijos:
Cuando varios objetivos requieren los mismos componentes y se crean con los mismos comandos, es posible definir una regla múltiple. Por ejemplo :
all: cible1 cible2 cible1: texte1.txt echo texte1.txt cible2: texte1.txt echo texte1.txtpuede ser reemplazado por:
all: cible1 cible2 cible1 cible2: texte1.txt echo texte1.txtTenga en cuenta que todos los objetivos son obligatorios; de lo contrario, solo se ejecutará el objetivo target1 .
Para averiguar el objetivo en cuestión, puede utilizar la variable $ @ , por ejemplo:
all: cible1.txt cible2.txt cible1.txt cible2.txt: texte1.txt cat texte1.txt > $@creará dos archivos, target1.txt y target2.txt , con el mismo contenido que text1.txt .
Un archivo MAKE puede contener definiciones de macros . Las macros se definen tradicionalmente en mayúsculas:
MACRO = definitionLas macros se denominan con mayor frecuencia como variables cuando solo contienen definiciones de cadenas simples, como CC = gcc . Las variables de entorno también están disponibles como macros. Las macros en un archivo MAKE se pueden sobrescribir con los argumentos pasados a make. El comando es entonces:
make MACRO="valeur" [MACRO="valeur" ...] CIBLE [CIBLE ...]Las macros pueden estar compuestas por comandos de shell usando el acento grave ( `):
DATE = ` date `También hay 'macros internas' para hacer:
Las macros permiten a los usuarios especificar qué programas usar o ciertas opciones personalizadas durante el proceso de compilación. Por ejemplo, la macro CC se usa con frecuencia en archivos MAKE para especificar un compilador de C a usar.
Para usar una macro, debe expandirla encapsulándola en $ () . Por ejemplo, para usar la macro CC , deberá escribir $ (CC) . Tenga en cuenta que también es posible utilizar $ {} . Por ejemplo :
NOUVELLE_MACRO = $(MACRO)-${MACRO2}Esta línea crea una nueva macro NOUVELLE_MACRO restando el contenido de MACRO2 del contenido de MACRO .
Tipos de asignacionesHay varias formas de definir una macro:
Estas reglas le permiten crear archivos MAKE para un tipo de archivo. Hay dos tipos de reglas de sufijos: dobles e individuales.
Una regla de doble sufijo se define mediante dos sufijos: un sufijo de destino y un sufijo de origen . Esta regla reconocerá cualquier archivo del tipo "objetivo". Por ejemplo, si el sufijo de destino es '.txt' y el sufijo de origen es '.html', la regla es equivalente a '% .txt:% .html' (donde% significa cualquier nombre de archivo). Una regla de sufijo simple solo necesita un sufijo fuente.
La sintaxis para definir una regla de doble sufijo es:
.suffixe_source.suffixe_cible :Tenga en cuenta que una regla de sufijo no puede tener componentes adicionales.
La sintaxis para definir la lista de sufijos es:
.SUFFIXES: .suffixe_source .suffixe_cibleUn ejemplo de uso de reglas de sufijo:
.SUFFIXES: .txt .html # transforme .html en .txt .html.txt: lynx -dump $< > $@La siguiente línea de comando transformará el archivo file.html en file.txt :
make -n fichier.txtEl uso de reglas de sufijos se considera obsoleto porque es demasiado restrictivo.
Aquí hay un ejemplo de Makefile:
# Indiquer quel compilateur est à utiliser CC ?= gcc # Spécifier les options du compilateur CFLAGS ?= -g LDFLAGS ?= -L/usr/openwin/lib LDLIBS ?= -lX11 -lXext # Reconnaître les extensions de nom de fichier *.c et *.o comme suffixes SUFFIXES ?= .c .o .SUFFIXES: $(SUFFIXES) . # Nom de l'exécutable PROG = life # Liste de fichiers objets nécessaires pour le programme final OBJS = main.o window.o Board.o Life.o BoundedBoard.o all: $(PROG) # Étape de compilation et d'éditions de liens # ATTENTION, les lignes suivantes contenant "$(CC)" commencent par un caractère TABULATION et non pas des espaces $(PROG): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG) $(OBJS) .c.o: $(CC) $(CFLAGS) -c $*.cEn este ejemplo, .c.oes una regla implícita. Por defecto, los destinos son archivos, pero cuando se trata de la yuxtaposición de dos sufijos, es una regla que deriva cualquier archivo que termine con el segundo sufijo de un archivo con el mismo nombre pero que termine con el primer sufijo.
Para alcanzar este objetivo, debe ejecutar la acción, el comando $(CC) $(CFLAGS) -c $*.c, donde $*representa el nombre del archivo sin sufijo.
Por otro lado, alles un objetivo que depende de $(PROG)(y por tanto life, que es un archivo).
$(PROG)- es decir life- es un objetivo que depende de $(OBJS)(y por lo tanto archivos main.o window.o Board.o Life.oy BoundedBoard.o). Para lograr esto, makeejecute el comando $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG) $(OBJS).
La sintaxis CC ?= gcc, o más en general <variable> ?= <valeur>, afecta <valeur>en <variable>sólo si <variable>aún no inicializado. Si <variable>ya contiene un valor, esta instrucción no tiene ningún efecto.
Las limitaciones de make provienen directamente de la sencillez de los conceptos que lo popularizaron: archivos y fechas. De hecho, estos criterios son insuficientes para garantizar tanto la eficiencia como la fiabilidad.
El criterio de fecha asociado con los archivos, por sí solo, combina las dos fallas. A menos que el archivo resida en un medio de una sola escritura, no hay garantía de que la fecha de un archivo sea de hecho la fecha de su última modificación.
Si para un usuario no privilegiado se asegura que los datos no pueden ser posteriores a la fecha indicada, no se garantiza la fecha exacta de su anterioridad.
Así, al más mínimo cambio en la fecha de un archivo, toda una producción puede considerarse necesaria si es una fuente, pero peor aún puede considerarse inútil si, por el contrario, es un destino.
Si la fecha y el archivo siguen siendo esencialmente necesarios para cualquier motor de producción que se desee confiable y óptimo, tampoco son suficientes y algunos ejemplos específicos son suficientes para ilustrar esto:
Otra limitación de make es que no genera la lista de dependencias y no puede verificar que la lista proporcionada sea correcta. Por lo tanto, el ejemplo simple anterior que se basa en la regla .c.oes erróneo: de hecho, los archivos objeto no solo dependen del archivo fuente .casociado, sino también de todos los archivos del proyecto incluidos en el archivo .c. Una lista más realista de dependencias sería:
$(PROG): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG) $(OBJS) main.o: main.c Board.h BoundedBoard.h Life.h global.h $(CC) $(CFLAGS) -c main.c window.o: window.c window.h global.h $(CC) $(CFLAGS) -c window.c Board.o: Board.c Board.h window.h global.h $(CC) $(CFLAGS) -c Board.c Life.o: Life.c Life.h global.h $(CC) $(CFLAGS) -c Life.c BoundedBoard.o: BoundedBoard.c BoundedBoard.h Board.h global.h $(CC) $(CFLAGS) -c BoundedBoard.cEs razonable suponer que los archivos del sistema incluidos (as stdio.h) no cambian y no los enumera como dependencias. A costa de escribir analizadores capaces de producir listas dinámicas de dependencias, algunas versiones de make solucionan este problema.
Es por estas razones que los motores de producción de nueva generación se especializan en el procesamiento de lenguajes particulares (semántica del contenido de los archivos) o también están acoplados a una base de datos en la que se registran todas las características de producción efectivas ( auditoría de producción ) objetivos (trazabilidad).
Hay varias alternativas para hacer: