Este artículo está disponible en los siguientes idiomas: English Castellano Deutsch Francais Nederlands Portugues Russian Turkce |
por Wilbert Berendsen Sobre el autor: Wilbert Berendsen es músico profesional y un entusiata usuario de Linux. En un tiempo, hackeó el ensamblador del Z80 intensamente. Actualmente, utiliza Linux para todo el trabajo de su producción. En su tiempo libre, escribe artículos introductorios y mantiene un pequeño website en http://www.xs4all.nl/~wbsoft/. Viva open source! Contenidos:
|
Resumen:
Este artículo muestra el funcionamiento de make y cómo puede ser aprovechado para más usos que el simple desarrollo de software.
Prácticamente cualquier usuario de Linux ha utilizado el programa make varias veces. Su uso habitual es construir un programa o kernel a partir del código fuente, instalar un paquete, etc. 'Make' es una herramienta importante en el desarrollo de software. Sin embargo, ¡make tiene muchas más posibilidades!
En este documento, se verá que make puede ser una potente herramienta en tareas cotidianas, como escribir artículos, libros o construir un buen website. A lo largo de esta introducción, se verán muchos otros 'trucos unix', y al final de esta historia, se presentarán algunos consejos más sobre el uso de make. Hay que tener en cuenta que estamos hablando sobre Linux, pero en principio es posible utilizar make en cualquier sistema operativo.
Necesitamos un sistema simple que separe el diseño del contenido. Una solución eficiente podría ser leer el contenido de una base de datos cada vez que se pide una página. Por ejemplo, PHP y Active Server de Microsoft trabajan de esta manera. De todas formas, sólo tenemos la posibilidad de almacenar código plano HTML (HyperText Markup Language). Además, el contenido no cambia con tanta frecuencia como para mantener una base de datos.
Utilizando algunos comandos muy simples, se puede construir un sitio web.
Por ejemplo, Piet pone la cabecera del site en header.html y el pie del site en footer.html. header.html podría tener el aspecto siguiente:
<html><!-- la cabecera --> <head> <title>Piet and Jan productions</title> </head> <body bgcolor="white"> <table border="0" width="100%"><tr> <td bgcolor="#c040ff" valign="top"> This is our website<br> Some rubbish is written down here.<br> We are very interactive<br> so this is our telephone number:<br> <b>0123-456789</b> </td><td valign="top"> <!-- Los contenidos se ponen aquí -->y este es el pie footer.html:
<!-- el pie --> </td></tr></table> </body></html>Por ejemplo, los comandos unix para construir la página definitiva a partir del index.html de Jan son:
cat header.html /home/jan/Docs/website/index.html echo -n '<hr>Last modification: ' date '+%A %e %B' cat footer.htmlPor favor, diríjanse a las páginas del manual de estos comandos. El fichero final, como resultado de los comandos anteriores, se redirecciona a la salida estándar, y se recoge en un fichero:
{ cat header.html /home/jan/Docs/website/index.html echo -n '<hr>Last modification: ' date '+%A %e %B' cat footer.html } > /home/piet/public_html/index.htmlEste procedimiento se puede repetir con el otro fichero, offer.html. De hecho, nosotros creamos un pequeño script que permite la construcción de nuestro website.
Sin embargo, ejecutar este comando a mano no es factible. Es posible crear un script de shell que se ejecute cada vez que Jan actualiza su index. De todas maneras, si Piet decide cambiar la cabecera o el pie, ¡este script también debería ser ejecutado! Por otro lado, si Jan no cambia nada un día, el script no debería ejecutarse. Estamos utilizando Linux, por lo que queremos una solución elegante (es decir: ¡automática!).
Es el momento de recurrir a make.
make determina si un conjunto de ficheros debe ser ejecutado, basándose en las fechas de última modificación de los ficheros destino y en las de los ficheros fuente.En otras palabras: si uno de los ficheros fuente, necesario para crear un fichero destino, es más reciente que el fichero destino, un conjunto de comandos serán ejecutados. El propósito de estos comandos es actualizar el fichero destino.
El fichero destino es el "objetivo y los ficheros fuente son los "prerrequisitos" Los comandos se ejecutan si uno de los 'prerrequisitos' es más reciente que el fichero destino (o si el destino no existe). Si todos los prerrequisitos son igual o más antiguos que el destino, entonces los comandos no se ejecutan y el fichero destino se considera actualizado.
En el directorio de trabajo actual, se debería crear un fichero con el nombre Makefile. Este fichero contiene la información que necesita make para realizar su trabajo de la manera adecuada. Una vez que se tiene el Makefile (con 'M' mayúscula), la única cosa que hay que hacer es: teclear 'make' y los comandos necesarios para crear un nuevo fichero destino se ejecutan automáticamente.
Make se invoca con el comando
make target1 target2 ....
El parámetro destino es opcional (si no se indica nada, se utiliza el primer destino del Makefile). Make siempre busca el Makefile en el directorio actual. Es posible proporcionar más de un destino.
# Esto es un ejemplo de Makefile. # Se pueden poner comentarios tras un carácter hash (#). destino1: prerrequisitos1 comando destino2: prerrequisitos2 comando # Etcétera.Se comienza con un destino, seguido de dos puntos (:) y los prerrequisitos necesarios. Si existen muchos prerrequisitos, se puede finalizar la línea con un backslash (\) y continuar en la siguiente línea.
En la/s línea/s siguiente/s se escriben uno o más comandos. Cada línea se considera como un comando independiente. Si se desea utilizar múltiples líneas para un comando, se debería poner un backslash (\) al final de cada línea del comando. Make conectará las líneas como si hubieran sido escritas en una única línea. En esta situación, se deben separar los comandos con un punto y coma (;) para prevenir errores en la ejecución de la shell.
Importante: ¡Los comandos deben ser indentados con un tabulador, no con 8 espacios! |
Make lee el Makefile y determina para cada fichero destino (empezando por el primero) si los comandos deben ser ejecutados. Cada destino, junto con los prerrequisitos y reglas, es denominado una 'regla'.
Si make se ejecuta sin argumentos, sólo se ejecutará el primer destino.
# Este Makefile construye el website de Piet y Jan. all: /home/piet/public_html/index.html /home/piet/public_html/offer.html /home/piet/public_html/index.html: header.html footer.html \ /home/jan/Docs/website/index.html { \ cat header.html /home/jan/Docs/website/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > /home/piet/public_html/index.html /home/piet/public_html/offer.html: header.html footer.html \ /home/jan/Docs/website/offer.html { \ cat header.html /home/jan/Docs/website/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > /home/piet/public_html/offer.html # fin
Ahora tenemos 3 destinos, "all" y los ficheros index.html y offer.html del website. La única función del destino "all" es tener los otros dos prerrequisitos. Los dos son testeados. Debido a que "all" por sí mismo no es un nombre de fichero, el destino "all" siempre se ejecutará. (Más tarde, se introducirá una forma más elegante de definir destinos que no son un fichero).
Si la cabecera y el pie fueran modificados, ambas páginas serían actualizadas. Si Jan modifica una de sus páginas, sólo la página modificada será actualizada. ¡Ejecutar el comando 'make' hace todo el trabajo!
Por supuesto, el Makefile tiene una desventaja: no es sencillo de revisar. Afortunadamente, ¡hay muchas formas de hacer las cosas más sencillas!
variable = valorNos referiremos a la variable con la expresión $(variable). Si incorporamos esto dentro del Makefile, tendrá mucho mejor aspecto:
# Este Makefile construye el website de Piet y Jan. # Directorio sonde se almacena el website: TARGETDIR = /home/piet/public_html # Directorio de Jans: JANSDIR = /home/jan/Docs/website # Ficheros necesarios para el diseño: LAYOUT = header.html footer.html all: $(TARGETDIR)/index.html $(TARGETDIR)/offer.html $(TARGETDIR)/index.html: $(LAYOUT) $(JANSDIR)/index.html { \ cat header.html $(JANSDIR)/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(LAYOUT) $(JANSDIR)/offer.html { \ cat header.html $(JANSDIR)/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $(TARGETDIR)/offer.html # finEs una buena costumbre utilizar letras mayúsculas para las variables. Ahora es mucho más sencillo cambiar, por ejemplo, el directorio de destino.
Si se desea, es posible definir otro método para cada documento que se quiere formatear con el mismo diseño. ¿Qué se debería hacer para ello? El Makefile aumentaría mucho su tamaño, debido a la gran cantidad del repeticiones que aparecerían. ¡Esto también se puede simplificar!
Si se utilizan reglas patrón, la sintaxis de la línea cambia; se añade un campo de patrón adicional:
destinos múltiples: patrón : prerrequisito prerrequisito ... comandoEl patrón es una expresión que debería ser aplicable a todos los destinos. Un signo de porcentaje se utiliza para incorporar partes de variables de un nombre de destino.
Un ejemplo:
/home/bla/target1.html /home/bla/target2.html: /home/bla/% : % comandosSi make lee esto, la línea se expande a dos líneas, Aquí, el patrón determina qué parte del nombre destino es incorporado en el signo de porcentaje.
El signo de porcentaje en los prerrequisitos representa la parte que es copiada con este signo de porcentaje.
Make expande lo enterior de la forma:
/home/bla/target1.html: target1.html comandos /home/bla/target2.html: target2.html comandosEl signo de porcentaje en el patrón `/home/bla/%' toma en el destino `/home/bla/target1.html' el valor `target1.html', de modo que el prerrequisito `%' se expande a `target1.html'.
Para el website, se incorporará la siguiente regla:
$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: \ $(TARGETDIR)/% : $(JANSDIR)/% \ $(LAYOUT)Ya sólo falta resolver un problema: ¿Cómo utilizar estas variables en los comandos? ¿Los comandos variarían algo para ambos destinos?
La variable especial $< se utiliza para indicar el primer prerrequisito y la variable $@ se expande siempre al destino actual.
Utilizando estas variables, es posible generalizae la regala completa de la siguiente manera:
$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : \ $(JANSDIR)/% \ $(LAYOUT) { \ cat header.html $< ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $@Voilà! ¡Esta única línea funciona ahora para ambos ficheros!
Para finalizar, se muesta el Makefile completo, incluyendo algunas optimizaciones:
# Este Makefile construye el website de Piet y Jan. # Directorio donde el website es publicado: TARGETDIR = /home/piet/public_html # Directorio de Jan: JANSDIR = /home/jan/Docs/website # Ficheros necesarios para el diseño: LAYOUT = header.html footer.html # Estas son las páginas web: DOCS = $(TARGETDIR)/index.html $(TARGETDIR)/offer.html # Por favor no cambiar nada bajo esta línea ;-) # ------------------------------------------------------------- all: $(DOCS) $(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT) { \ cat header.html $< ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $@ # finEsto empieza a tomar la forma que debería tener. Si se añaden más documentos, es bastante fácil incorporarlos al Makefile, utilizando la variable DOCS, y sin teclear demasiado.
Al final, la persona que mantiene el Makefile debería ver fácilmente cómo funciona, sin romperse la cabeza inetentando adivinar sun funcionamiento.
TEXTS = index.html offer.html yetanotherfile.html # Por favor, no cambiar nada bajo esta línea ;-) # ------------------------------------------------------------- DOCS = $(addprefix $(TARGETDIR)/,$(TEXTS)) all: $(DOCS) # etcéteraLo que vemos aquí es una función make especial: en lugar del nombre de una variable, es posible usar una expresión completa entre paréntesis. De esta manera es posible modificar textos de muchas maneras.
El comando especial $(addprefix prefix,list) añade un prefijo a cada elemento de la lista. En el ejemplo, es el contenido de TARGETDIR más un slash (/).
Los elementos listados se separan por espacios. Por esta razón, no es una buena idea procesar nombres de fichero que contengan espacios con el comando make.
Para concluir: al comienzo, ya se mencionó que el destino "all" no crearía un fichero con el nombre "all" (esta línea no contiene ningún comando) y en consecuencia, este destino es ejecutado siempre. Pero, ¿cómo manejarlo si, accidentalmente, sí que existe un fichero con ese nombre, y es más reciente que los otros ficheros ...?
Hay una forma sencilla de decirle a make que un determinado destino siempre debe ejecutarse y que este destino no hace referencia a ningún fichero en el disco duro. Para lograr esto, el destino es markado como 'phony' (no real). Se hace de la siguiente forma:
.PHONY: allahora, el Makefile completo tendrá el siguiente aspecto:
# Este makefile construye el website de Piet y Jan. # Directorio donde se publica el website: TARGETDIR = /home/piet/public_html # Directorio de Jan: JANSDIR = /home/jan/Docs/website # Ficheros necesarios para el diseño: LAYOUT = header.html footer.html # Estos son los nombres de las páginas web: TEXTS = index.html offer.html yetanotherfile.html # Por favor, no realizar cambios bajo esta línea;-) # ------------------------------------------------------ DOCS = $(addprefix $(TARGETDIR)/,$(TEXTS)) .PHONY: all all: $(DOCS) $(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT) { \ cat header.html $< ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $@ # fin¡Guarden este archivo y olvídenlo! ¡De ahora en adelante, es posible mantener sus páginas web, quizá utilizando su crontab, y separar el diseño del contenido!
Por ejemplo, simplemente la manera en que se genera un documento no está exenta de errores: si Jan finaliza accidentalmente sus artículos con </body></html>, la mayoría de navegadores no visualizarán el pie creado por Piet. Si se recurre a grep, perl o tcl, es posible colocar de forma inteligente algunos títulos de los documentos de Jan en la cabecera del site.
Por supuesto, Jan simplemente puede escribir texto plano y utilizar el comando sed para sustituir todas las líneas en blanco (retornos de carro) por <P>:
sed -e 's/^\s*$/<p>/g'Además, Jan puede escribir sus textos en LyX y utilizar un programa como lyx2html para convertirlo a html. ¡Existen miles de posibilidades!
También es posible realizar otra construcción variable.
No se ha considerado cuántas imágenes se han transportado (a escala, convertidas o comprimidas) al directorio web. ¡También es posible automatizar este proceso!
En este ejemplo, Piet debería tener permiso de lectura en el directorio de Jan en el website. Lo interesante de separar estas tareas es que se pueden aplicar a organizaciones muy grandes. Piet puede incluso logearse desde la otra punta del mundo, o montar su directorio home sobre NFS. Los ejemplos también se pueden utilizar en trabajos realizados por un único usuario.
Espero que hayan quedado claros los principios de funcionamiento de Makefile y lo fácil que puede resultar el trabajo diario cuando se ha escrito un buen Makfile.
Utilizando destinos 'phony' (. PHONY: destino), resulta sencillo agrupar funciones simples. Un ejemplo es configurar el kernel de Linux.
Tecleando make menuconfig comienza la configuración con un menú interactivo. Tecleando make xconfig comienza la configuración con un interfaz Tcl/Tk bajo X.
Los dos destinos mencionados anteriormente no tienen nada que ver con la construcción read del kernel. Constituyen un simple interfaz para las funciones necesarias (como configurar el kernel).
Podrían crear un Makefile con los siguientes destinos PHONY:
De esta forma, es posible generar HTML desde un fichero de texto y mejorar el diseño del fichero HTML recientemente obtenido. Un ejemplo:
TEMPLATE = layout1/Template1.txt /home/httpd/sales/sales.html: sales.html $(TEMPLATE) perl Scripts/BuildPage.pl -template $(TEMPLATE) $< > $@-new mv -f $@-new $@ sales.html: sales.txt aptconvert -toc $@ $<Observar cómo el fichero sería actualizado cuando Template1.txt hubiera combiado.
Si un comando va precedido por '@', el comando make no lo visualiza:
destino: prerrequisito @cc -o destino prerrequisitoSi un comando comienza por '-' el proceso make no terminará si esta comando genera un error (por ejemplo, borrando un fichero que no existe):
.PHONY: clean clean: -rm -r $(tempdir)Si se desea ver lo que hace un determinado comando, por ejemplo make install, pero realmente no se desea ejecutar el comando, utilizar la opción -n en el prompt:
wilbert@nutnix:~ > make -n install install -m 755 program /usr/local/bin install -m 644 program.1 /usr/local/man/man1 wilbert@nutnix:~ >
Si necesita el signo de dólar ($) como parte, por ejemplo, de un nombre de fichero o comando shell . utilícelo duplicado ($$):
# Un makefile # ¡No lo intenten en casa! :-) source = menu.txt help.txt target: $(source) for i in $(source) ;\ do \ if [ "$$i" = "menu.txt" ] ;\ then \ doThis $$i ;\ else \ doThat $$i ;\ fi ;\ done > targetAntes de enviar el comando a la shell para su ejecución, make sustituirá sus propias variables y cambiará los dobles signos de dólar por un sólo signo.
info makePor supuesto, es posible leer el GNU Make Manual con los browsers de ayuda de GNOME y KDE o el práctico programa tkinfo.
Enlaces a más información sobre make:
|
Contactar con el equipo de LinuFocus
© Wilbert Berendsen, FDL LinuxFocus.org Pinchar aquí para informar de algún problema o enviar comentarios a LinuxFocus |
Información sobre la traducción:
|
2001-07-02, generated by lfparser version 2.9