Make

Make es una herramienta muy habitual en sistemas UNIX como es el caso de GNU/Linux, con la que se puede orquestar la compilación de programas compuestos de múltiples archivos, en base a reglas. Este es un tutorial un poco breve para introducir al uso de Make allá donde haga falta, pero tengo un curso de Make con más duración, por si quieres profundizar en algunos de los temas, que puedes ver en https://www.makigas.es/series/make.

En el desarrollo de proyectos en C, especialmente cuando trabajamos desde la línea de comandos, nos encontramos con la necesidad de ejecutar comandos largos y repetitivos para compilar nuestro código. Estos comandos, como gcc -c -g -I -Wall, pueden ser tediosos de recordar y escribir, sobre todo cuando el proyecto crece y requiere múltiples pasos para compilar diferentes archivos y bibliotecas. Para simplificar este proceso, podemos recurrir a una herramienta muy útil llamada Make, que nos permite automatizar y gestionar la compilación de manera eficiente.

Make funciona a partir de un archivo llamado Makefile, donde definimos una serie de recetas o reglas que indican cómo construir los distintos objetivos de nuestro proyecto, como ejecutables o bibliotecas. Cada receta especifica un objetivo, sus dependencias y los comandos necesarios para generarlo. Esto nos permite tener un único archivo que controle todo el proceso de compilación, evitando tener que escribir manualmente cada comando.

Una de las grandes ventajas de Make es que nos obliga a pensar en nuestro proyecto como un conjunto de dependencias. Por ejemplo, si tenemos un programa llamado contable que depende de un archivo objeto contable.o y de una biblioteca estática lib/calculadora.a, debemos asegurarnos de que estas dependencias estén actualizadas antes de compilar el ejecutable. A su vez, lib/calculadora.a puede depender de otros archivos objeto, que a su vez se generan a partir de archivos fuente .c. Make construye un grafo de dependencias que garantiza que todo se compile en el orden correcto.

Además, Make optimiza la compilación al recompilar únicamente lo que ha cambiado. Si modificamos solo utiles.c, Make detectará que solo es necesario recompilar utiles.o y actualizar las dependencias relacionadas, sin recompilar todo el proyecto. Esto acelera significativamente el proceso y evita trabajo innecesario.

Veamos un ejemplo práctico de un Makefile para un proyecto con estas características:

# Receta para generar el ejecutable contable
contable: contable.o lib/calculadora.a
	@gcc -o contable contable.o lib/calculadora.a

# Receta para generar la biblioteca estática lib/calculadora.a
lib/calculadora.a: lib/calculadora.o
	@ar crs lib/calculadora.a lib/calculadora.o

# Receta para compilar el archivo objeto lib/calculadora.o
lib/calculadora.o: lib/calculadora.c
	@gcc -c -o lib/calculadora.o lib/calculadora.c

# Receta para compilar el archivo objeto contable.o
contable.o: contable.c
	@gcc -c -o contable.o contable.c

# Receta para limpiar los archivos generados
clean:
	@rm -f contable contable.o lib/calculadora.a lib/calculadora.o

En este Makefile, cada objetivo está claramente definido con sus dependencias y comandos. Por ejemplo, para generar contable, Make primero verifica que contable.o y lib/calculadora.a estén actualizados. Si no lo están, ejecuta las recetas correspondientes para compilarlos. El uso del símbolo @ antes de los comandos evita que Make imprima el comando en la salida, permitiéndonos añadir mensajes personalizados si lo deseamos.

También es común incluir una receta llamada clean que elimina todos los archivos generados durante la compilación, dejando el proyecto limpio para una compilación desde cero.

Al ejecutar make contable, Make analiza las dependencias y ejecuta solo los comandos necesarios para construir el ejecutable. Si modificamos un archivo fuente, solo recompilará lo afectado, ahorrándonos tiempo y esfuerzo.

En definitiva, Make nos ofrece una forma clara, organizada y eficiente de gestionar la compilación de proyectos en C, especialmente cuando trabajamos sin IDEs y desde la línea de comandos. Nos permite automatizar tareas complejas, gestionar dependencias y acelerar el desarrollo, todo ello con un archivo sencillo y fácil de mantener.

Lista de reproducción
  1. 1
    Instalar CodeBlocks
    15 minutos
  2. 2
    Funciones y hola mundo
    17 minutos
  3. 3
    Variables y tipos de datos
    17 minutos
  4. 4
    Condicionales y operadores lógicos
    16 minutos
  5. 5
    Bucles
    11 minutos
  6. 6
    Punteros
    12 minutos
  7. 7
    Arrays
    14 minutos
  8. 8
    Estructuras
    12 minutos
  9. 9
    Otras construcciones de C
    9 minutos
  10. 10
    Memoria dinámica
    8 minutos
  11. 11
    El preprocesador (parte 1)
    10 minutos
  12. 12
    El preprocesador (parte 2)
    9 minutos
  13. 13
    Archivos de cabecera y múltiples .c (parte 1)
    8 minutos
  14. 14
    Archivos de cabecera y múltiples .c (parte 2)
    9 minutos
  15. 15
    C desde la línea de comandos (parte 1)
    8 minutos
  16. 16
    C desde la línea de comandos (parte 2)
    9 minutos
  17. 17
    Break y continue
    10 minutos
  18. 18
    Goto
    13 minutos
  19. 19
    Manipulación de bits
    15 minutos
  20. 20
    Máscaras de bit
    19 minutos
  21. 21
    Archivos (1): fopen y fclose
    13 minutos
  22. 22
    Archivos (2): leer con fgetc
    9 minutos
  23. 23
    Archivos (3): fseek y ftell
    11 minutos
  24. 24
    Archivos (4): leer con fgets
    9 minutos
  25. 25
    Archivos (5): fputc y fputs
    7 minutos
  26. 26
    Archivos (6): volcar en archivos con fwrite
    10 minutos
  27. 27
    Archivos (7): fread, fwrite y los arrays
    10 minutos
  28. 28
    Archivos (8): entrada estándar y salida estándar
    9 minutos
  29. 29
    Archivos (9): buffers
    14 minutos
  30. 30
    Archivos (y 10): otras funciones útiles con archivos
    5 minutos
  31. 31
    printf (1)
    18 minutos
  32. 32
    printf (parte 2)
    12 minutos
  33. 33
    scanf (parte 1)
    17 minutos
  34. 34
    scanf (parte 2)
    17 minutos
  35. 35
    fprintf, sprintf y snprintf
    8 minutos
  36. 36
    Tipos de datos opacos
    13 minutos
  37. 37
    Bibliotecas estáticas
    13 minutos
  38. 38
    Bibliotecas dinámicas
    15 minutos
  39. 39
    Más flags: i mayúscula (include), wall, werror, pedantic...
    12 minutos
  40. 40
    pkg-config
    12 minutos
  41. 41
    Make
    17 minutos
  42. 42
    GDB
    21 minutos
  43. 43
    Variables globales
    6 minutos
  44. 44
    extern
    9 minutos
  45. 45
    Funciones variádicas
    12 minutos
  46. 46
    El optimizador de GCC y la opción -O
    12 minutos
  47. 47
    Volatile
    6 minutos