resueltos - ¿Por qué describimos los procedimientos de compilación con Makefiles en lugar de los scripts de shell?
scripts linux ejercicios resueltos (1)
Observación Esta es una variación de la pregunta " ¿Cuál es el propósito de vincular archivos de objetos por separado en un Makefile? "Por usuario4076675 teniendo un punto de vista ligeramente diferente. Ver también la discusión META correspondiente.
Consideremos el caso clásico de un proyecto C. El compilador gcc
puede compilar y vincular programas en un solo paso. A continuación, podemos describir fácilmente la rutina de compilación con un script de shell:
case $1 in
build) gcc -o test *.c;;
clean) rm -f test;;
esac
# This script is intentionally very brittle, to keep
# the example simple.
Sin embargo, parece ser idiomático describir el procedimiento de compilación con un Makefile
, que implica pasos adicionales para compilar cada unidad de compilación en un archivo de objeto y, finalmente, vincular estos archivos. El archivo Makefile de GNU correspondiente sería:
.PHONY: all
SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
%.o: %.cpp
g++ -c -o $@ $<
all: default
default: $(OBJECTS)
g++ -o test $^
clean:
rm -rf *.o
Esta segunda solución es más discutible que el simple script de shell que escribimos antes. También es un inconveniente, ya que desordena el directorio fuente con archivos de objetos. Entonces, ¿por qué describimos los procedimientos de compilación con Makefiles en lugar de scripts de shell? De la mano del ejemplo anterior, parece ser una complicación inútil.
En el caso simple donde compilamos y vinculamos tres archivos de tamaño moderado, cualquier enfoque es igualmente satisfactorio. Por lo tanto, consideraré el caso general, pero muchos beneficios del uso de Makefiles solo son importantes en proyectos más grandes. Una vez que aprendimos la mejor herramienta que nos permite dominar los casos complicados, también queremos usarla en casos simples.
Permítanme resaltar los '''' beneficios '''' de usar make
lugar de un simple script de shell para trabajos de compilación. Pero primero, me gustaría hacer una observación inocua.
El paradigma de procedimiento de los scripts de shell es incorrecto para los trabajos de compilación
Escribir un Makefile es similar a escribir un script de shell con un ligero cambio de perspectiva. En un script de shell, describimos una solución de procedimiento para un problema: podemos comenzar a describir todo el procedimiento en términos muy abstractos utilizando funciones no definidas, y refinamos esta descripción hasta que alcanzamos el nivel de descripción más elemental, donde un procedimiento es solo un comando de shell simple. En un Makefile, no presentamos ninguna abstracción similar, pero nos enfocamos en los archivos que queremos producir y cómo podemos producirlos. Esto funciona bien porque en UNIX, todo es un archivo, por lo tanto, cada tratamiento se realiza mediante un programa que lee sus datos de entrada de los archivos de entrada , hace algunos cálculos y escribe los resultados en algunos archivos de salida .
Si queremos calcular algo complicado, tenemos que usar una gran cantidad de archivos de entrada que son tratados por programas cuyos resultados se utilizan como entradas para otros programas, y así sucesivamente hasta que hayamos producido nuestros archivos finales que contienen nuestro resultado. Si traducimos el plan para preparar nuestro archivo final en un conjunto de procedimientos en un script de shell, entonces el estado actual del procesamiento se hace implícito : el ejecutor del plan sabe "dónde está" porque está ejecutando un procedimiento dado, que garantiza implícitamente que dichos cálculos ya se realizaron, es decir, que dichos archivos intermedios ya estaban preparados. Ahora, ¿qué datos describen "dónde está el ejecutor del plan"?
Observación inocua La información que describe "dónde está el ejecutor del plan" es precisamente el conjunto de archivos intermedios que ya estaban preparados, y esta es exactamente la información que se hace explícita cuando escribimos Makefiles.
Esta observación inocuo es en realidad la diferencia conceptual entre los scripts de shell y los Makefiles, que explican todas las ventajas de los Makefiles sobre los scripts de shell en trabajos de compilación y trabajos similares. Por supuesto, para apreciar plenamente estas ventajas, tenemos que escribir Makefiles correctos , lo que puede ser difícil para los principiantes.
Hacer hace que sea más fácil continuar una tarea interrumpida donde estaba en
Cuando describimos un trabajo de compilación con un Makefile, podemos interrumpirlo fácilmente y reanudarlo más tarde. Esto es una consecuencia de la observación inocua . Un efecto similar solo se puede lograr con esfuerzos considerables en un script de shell, mientras que solo se crea en make
.
Make hace que sea fácil trabajar con varias compilaciones de un proyecto
Observaste que los Makefiles saturarán el árbol fuente con archivos de objeto. Pero los Makefiles en realidad se pueden parametrizar para almacenar estos archivos de objetos en un directorio dedicado. Trabajo con macros BSD Owl para bsdmake y uso
MAKEOBJDIR=''/usr/home/michael/obj${.CURDIR:S@^/usr/home/michael@@}''
para que todos los archivos de objetos terminen bajo ~/obj
y no contaminen mis fuentes. Vea esta respuesta para más detalles.
Los Makefiles avanzados nos permiten tener simultáneamente varios directorios que contienen varias compilaciones de un proyecto con distintas opciones de compilación. Por ejemplo, con características distintas habilitadas, o versiones de depuración, etc. Esto también es consecuencia de la observación inocuo de que los Makefiles en realidad están articulados alrededor del conjunto de archivos intermedios. Esta técnica se ilustra en la suite de pruebas de BSD Owl .
Make facilita la paralelización de compilaciones
Podemos construir fácilmente un programa en paralelo ya que esta es una función estándar de muchas versiones de make
. Esto también es consecuencia de la observación inocuo : porque "donde está el ejecutor del plan" es un dato explícito en un Makefile, es posible make
razonamiento al respecto. Lograr un efecto similar en un script de shell requerirá un gran esfuerzo.
El modo paralelo de cualquier versión de make
solo funcionará correctamente si las dependencias están especificadas correctamente. Esto puede ser bastante complicado de lograr, pero bsdmake tiene la característica que literalmente anila el problema. Se llama el modo META . Utiliza un primer pase, no paralelo, de un trabajo de compilación para calcular las dependencias reales mediante el control de acceso a archivos, y utiliza esta información en compilaciones paralelas posteriores.
Los archivos Makefiles son fácilmente extensibles
Debido a la perspectiva especial, es decir, como otra consecuencia de la observación inocua , solía escribir Makefiles, podemos extenderlos fácilmente enganchándonos en todos los aspectos de nuestro sistema de compilación.
Por ejemplo, si decidimos que todo nuestro código repetitivo de E / S de base de datos debe escribirse con una herramienta automática, solo tenemos que escribir en el Makefile qué archivos debe utilizar la herramienta automática como entradas para escribir el código repetitivo. Nada menos, nada más. Y podemos agregar esta descripción más o menos donde nos gusta, make
que la obtenga de todos modos. Hacer tal extensión en una compilación de shell script sería más difícil de lo necesario.
Esta facilidad de extensibilidad es un gran incentivo para la reutilización del código Makefile.