Ficheros Make FICHEROS MAKE A todos nos llega la hora de tratar con los dichosos archivos Makefile. Aun si no vamos a programar es esencial conocer un poco el funcionamiento de estos archivos usados por el compilador make. Normalmente no tendremos muchos problemas con ellos, pero cuando surgen debemos saber mas o menos a que es debido el error. En este manual intentaré explicar las bases de este tipo de archivos. Consideraciones previas Con make creamos un fichero de forma escalonada. Si tenemos varios archivos y uno solo cambia podemos recompilar solo el archivo que ha cambiado sin necesidad de recompilar todo el código. Debemos saber como actúa gcc, del que depende make, entre otros. Teniendo un archivo fuente llamado "ZX80.c" debemos obtener el ejecutable "ZX80" mediante un archivo objeto que se creará al compilar llamado "ZX80.o". La sintaxis es la siguiente: gcc -o ZX80 ZX80.c El compilador creará el archivo objeto "ZX80.o" que luego transformará en ZX80, el deseado ejecutable.En caso de que hayan más de un archivo fuente se deberian compilar todos de la siguiente manera: gcc -c ZX80-1.c gcc -c ZX80-2.c gcc -c ZX80-3.c gcc -o ZX80 ZX80-1.o ZX80-2.o ZX80-3.o Lo que nos dará el archivo ejecutable "ZX80". Imaginaos si en vez de 3 archivos fuente hubieran 200 y en varios directorios..... La faena sería demasiado engorrosa, ¿no creeis?. Cómo funciona MAKE Make trabaja con resultados y requisitos.Vamos a ver un ejemplo de un fichero Makefile muy sencillo: edimh: main.o edit.o gcc -o edimh main.o edit.o main.o: main.c gcc -c main.c edit.o: edit.c gcc -c edit.c Este fichero define la construcción de un programa llamado "edimh" a partir de 2 archivos fuentes llamados "edit.c" y "main.c". El orden de las entradas es lo de menos ya que no va por orden de escritura, si no por orden de dependencias aunque es bueno poner en la primera línea la construcción del ejecutable para que se establezca el nombre del ejecutable. Make ya sabe lo que tiene que hacer. file:///E|/Archivos de programa/explore2fs/make.html (1 de 6) [18/04/2003 23:23:24] Ficheros Make Hay tres entradas en el make. Cada una tiene una Linea de dependencias que muestra como construir el archivo. Así la primera linea dice que "edimh" (el resultado antes de los dos puntos) se construye a partir de "main.o" y "edit.o" (los requisitos, detras de los dos puntos y que se ejecuta primero). Obtenemos los objeto a partir de fuentes y el ejecutable a partir de los objeto. NOTA: La linea que contiene comandos ha de empezar con tabulador, no con espacios. El comando "make edimh" ejecuta la linea gcc correspondiente si no hay ningún fichero llamado "edimh", aunque tambien lo hace si "edimh" cambia alguno de sus archivos objeto. ¿Y como sabe make que un fichero ha cambiado?. Simplemente comparando la fecha y hoa de modificación de ambos ficheros. Probemos lo siguiente: ZX80# make edimh gcc -c main.c gcc -c edit.c gcc -o edimh edit.o main.o Ahora modificamos main.c y volvemos a lanzar make: ZX80# make edimh gcc -c main.c gcc -o edimh edit.o main.o UFFFF !!!!! Un descanso por favor....Ahora se muestra un script algo más elaborado. Haber si sabes que hace: install: all mv edimh /usr/local mv readimh /usr/local all: edimh readimh readimh: read.o edit.o gcc -o readimh main.o read.o edimh: main.o edit.o gcc -o edimh main.o edit.o main.o: main.c gcc -c main.c edit.o: edit.c gcc -c edit.c read.o: read.c gcc -c read.c En primer lugar se define como resultado install con all como requisito (se le denomina resultado falso o phony target) que permite que se ejecuten todos los comandos relacionados. De esta manera make presta atención al resultado "all" que carece de comandos pero que depende de "edimh" y "readimh", por lo que se sigue retrocediendo para buscar dependencias hasta llegar a los .c que no dependen de nada. La ejecución de dicho programa daria la siguiente salida: Ficheros Make ZX80# make install gcc -c main.c gcc -c edit.c gcc -o edimh edit.o main.o gcc -c read.c gcc -o readimh main.o read.o mv edimh /usr/local mv readimh /usr/local Se crean los objeto, luego se consiguen los ejecutables y con ambos ejecutables creados se satisfacen las condiciones de "all" por lo que se procede a "install" que cambia de lugar los ejecutables. ¿SENCILLO NO? Algunas reglas de escritura Make en este aspecto es bastante "tonto". Si usamos espacios en vez de tabuladores se nos quejará y los mensajes de error suelen ser bastante confusos. ● Los comandos van SIEMPRE precedidos de Tabuladores. No pongas nunca un tabulador en otro sitio que no sea un comando. ● Los comentarios se escriben detras de una almohadilla (#). Todo lo que le siga se ignora. ● Si queremos extender una linea larga en dos porque sea muy larga se pondrá una barra invertida (\) ● Si se quiere evitar el eco de un comando pondremos una arroba (@) delante de la llamada: @if [-x /bin/user]; then ● Con un guión (-) delante de un comando forzamos a que se continúe con el proceso aunque falle el comando. Macros Se usan para utilizar repetidamente una expresión y no tener que escribirla cada dos por tres, es decir, se expande en el valor que tiene. Se escriben de la siguiente manera: $(expresion). Vamos a poner un ejemplo: OBJECTS = main.o edit.o PATHS = /usr/local/bin edimh: $(OBJECTS) gcc -o edimh &(OBJECTS) mv edimh $(PATH)/nuevo Este programa hará que se efectue el ejecutable edimh con los objetos que marca OBJECTS que son "main.o" y "edit.o". Luego copiará el ejecutable a la ruta que le marca PATH que es /usr/local/bin con el Ficheros Make añadido /nuevo que se especificadetras de $(PATH). Las mayúsculas no son imprscindibles, pero ayudan a diferenciar a simple vista las macros. Una extensión del GNU Make es la de añadiruna expresión a la que ya tiene la macro. Se hace con el operador := DRIVERS = drivers/block/block.a ifdef CONFIG_SCSI DRIVERS := $(DRIVERS) drivers/scsi/scsi.a endif La expresión DRIVERS equivale a drivers/block/block.a pero si se define la macro CONFIG_SCSI la expresión equivaldrá a drivers/scsi/scsi.a por lo que la macro valdrá entoces drivers/block/block.a drivers/scsi/scsi.a. ¿Y como definimos CONFIG_SCSI?. Pues sencillamente añadiendo una linea del tipo: CONFIG_SCSI = yes. Tambien se puede definir externamente, en la propia llamada: # make CONFIG_SCSI=yes install. Sufijos y precedencias Linux funciona de una manera simple con las dependencias ya expuesta: Hacer los objetos de los fuente y los ejecutables de los objetos. Para aprovechar esto se echa mano de la "regla de sufijos": .c.o: gcc -c $(CFLAGS) $< La linea .c.o le dice a make que use los .c como requisitos para los .o. La macro CFLAGS es de propósito general y sirve para escribir cualquier opción de compilación que queramos, por ejemplo -g para depurar. La expresión $< significa "el requisito", por lo tanto sustituye al nombre de listado C. Para usarlo se escribe: ZX80# make CFLAGS="-g" edit.o gcc -c -g edit.c No hay por qué asignar CFLAGS en el make, ya que se puede especificar dentro del script mediante: CFLAGS = -g. Hay una opción que merece la pena citar. Se trata de la opción -D, que define algunos simbolos en el listado fuente. Se emplean en C muchos símbolos (-DDEBUG, -DBSD, -DKERNEL...)marcados por la directiva "ifdefs" por lo que necesitamos alguna manera de pasárselos al make. Se puede hacer mediante la linea de comandos: make CFLAGS="-DDEBUG _DKERNEL..."; o tambien se pueden añadir en el script mediante las "Reglas de patrones", que son más potentes que las reglas de sufijos. Se usa el signo de porcentaje (%) para definirlas en vez del nombre completo: %.o: &.c gcc -c -o $@ $(CFLAGS) $< En este caso el fichero de salida %.o aparece en primer lugar y su requisito %.c el último y tras dos Ficheros Make puntos. La expresión $< se refiere a los requisitos mientras que $@ a la salida (resultado), de manera que representa al nombre del archivo objeto que queremos conseguir (por el que pusimos un % antes). Estas dos expresiones son macros prestablecidas y se actualizan cada vez que se ejecuta una entrada. Otra macro preestablecida es $* que representa el nombre de los requisitos pero sin su extensión (tipo comodin de consola). Por ejemplo, si el requisito es edit.c la expresión $*.s valdría edit.s, que sería un archivo fuente en ensamblador. Bueeeeeeeeno.... Vamos a darle una vuelta de turca más. Veamos algo que se puede hacer con estas reglas pero no con la regla de sufijos: Añadir una expresión al nombre del resultado. Añadiremos _gdb que indica que el objeto tiene información de depuración: %_gdb.o: %.c gcc -c -g -o $@ $(CFLAGS) $< DEBUG_OBJECTS = main_dbg.o edit_dbg.o edimh_dbg: $(DEBUG_OBJECTS) gcc -o $@ $(DEBUG_OBJECTS) Ahora se puede construir los objetos con o sin depuración. Al tener nombres distintos no se reescribirán: ZX80# make edimh_dbg gcc -c -g -o main_dbg.o main.c gcc -c -g -o edit_dbg.o edit.c gcc -o edimh_dbg main_dbg.o edit_dbg.o Comandos múltiples Podemos ejecutar cualquier comando de intérprete dsde el fichero make, aunque la ejecución es algo más complicada porque cada comando se lanza bajo un intérprete distinto. La siguiente sintaxis no haría lo que se espera: target: cd obj HOST_DIR=/home/zx80 mv *.o $HOST_DIR Ni "cd" ni "HOST_DIR" tienen el menor efecto en los comandos. Para que lo tengan se deben escribir en la misma linea separados por punto y coma (;): target: cd obj ; HOST_DIR=/home/zx80 ; mv *.o $HOST_DIR Una anotación: Para usar las variables de entorno dentro de los comandos hay que ponerles doble signo de dólar ($$). Esto informa a make de que se trta de una variable de entorno y no de una macro: target: cd obj ; HOST_DIR=/home/zx80 ; mv *.o $$HOST_DIR A veces los propios ficheros hacen una llamada a make, lo que da lugar a un "make recursivo", estas Ficheros Make llamdas son algo así: linuxsubdirs: dummy set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done La macro $(MAKE) hace una llamada a make. Una posible aplicación de esta técnica es la construcción en varios directorios, cada uno de los cuales debería tener su propio fichero make. Además se permite lanzar un intérprete de comandos y asignar la salida a una macro. Por ejemplo con HOST_NAME = $(shell uname -n). Con esto se consigue asignar el nombre del nodo de red a la macro HOST_NAME. Inclusión de otros ficheros make Un proyecto de cierta envergadura crea varios ficheros make, lo que permite compartir ciertos recursos, sobre todo definiciones de macros. Para incluir un fichero dentro de otro usamos: include nombrefichero. A veces estas inclusiones pueden hacer referencia a macros: include $(INC_FILE) Autoconf y Automake Escribir un fichero de configuración de complilación es una tarea demasiado pesada por lo que se dispone de las herramientas Automake y Autoconf. Son muy difíciles de explicar en este mini-manual, pero si os interesa el tema podeis visitar: ftp://ftp.gnu.org/gnu