Hogar Mapa Indice Busqueda Noticias Arca Enlaces Sobre LF
[Top bar]
[Bottom bar]
Este artículo está disponible en los siguientes idiomas: English  Castellano  Deutsch  Francais  Nederlands  Portugues  Russian  Turkce  

convert to palmConvert to GutenPalm
or to PalmDoc

[Wilbert Berendsen]
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:

¡Trabaja con make!

[Illustration]

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.



 

Introducción

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.  

Ejemplo: construyendo un website

Queremos construir un website mantenido por diferentes personas. Jan se ocupa de dos páginas y las mantiene. Piet se ocupa de la composición.

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.html
Por 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.html
Este 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.  

Primer encuentro con make

El manual de GNU para make es un documento estupendo. Sin embargo, desde el principio se orienta hacia un entorno de programación. Por esta razón, yo intento mostrar las funciones de make en un sentido más amplio:
    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.  

Sintaxis del Makefile

El Makefile se puede crear con un editor de texto y tiene el siguiente aspecto:
# 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.  

Un Makefile para nuestro ejemplo

En nuestro ejemplo, el Makefile debería tener el siguiente aspecto:

# 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!  

Simplificando el Makefile

 

Variables

Gracias a las variables, un Makefile se puede simplificar significativamente. Las variables se definen de la siguiente manera:
variable = valor
Nos 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

# fin

Es 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!  

Reglas Patrón

Las 'Reglas Patrón' permiten utilizar el mismo conjunto de comandos en cualquier tipo de destinos diferentes.

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 ...
	comando
El 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/% : %
	comandos
Si 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
	comandos
El 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?  

Variables automáticas

Afortunadamente, make define algunas variables para sí mismo. Algunas de esas variables son llamadas variables automáticas. Estas vaiables contienen, durante la ejecución de los comandos (mejor: justo antes de ejecutar esos comandos), el valos del destino y/o prerrequisito.

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                           ;\
        } > $@

# fin
Esto 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.  

Pequeñas optimizaciones finales

A lo mejor se prefiere mencionar los documentos en DOCS, sin incluir el directorio completo. Esto puede hacerse de la siguiente manera (cambiamos DOCS al principio del Makefile por TEXTS):
TEXTS = index.html  offer.html  yetanotherfile.html

# Por favor, no cambiar nada bajo esta línea ;-)
# -------------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))

all: $(DOCS)

# etcétera
Lo 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: all
ahora, 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!  

Observaciones finales

Obviamente, es posible modificar este ejemplo para adaptarlo a otras situaciones.

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.  

Consejos

 

Más información

Más información sobre el funcionamiento de make y todas sus otras posibilidades se pueden encontrar en el 'GNU Make Manual'. Pueden leer este manual en su sistema linux con el siguiente comando:
info make
Por 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:

¡Diviertanse!  

Formulario de "talkback" para este artículo

Cada artículo tiene su propia página de "talkback". A través de esa página puedes enviar un comentario o consultar los comentarios de otros lectores
 Ir a la página de "talkback" 

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:
nl -> -- Wilbert Berendsen
nl -> en Philip de Groot
en -> es Begoña Blanco

2001-07-02, generated by lfparser version 2.9