diff --git a/book/21-makefiles.md b/book/21-makefiles.md
new file mode 100644
index 0000000..3cc0834
--- /dev/null
+++ b/book/21-makefiles.md
@@ -0,0 +1,1808 @@
+# Capítulo 21 — Makefiles: de "compilar a mano" a proyectos profesionales
+
+> Capítulo avanzado, pero **escrito para que se entienda de verdad**. Se apoya en funciones (`05`),
+> arreglos y cadenas (`06`), punteros (`07`), structs (`08`), memoria dinámica (`09`) y archivos
+> (`10`). También asume que ya compilaste un programa con `gcc`.
+>
+> Este capítulo no es solo sobre `make`: es sobre **automatizar tu flujo de trabajo**. Cada vez que
+> repetís el mismo comando dos veces, algo anda mal. Acá vas a aprender a dejar que una herramienta
+> se encargue de compilar, enlazar y construir bibliotecas.
+
+---
+
+## Cómo leer este capítulo
+
+Este es el capítulo donde "compilar a mano" se convierte en "**la máquina compila por mí**". Ya no vas
+a escribir la línea de gcc cada vez. Vas a escribir **las reglas** y dejar que una herramienta lo haga.
+
+Está dividido en **ocho partes**. Después de cada concepto importante hay uno o varios ejercicios
+cortos. **No los saltees**: son la mitad del capítulo.
+
+| Parte | De qué habla | La idea en una frase |
+|---|---|---|
+| **1** | El problema: compilar a mano | Cada cambio obliga a recompilar todo con el mismo comando largo: rápido, repetitivo, propenso a error. |
+| **2** | Makefile básico | `make` ejecuta tus reglas: variables, targets, dependencias y recipes. |
+| **3** | Patrones y automático | `%`, `$@`, `$^`: escribir una regla que sirve para cualquier `.c`. |
+| **4** | Variables avanzadas | `+=`, `-=` , `?=` : configurar tu build sin copiar-pastear. |
+| **5** | Bibliotecas estáticas | `ar` y `ranlib`: empaquetar objetos en un archivo que el linker usa como si fuera código. |
+| **6** | Bibliotecas dinámicas | `gcc -shared -fPIC`: crear `.so`/`.dll` que se cargan al ejecutar, no al compilar. |
+| **7** | Makefiles profesionales | Include condicional, sanitizers, targets implícitas y variables integradas de gcc. |
+| **8** | Integrador multi-subdirectorio | Proyectos con `lib/`, `tests/` y `$(MAKE) -C
` para orquestar builds anidados. |
+| **9** | Instalación y configuraciones | `PREFIX`, `make install/uninstall`, feature toggles (`RENDERER=sdl|opengl|vulkan`), recursos propios. |
+
+Los **ejercicios** se numeran en orden: `21.1`, `21.2`, `21.3`, y así. Cada uno trae su **escenario de
+prueba** (qué hacés y qué tiene que pasar) para que lo ejecutes y compruebes.
+
+> **Regla de oro del libro:** el capítulo explica y muestra **cómo funciona** cada cosa (con comandos
+> y Makefile reales), pero **los ejercicios no traen la solución**. El enunciado te da el problema; la
+> respuesta la escribís vos.
+
+---
+
+# PARTE 1 — El problema: compilar a mano
+
+Empecemos por lo que ya hacés (o harías) hoy. Y por qué eso se vuelve un problema rápido.
+
+## 1.1 "Compilar con gcc" no es una vez, es siempre
+
+La primera vez que compilas algo, el comando parece inocente:
+
+```bash
+$ gcc main.c -o programa
+```
+
+Funciona. Pero ahora imaginá esto: tenés un proyecto con **diez archivos `.c`** y tres de ellos
+cambiaron desde la última compilación. ¿Qué hacés?
+
+Opción A — recompilar todo:
+
+```bash
+$ gcc main.c file1.c file2.c utils.c helper.c parser.c config.c network.c io.c mathops.c \
+ -o programa -Wall -Wextra -I./include -L./lib -lmylib -lm
+```
+
+Opción B — recompilar solo lo que cambió (si te acordás cuáles):
+
+```bash
+$ gcc main.c file1.c file2.c utils.c helper.c parser.c config.c network.c io.c mathops.c \
+ -o programa -Wall -Wextra -I./include -L./lib -lmylib -lm
+```
+
+Sí, misma opción. Porque **gcc no sabe qué archivos cambiaron**: vos tenés que decirle cuáles
+recompilar y cuáles reusar. Y la línea de arriba tiene **doce archivos**, **seis banderas**, **tres
+flags de linker**. Si te olvidás de uno, el programa no compila bien. Si escribís mal `-Wall`, no
+ves advertencias nuevas.
+
+Y esto es lo peor: **cada vez que hagas un cambio**, tenés que ejecutar esa línea otra vez. O una
+variante si querés optimizar para producción (`-O2`). O con sanitizers para debugging
+(`-fsanitize=address,undefined`). El comando se multiplica y el error humano entra por la puerta grande.
+
+> **La analogía del chef.** Compilar un programa a mano es como ser chef, lavaplatos y mozo al mismo
+> tiempo. Cada vez que cocinás algo nuevo, tenés que recordar exactamente qué ingredientes usaste,
+> en qué orden, con qué fuego, y repetir el proceso cada vez que alguien pida lo mismo. Un sistema de
+> build es como un **chef asistente** que se acuerda de todo: "solo los platos que cambiaron necesitan
+> prepararse de nuevo".
+
+## 1.2 ¿Por qué `make`? ¿No hay alternativas?
+
+Sí las hay: `cmake`, `ninja`, `bazel`, `meson`, `just`. Pero `make` es **universal**: está en Linux,
+macOS (con un `brew install make`), Windows (MSYS2/MinGW). Es el **estándar mínimo** que todo programador
+C debería conocer. Las otras herramientas son capas encima de `make` o para proyectos más grandes:
+
+```text
+ cmake → genera Makefiles (o Ninja, o Xcode, o Visual Studio projects)
+ ninja → ejecuta reglas muy rápido (usa un formato propio)
+ bazel → multiproyecto, multi-lenguaje, con cache de build
+ just → reemplazo moderno de make con syntax más limpia
+```
+
+Conocer `make` no significa que siempre uses `make`. Significa que **entendés el concepto de
+"reglas de build"**, que es la idea central. Las otras herramientas comparten esa misma idea: tenés
+**dependencias**, un **target** y una **receta**. Si entendés eso, podés migrar a cualquier otra cosa.
+
+## 1.3 La filosofía de `make`
+
+Antes de escribir la primera regla, entiende la **pregunta central** que `make` se hace:
+
+> **"¿Este target necesita reconstruirse?"**
+
+Y responde con un solo algoritmo:
+
+```text
+ ¿el archivo del target existe? → no → ejecutar la receta
+ ¿la fecha de alguna dependencia es más reciente que el target? → sí → ejecutar la receta
+ ¿todas las dependencias son más viejas? → no → SKIP (ya actualizado)
+```
+
+Es **todo o nada**: si una dependencia cambió, reconstruís. Si no, no hacés nada. Este "no hacer
+nada" es lo que hace a `make` tan rápido en proyectos grandes: solo se toca lo necesario.
+
+> **Eso de "fecha más reciente"** es la clave. `make` no sabe qué código cambiaste; mira los **timestamps**
+> de los archivos. Si modificaste `main.c`, su timestamp se actualizó. Si el ejecutable tiene un
+> timestamp anterior, `make` decide que hay que recompilar. Simple y efectivo.
+
+---
+
+# PARTE 2 — Makefile básico: tu primera regla
+
+Empezamos con lo más simple posible. Un solo archivo `.c`, un comando de compilación. Sin optimizaciones,
+sin sanitizers. El equivalente a `gcc main.c -Wall -o programa` pero dentro de un Makefile.
+
+## 2.1 La estructura mínima de un Makefile
+
+Un **Makefile** (o `makefile`, que es lo mismo) es un archivo de texto con una sintaxis muy estricta:
+
+```text
+ target : dependencias
+ ← ¡ESTE SPACIO ES UN TABULADOR, NO ESPACIOS!
+```
+
+Cada línea de receta **debe empezar con un carácter TAB**. No espacios. Si usás espacios, `make` va a
+decirte:
+
+```text
+Makefile:3: *** missing separator. Stop.
+```
+
+Ese error es el primero que todo el mundo ve y **no significa** "tenés un error de sintaxis": significa
+"la línea de arriba empieza con espacios, poné un TAB".
+
+Acá tenés tu primer Makefile, llamado `Makefile` (sin extensión):
+
+```makefile
+CC = gcc
+CFLAGS = -Wall -Wextra
+TARGET = programa
+SRCS = main.c utils.c parser.c
+OBJS = $(SRCS:.c=.o)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+[TAB]$(CC) $(OBJS) -o $(TARGET)
+
+%.o: %.c
+[TAB]$(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+[TAB]rm -f $(OBJS) $(TARGET)
+```
+
+Vamos a desglosar cada línea. **No es mucho**: son cuatro conceptos y seis líneas de receta.
+
+### Concepto 1: variables
+
+```makefile
+CC = gcc
+CFLAGS = -Wall -Wextra
+```
+
+`CC` es la variable del compilador (por convención; `make` ya trae `CC = gcc` como default, pero es
+bueno declararlo explícitamente). `CFLAGS` es donde van las banderas del compilador. La razón por la que
+las declarás como variables: **podés cambiarlas todas desde un solo lugar**, y también **sobreescribirlas
+desde la línea de comandos**:
+
+```bash
+$ make CFLAGS="-Wall -Wextra -O2" # sobreescribís CFLAGS para este invocation
+$ CC=clang make # usás clang en vez de gcc
+```
+
+### Concepto 2: target `all`
+
+```makefile
+all: $(TARGET)
+```
+
+`all` es el **target por defecto**. Cuando ejecutás `make` sin argumentos, ese es el primer target que
+se construye. Es una convención universal: si alguien ve tu Makefile, espera encontrar un `all`.
+
+### Concepto 3: la regla de compilación
+
+```makefile
+$(TARGET): $(OBJS)
+[TAB]$(CC) $(OBJS) -o $(TARGET)
+```
+
+Esta regla dice: "**para construir** `$TARGET` (que es el ejecutable), necesito los objetos en `$OBJS`.
+Si cualquier `.o` está más nuevo que el `.o`, ejecutá la receta." La receta es el linker: junta todos
+los `.o` y produce el ejecutable.
+
+### Concepto 4: la regla de patrón
+
+```makefile
+%.o: %.c
+[TAB]$(CC) $(CFLAGS) -c $< -o $@
+```
+
+Esta es la **regla de patrón**: dice "cualquier `.o` se construye a partir de un `.c` del mismo nombre,
+con estas banderas". `%` es un comodín: si el target es `main.o`, `%` es `main`; si es `utils.o`, `%`
+es `utils`.
+
+Y `$<` y `$@` son **variables automáticas** (las vemos en detalle más abajo):
+
+| Variable | Qué significa |
+|---|---|
+| `$@` | El target de la regla (`$@` = `main.o`) |
+| `$<` | La primera dependencia (`$<` = `main.c`) |
+| `$^` | Todas las dependencias separadas por espacio |
+
+Con estas tres variables automáticas, **una sola regla de patrón te sirve para cualquier archivo `.c`** del
+proyecto. No necesitás escribir una regla por cada uno.
+
+### Concepto 5: `clean`
+
+```makefile
+clean:
+[TAB]rm -f $(OBJS) $(TARGET)
+```
+
+`clean` **no es un target real**: no produce ningún archivo que se llame `clean`. Es un target "de
+mantenimiento", una orden manual. Por eso existe `.PHONY` (lo vemos en la Parte 7): le dice a `make`
+que `clean` no genera un archivo, así si alguien crea un archivo llamado `clean` por error, `make` igual
+ejecuta la receta.
+
+### La regla de los archivos: `SRCS` y `OBJS`
+
+```makefile
+SRCS = main.c utils.c parser.c
+OBJS = $(SRCS:.c=.o)
+```
+
+La segunda línea usa **sustitución de patrones**: reemplaza `.c` por `.o` en la lista de `SRCS`. El
+resultado es:
+
+```text
+ OBJS = main.o utils.o parser.o
+```
+
+Esto es **automático**: si agregás un nuevo archivo a `SRCS`, `OBJS` se actualiza solo. No tenés que
+tocar nada más.
+
+## 2.2 Cómo se usa `make`
+
+```bash
+$ make # construye el primer target (all → programa)
+$ make clean # borra objetos y ejecutable
+$ make CFLAGS="-Wall -O2" # sobreescribís las banderas solo para este invocation
+```
+
+Cuando ejecutás `make`, ve qué archivos cambiaron, recompila lo que hace falta y enlaza todo. Si no
+cambió nada:
+
+```bash
+$ make
+make: 'programa' is up to date.
+```
+
+> **Momento ajá.** Esa frase —"is up to date"— es el momento más satisfactorio de la programación. Es
+> la máquina diciéndote "no hice nada porque no había que hacer nada". En proyectos grandes, eso puede
+> significar "30 segundos de ahorro en vez de 5 minutos de compilación".
+
+## Práctica de la Parte 2
+
+### Ejercicio 21.1 — El Makefile mínimo
+
+**Conceptos** · variables (`CC`, `CFLAGS`), target `all`, regla de patrón, `$@`, `$<`, `clean`
+
+**Contexto** · Tenés un programa simple con tres archivos: `main.c`, `utils.c` y `parser.c`. Cada uno
+tiene su propia función. El ejecutable se llama `palabra`. Es una simplificación: no hay includes ni
+librerías externas.
+
+**Consigna** · Creá un archivo `Makefile` en el mismo directorio que tus fuentes, con:
+
+1. Variables `CC`, `CFLAGS = -Wall -Wextra`, `TARGET = palabra`, y `SRCS` listando los tres archivos `.c`.
+2. Target `all` que depende de `$(TARGET)`.
+3. La regla de enlace (`$(TARGET): $(OBJS)`).
+4. La regla de patrón para `.o → .c`.
+5. El target `clean`.
+
+Después:
+
+```bash
+$ make clean # borra cualquier cosa vieja
+$ make # compila todo desde cero
+$ ./palabra # ejecutá el resultado
+```
+
+**Entrada** · Ninguna (el programa imprime algo al arrancar).
+**Salida** · El Makefile funciona: `make` compila y enlaza sin errores, y `make clean` borra todo.
+
+**Escenario de prueba**
+
+```text
+$ make
+gcc -Wall -Wextra -c main.c -o main.o
+gcc -Wall -Wextra -c utils.c -o utils.o
+gcc -Wall -Wextra -c parser.c -o parser.o
+gcc main.o utils.o parser.o -o palabra
+
+$ ./palabra
+Hola desde palabra
+
+$ make # segunda vez: nada que hacer
+make: 'palabra' is up to date.
+
+$ make clean
+rm -f main.o utils.o parser.o palabra
+```
+
+**Identificadores sugeridos** · `Makefile`, `TARGET`, `SRCS`, `OBJS`
+**Restricciones/pistas**
+- Usá `$@` y `$<` en las reglas de patrón. No escribas los nombres a mano.
+- `clean` debe borrar todos los `.o` **y** el ejecutable.
+
+**Criterios de aceptación**
+- [ ] `make` compila los tres archivos y genera el ejecutable sin errores.
+- [ ] `make` segunda vez dice "is up to date" (no recompila nada).
+- [ ] `make clean` borra todos los `.o` y el ejecutable.
+- [ ] Se usa `$@` y `$<` en la regla de patrón.
+
+---
+
+### Ejercicio 21.2 — El error del espacio vs tabulador
+
+**Conceptos** · sintaxis de Makefile, caracteres de tabulación, diagnóstico del error "missing separator"
+
+**Contexto** · Este es el primer y más común error al escribir un Makefile. Si lo entendés desde ya,
+no te va a frustrar la primera vez (que sí va a pasar).
+
+**Consigna** · Creá un Makefile con **espacios en vez de TABs** en las recetas (copiá el ejercicio 21.1
+pero reemplazá los TAB por cuatro espacios). Compilalo con `make` y **leé el mensaje de error completo**.
+Después corregilo cambiando los espacios por un TAB real y comprobá que funcione.
+
+Escribí un párrafo corto explicando:
+
+1. Cuál fue el mensaje exacto.
+2. Por qué `make` exige un TAB y no permite espacios (una razón histórica o de parsing).
+3. Cómo podés asegurarte de que tu editor usa TABs en los Makefiles.
+
+**Entrada** · Ninguna.
+**Salida** · El Makefile corregido + párrafo explicativo.
+
+**Escenario de prueba**
+
+```text
+$ make
+Makefile:8: *** missing separator. Stop.
+```
+
+**Restricciones/pistas**
+- La mayoría de los editores modernos permiten configurar "insert spaces instead of tabs" por tipo
+ de archivo. Usá esa configuración para `Makefile`.
+- Si tu editor no lo soporta, podés verificar el Makefile con:
+ ```bash
+ $ cat -A Makefile | grep '^\s' # los TAB aparecen como ^I
+ ```
+
+**Criterios de aceptación**
+- [ ] El mensaje de error está citado textualmente.
+- [ ] La explicación del "por qué TAB" es correcta (historia o parsing).
+- [ ] Se documentó al menos una forma de prevenir el error en tu editor.
+- [ ] El Makefile corregido funciona: `make` compila sin errores.
+
+---
+
+# PARTE 3 — Patrones y variables automáticas
+
+Ya escribiste un Makefile que funciona para tres archivos. Pero ¿qué pasa cuando tenés **cincuenta**?
+Acá entran las reglas de patrón y las variables automáticas, que son la razón por la que `make` no se
+vuelve un archivo de 2000 líneas.
+
+## 3.1 La regla de patrón: una para todas
+
+La regla de patrón usa `%` como comodín:
+
+```makefile
+%.o: %.c
+[TAB]$(CC) $(CFLAGS) -c $< -o $@
+```
+
+Esto le dice a `make`: "**siempre** que necesite construir un archivo que termine en `.o` a partir de
+un archivo con el mismo nombre pero `.c`, usá esta receta." No importa cuántos archivos `.c` tengas:
+una regla de patrón los cubre todos.
+
+Y hay reglas de patrón para **cualquier extensión**:
+
+```makefile
+%.o: %.s # ensamblador → objeto
+[TAB]$(CC) $(ASFLAGS) -c $< -o $@
+
+%.html: %.md # markdown → html (para documentación, por ejemplo)
+[TAB]pandoc $< -o $@
+```
+
+## 3.2 Las variables automáticas completas
+
+`$@`, `$<` y `$^` son las tres más usadas. Pero hay más:
+
+| Variable | Qué significa | ¿Cuándo usarla |
+|---|---|---|
+| `$@` | El target de la regla | Siempre que necesites saber "¿qué estoy construyendo?" |
+| `$<` | La primera dependencia (ordenada) | Cuando necesitás solo una dependencia (ej. un `.c`) |
+| `$^` | **Todas** las dependencias, ordenadas y sin duplicados | Cuando necesitás listar todo (ej. varios `.o` para enlazar) |
+| `$?` | Dependencias **más recientes** que el target | Para compilar solo lo que cambió dentro de la misma receta |
+| `$*` | La parte del comodín (`%`) | Cuando necesitás saber "¿qué archivo es?" (ej. `main.o` → `main`) |
+
+Ejemplo con `$^` en la regla de enlace:
+
+```makefile
+$(TARGET): $(OBJS)
+[TAB]$(CC) $^ -o $@ $(LDLIBS)
+```
+
+### Ejemplo con múltiples dependencias
+
+Imaginate una regla donde el target tiene **varias** dependencias:
+
+```makefile
+programa: main.o utils.o parser.o libmath.a
+[TAB]$(CC) $^ -o $@ -lm # $^ = main.o utils.o parser.o libmath.a
+```
+
+`$^` expande a `main.o utils.o parser.o libmath.a` — **todas las dependencias**. `$?` sería solo las
+más recientes. Y `$@` es `programa`.
+
+## 3.3 Reglas implícitas: `make` ya sabe mucho
+
+Antes de escribir reglas, sabé que `make` tiene **reglas implícitas** (también llamadas "built-in rules")
+para compilaciones comunes. Por ejemplo, `make` **ya sabe** que para construir `main.o` necesita `main.c`:
+
+```text
+ Regla implícita de make:
+ %.o: %.c
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
+```
+
+Esto significa que, en su forma más simple, podés tener un Makefile **vacío**:
+
+```makefile
+# ¡Makefile vacío! make igual compila todos los .o y enlaza.
+all: programa
+programa: main.o utils.o parser.o
+[TAB]$(CC) $^ -o $@
+```
+
+O incluso, si solo tenés un `main.c`:
+
+```bash
+$ touch Makefile # Makefile vacío
+$ make # make usa la regla implícita para main.o y después enlaza
+```
+
+Pero las reglas implícitas **no usan** tus variables personalizadas (`CFLAGS`, etc.) ni tus banderas
+custom. Por eso siempre es mejor escribir las reglas explícitamente: tenés control total.
+
+> **Para guardar.** Las reglas implícitas son un "escape hatch" para proyectos triviales de un solo
+> archivo, pero en cualquier proyecto real **escribí las reglas explícitas**. Porque necesitás `-Wall`,
+> `-Wextra`, `-I./include` y `$(LDFLAGS)`.
+
+## 3.4 El doble punto: dependencias sin archivo
+
+Ya vimos `.PHONY`. Pero hay otros "targets especiales" que empiezan con `.` y no generan un archivo:
+
+| Target | Qué significa |
+|---|---|
+| `.PHONY` | Lista de targets que **no son archivos** (como `clean`, `all`, `install`) |
+| `.SUFFIXES` | Lista de extensiones reconocidas (para reglas implícitas) |
+| `.DEFAULT` | Target por defecto si no se encuentra ninguno (raro, casi nunca usado) |
+
+`.PHONY` es importante: le dice a `make` que esos targets **siempre deben ejecutarse**, incluso si
+existe un archivo con ese nombre. Si no lo declarás y alguien crea un archivo llamado `clean`, `make`
+no va a ejecutar la receta (porque cree que ya está "actualizado").
+
+```makefile
+.PHONY: all clean install
+```
+
+> **La trampa del Makefile sin `.PHONY`.** Es el bug silencioso más común: creás un archivo llamado
+> `clean`, `all` o `install` (por accidente, por un script, por copiar-paste), y de repente `make
+> clean` no hace nada. La solución: **declarar siempre** `.PHONY`.
+
+## Práctica de la Parte 3
+
+### Ejercicio 21.3 — Reglas de patrón para ensamblador
+
+**Conceptos** · reglas de patrón, `$@`, `$<`, `$^`, variables automáticas
+
+**Contexto** · A veces necesitás compilar código en ensamblador (por performance o porque una librería
+externa lo distribuye así). Hacés las reglas necesarias para que `make` compile `.s → .o`.
+
+**Consigna** · Tenés un proyecto con:
+
+- `main.c` — código C principal
+- `utils.c` — código C auxiliar
+- `vector.s` — una función en ensamblador (asumí que tiene una función exportada `vec_add`)
+
+Creá las reglas necesarias para compilar todo y generar el ejecutable `mixto`:
+
+1. Regla de patrón `%.o: %.c`
+2. Regla de patrón `%.o: %.s` con una variable `ASFLAGS`
+3. Regla de enlace que usa `$^`
+
+Asegurate de declarar `.PHONY: all clean`.
+
+**Entrada** · Los archivos fuente (`main.c`, `utils.c`, `vector.s`).
+**Salida** · El Makefile completo y el ejecutable `mixto` funcionando.
+
+**Escenario de prueba**
+
+```text
+$ make
+gcc -Wall -Wextra -c main.c -o main.o
+gcc -Wall -Wextra -c utils.c -o utils.o
+nasm -f elf64 -g -F dwarf vector.s -o vector.o
+gcc main.o utils.o vector.o -o mixto
+
+$ ./mixto
+Vector sum result: 42
+```
+
+**Identificadores sugeridos** · `Makefile`, `ASFLAGS`, `MIXTO`
+**Restricciones/pistas**
+- No necesitás escribir el ensamblador real: asumí que existe y tiene la función `vec_add`.
+- Usá `$^` en la regla de enlace.
+
+**Criterios de aceptación**
+- [ ] Existe una regla de patrón para `.s → .o`.
+- [ ] Se usa `$^` en la regla de enlace.
+- [ ] `.PHONY` incluye `all` y `clean`.
+- [ ] El Makefile compila todo y genera el ejecutable.
+
+---
+
+### Ejercicio 21.4 — Entender las variables automáticas
+
+**Conceptos** · `$@`, `$<`, `$^`, `$?`, `$*`, diagnóstico con `echo`
+
+**Contexto** · Las variables automáticas son poderosas, pero si no sabés qué devuelve cada una,
+escribís recetas que parecen magia. Este ejercicio te obliga a **ver lo que hacen**.
+
+**Consigna** · Escribí un Makefile con **una regla de patrón simple** y otra regla con múltiples
+dependencias. En cada receta, usá `echo` para imprimir el valor de **cada variable automática**:
+
+```makefile
+%.o: %.c
+[TAB]@echo "[$@] target"
+[TAB]@echo "[$<] first prerequisite"
+[TAB]@echo "[$^] all prerequisites"
+[TAB]@echo "[$?] newer prerequisites"
+[TAB]@echo "[$*] stem (pattern match)"
+[TAB]$(CC) $(CFLAGS) -c $< -o $@
+
+programa: main.o utils.o parser.o
+[TAB]@echo "[$@] target: programa"
+[TAB]@echo "[$^] all prereqs: $(words $^) items"
+[TAB]$(CC) $^ -o $@
+```
+
+El `@` al inicio de la línea evita que `make` imprima el comando antes de ejecutarlo (solo ves el
+resultado del `echo`).
+
+Ejecutá `make clean && make` y **documentá la salida completa**: qué imprimió cada variable,
+y por qué.
+
+**Entrada** · Un Makefile con `echo` en las recetas + algunos `.c`.
+**Salida** · La salida de `make`, línea por línea, explicada.
+
+**Escenario de prueba**
+
+```text
+$ make clean && make
+rm -f *.o programa
+gcc -Wall -Wextra -c main.c -o main.o
+[main.o] target
+[main.c] first prerequisite
+[main.c] all prerequisites
+[main.c] newer prerequisites
+[main] stem (pattern match)
+
+...
+```
+
+**Restricciones/pistas**
+- El `@` al inicio de la línea hace que `make` **no imprima el comando**, solo el resultado del `echo`.
+ Sin el `@`, cada `echo` se imprime dos veces: una vez el comando mismo y otra su salida.
+
+**Criterios de aceptación**
+- [ ] Cada variable automática (`$@`, `$<`, `$^`, `$?`, `$*`) se imprime y explica.
+- [ ] La regla con múltiples dependencias muestra cómo `$^` los junta todos.
+- [ ] Se nota la diferencia entre `$?` (solo las nuevas) y `$^` (todas).
+
+---
+
+# PARTE 4 — Variables avanzadas de Makefile
+
+Ya sabés que `CC = gcc` y `CFLAGS = -Wall`. Pero un Makefile real tiene **decenas** de variables, y saber
+cuáles usar y cómo combinarlas es lo que separa un Makefile básico de uno profesional.
+
+## 4.1 Tipos de asignación
+
+Hay cuatro formas de asignar variables, y cada una tiene un comportamiento distinto:
+
+| Operador | Qué hace | ¿Se puede sobreescribir desde la CLI? |
+|---|---|---|
+| `=` | Asignación simple (expandida cuando se usa) | Sí (`make CC=clang`) |
+| `:=` | Asignación inmediata (expandida en el momento) | Sí |
+| `?=` | Condicional: solo asigna si **no está definida** | Solo si no viene de la CLI |
+| `+=` | Concatena al final del valor actual | Sí |
+
+### `=` vs `:=`: cuándo importa la diferencia
+
+```makefile
+A = $(B) # expande $(B) cuando se usa A (lazy)
+B = hello
+C := $(B) # expande $(B) ahora mismo, aunque B cambie después
+
+# Si B cambia después...
+B = world
+
+all:
+[TAB]@echo "A = $(A)" # A = world (expandió B cuando se usó A)
+[TAB]@echo "C = $(C)" # C = hello (ya estaba fijada con :=)
+```
+
+En la práctica, **`:=` es más seguro** para variables que dependen de otras: sabés exactamente qué valor
+van a tener. Pero `=` es más flexible cuando querés que las variables se resuelvan al final, después de
+que todas fueron definidas.
+
+### `?=` : el default configurable
+
+```makefile
+CC ?= gcc # Usá gcc por defecto, pero si el usuario pasó CC=clang, respetalo
+CFLAGS ?= -Wall -Wextra # mismo principio
+```
+
+¿Por qué importa? Porque la CLI tiene prioridad sobre `?=`:
+
+```bash
+$ make CC=clang # clang gana porque viene de la línea de comandos
+$ make # gcc es el default (no vino nada por CLI)
+```
+
+### `+=` : construir listas
+
+```makefile
+CFLAGS += -std=c11
+CFLAGS += -pedantic
+# CFLAGS = -Wall -Wextra -std=c11 -pedantic
+```
+
+Cada `+=` **agrega al final**. Es perfecto para ir construyendo una lista de banderas sin reescribir
+todas las anteriores.
+
+## 4.2 Variables que ya vienen con `make` (y las que gcc espera)
+
+Hay variables que `make` conoce por defecto, y otras que el **compilador** conoce por convención. Es
+bueno distinguir ambas:
+
+### Variables de `make`
+
+| Variable | Default | Descripción |
+|---|---|---|
+| `CC` | `gcc` | El compilador C |
+| `CXX` | `g++` | El compilador C++ |
+| `AR` | `ar` | El archivador (para bibliotecas estáticas) |
+| `RM` | `rm -f` | El comando de borrado (`clean` usa esto) |
+
+### Variables que gcc espera (por convención)
+
+| Variable | Qué va adentro | Ejemplo |
+|---|---|---|
+| `CFLAGS` | Banderas del compilador C | `-Wall -Wextra -O2` |
+| `CPPFLAGS` | Banderas del preprocesador | `-I./include -DDEBUG=1` |
+| `LDFLAGS` | Banderas del linker | `-L./lib -rpath=/opt/lib` |
+| `LDLIBS` | Librerías para el linker | `-lm -lpthread -lmylib` |
+
+> **Fijate la diferencia entre `LDFLAGS` y `LDLIBS`.** `LDFLAGS` son opciones del linker (dónde buscar
+> librerías, dónde buscar paths). `LDLIBS` son las librerías en sí (el `-l...`). Es una convención de GNU:
+> separás la **ubicación** (`-L`) de los **nombres** (`-l`). Si mezclás ambas cosas en una sola variable,
+> el Makefile se vuelve confuso.
+
+## 4.3 La receta del linker: el orden importa
+
+Cuando enlazás un programa con bibliotecas, el **orden** de los argumentos del linker importa:
+
+```bash
+$ gcc main.o utils.o -lm -o programa # correcto: bibliotecas AL FINAL
+$ gcc -lm main.o utils.o -o programa # a veces funciona, a veces no
+```
+
+La regla general es simple: **las bibliotecas van al final**. El linker procesa los archivos de
+izquierda a derecha: si una biblioteca aparece antes que el código que la necesita, no la encuentra.
+
+### La receta completa del linker
+
+En un Makefile profesional, la regla de enlace se ve así:
+
+```makefile
+$(TARGET): $(OBJS)
+[TAB]$(CC) $^ -o $@ $(LDFLAGS) $(LDLIBS)
+```
+
+Desglosado:
+
+| Parte | Qué es | Ejemplo de contenido |
+|---|---|---|
+| `$(CC)` | El compilador (gcc, clang, etc.) | `gcc` |
+| `$^` | Todos los objetos | `main.o utils.o parser.o` |
+| `-o $@` | Nombre del ejecutable.
+| `$^` | Todos los archivos objeto y dependencias | `main.o utils.o parser.o libfoo.a` |
+| `$(LDFLAGS)` | Opciones del linker | `-L./lib -rpath=/opt/lib` |
+| `$(LDLIBS)` | Librerías para el linker | `-lm -lpthread -lmylib` |
+
+> **La receta profesional completa.** Un Makefile de producción se parece a esto:
+
+```makefile
+CC = gcc
+CFLAGS := -Wall -Wextra -std=c11 -O2 -g
+CPPFLAGS += -I./include -DVERSION="\"1.0.0\""
+LDFLAGS += -L./lib
+LDLIBS += -lm -lpthread
+
+TARGET = miapp
+SRCS := $(wildcard src/*.c)
+OBJS := $(patsubst src/%.c, build/%.o, $(SRCS))
+
+.PHONY: all clean release debug
+
+all: $(TARGET)
+
+build/%.o: src/%.c | build
+[TAB]$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+$(TARGET): $(OBJS)
+[TAB]$(CC) $^ -o $@ $(LDFLAGS) $(LDLIBS)
+
+build:
+[TAB]mkdir -p build
+
+debug: CFLAGS += -fsanitize=address,undefined -DDEBUG
+debug: clean all
+
+release: CFLAGS += -O3 -flto
+release: clean all
+
+clean:
+[TAB]$(RM) -r build $(TARGET)
+```
+
+Este Makefile incluye: `:=` para variables principales, `+=` para banderas adicionales, regla de patrón genérica
+que compila cualquier `.c` de `src/` a un `.o` en `build/`, order-only prerequisite (`| build`) para crear
+directorios, targets condicionales (`debug` y `release`), y sanitizers.
+
+> **Truco de laboratorio: `make -n` o `make --dry-run`.** Antes de ejecutar cualquier receta, ejecutá
+> `make -n`. Imprime los comandos que SE EJECUTARÍA sin ejecutarlos. Úsalo antes de cambiar recetas.
+
+## 4.4 El Makefile ideal: resumen completo
+
+```makefile
+CC ?= gcc
+CFLAGS ?= -Wall -Wextra -std=c11
+AR ?= ar
+RM ?= rm -f
+
+TARGET = programa
+SRCS := $(wildcard src/*.c)
+OBJS := $(patsubst src/%.c, build/%.o, $(SRCS))
+
+CPPFLAGS += -I./include
+LDFLAGS += -L./lib
+LDLIBS += -lm
+
+.PHONY: all clean install
+
+all: $(TARGET)
+
+build/%.o: src/%.c
+[TAB]$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+$(TARGET): $(OBJS)
+[TAB]$(CC) $^ -o $@ $(LDFLAGS) $(LDLIBS)
+
+clean:
+[TAB]$(RM) -rf build/ $(TARGET)
+```
+
+Las claves: `?=` para CC/CFLAGS (sobreescribible), `:=` para TARGET/SRCS/OBJS, `+=` para banderas extra,
+regla de patrón genérica y `.PHONY`. Este esqueleto es tu punto de partida — en las siguientes partes
+añadimos bibliotecas estáticas, dinámicas y subdirectorios.
+
+---
+
+# PARTE 5 — Bibliotecas estáticas
+
+Una biblioteca estática (`.a` en Linux/macOS, `.lib` en Windows) es un **archivo ZIP** que contiene
+múltiples archivos objeto (`.o`) con un índice para que el linker encuentre cualquier símbolo. Al
+enlazar contra una biblioteca estática, el linker **copia** el código necesario en tu ejecutable.
+
+## 5.1 ¿Qué es una biblioteca estática?
+
+Si escribiste `mathops.c`, `vector.c` y `matrix.c` con funciones reutilizables, querés que otros
+programadores las usen **sin recompilar tu código cada vez**. La solución: empaquetar los `.o` en un `.a`:
+
+```bash
+gcc -c mathops.c vector.c matrix.c -I./include
+ar rcs libmath.a mathops.o vector.o matrix.o
+ar t libmath.a # muestra: mathops.o vector.o matrix.o
+```
+
+La herramienta `ar` (archiver) usa tres opciones siempre juntas:
+
+| Opción | Qué hace |
+|---|---|
+| `r` | Reemplaza o agrega archivos (crea si no existe) |
+| `c` | No genera mensaje si el archivo es nuevo |
+| `s` | Escribe el índice (esencial para que el linker encuentre símbolos) |
+
+> **Para guardar.** `ar rcs` es siempre las mismas cuatro letras. Sin `s`, el linker no encuentra símbolos.
+
+## 5.2 Cómo usar una biblioteca estática
+
+```bash
+gcc main.c -o programa -L./lib -lmath # forma con -L y -l
+gcc main.o -o programa ./lib/libmath.a # equivalente directo
+```
+
+| Opción | Qué significa | Ejemplo |
+|---|---|---|
+| `-L` | Agrega `` al path del linker | `-L./lib` |
+| `-l` | Busca `lib.a` o `.so` | `-lmath` busca `libmath.a` |
+
+> **La convención:** las bibliotecas se llaman siempre `libNOMBRE.a`. El linker busca `lib` + nombre + `.a`/`.so`.
+
+## 5.3 Biblioteca estática en el Makefile
+
+```makefile
+STATIC_LIB = libmath.a
+LIB_SRCS := mathops.c vector.c matrix.c
+LIB_OBJS := $(patsubst %.c,build/%.o,$(LIB_SRCS))
+
+.PHONY: all clean static
+
+all: $(TARGET) $(STATIC_LIB)
+
+$(STATIC_LIB): $(LIB_OBJS)
+[TAB]$(AR) rcs $@ $^
+
+build/%.o: src/%.c
+[TAB]$(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+[TAB]$(RM) -rf build/ $(TARGET) $(STATIC_LIB)
+```
+
+> **Fijate que `$(AR)` usa `$^`.** Esto ejecuta `ar rcs libmath.a a.o b.o c.o` en una línea.
+
+## Práctica de la Parte 5
+
+### Ejercicio 21.5 — Crear una biblioteca estática
+
+**Conceptos** · `ar`, `ar rcs`, bibliotecas estáticas, `-L`, `-l`
+
+**Consigna** · Con archivos `mathops.c`, `vector.c`, `matrix.c`:
+
+1. Regla de patrón para compilar `.c` → `.o` en `build/`.
+2. Target `$(STATIC_LIB)` con `ar rcs $@ $^`.
+3. Target `all` que construye ejecutable + biblioteca.
+4. Target `clean` que borra todo.
+
+**Escenario de prueba**
+
+```text
+$ make clean && make
+gcc -Wall -Wextra -c src/mathops.c -o build/mathops.o
+gcc -Wall -Wextra -c src/vector.c -o build/vector.o
+gcc -Wall -Wextra -c src/matrix.c -o build/matrix.o
+ar rcs libmath.a build/mathops.o build/vector.o build/matrix.o
+
+$ ar t libmath.a
+build/mathops.o
+build/vector.o
+build/matrix.o
+```
+
+**Identificadores sugeridos** · `STATIC_LIB`, `LIB_SRCS`, `LIB_OBJS`
+**Criterios de aceptación**
+- [ ] `libmath.a` se crea con `ar rcs`.
+- [ ] `ar t libmath.a` muestra los tres objetos.
+- [ ] Un programa externo enlaza con `-L. -lmath`.
+
+---
+
+# PARTE 6 — Bibliotecas dinámicas
+
+Una biblioteca dinámica (`.so` en Linux/macOS, `.dll` en Windows) se **carga en memoria** al ejecutar.
+El código no se copia en tu ejecutable: se comparte entre todos los procesos que lo usan.
+
+## 6.1 ¿Qué es una biblioteca dinámica?
+
+```text
+ BIBLIOTECA ESTÁTICA (.a) BIBLIOTECA DINÁMICA (.so)
+ Código copiado en el ejecutable Código fuera, en un .so
+ Mayor tamaño del binario Menor tamaño del binario
+ No necesita el .a al ejecutar Necesita el .so al ejecutar
+ Un solo proceso por copia Compartida entre procesos
+```
+
+## 6.2 Compilar una biblioteca dinámica
+
+```bash
+gcc -fPIC -c mathops.c vector.c matrix.c # Paso 1: objetos position-independent
+gcc -shared -o libmath.so mathops.o vec.o # Paso 2: enlazar como .so
+```
+
+La bandera `-fPIC` (Position Independent Code) es **obligatoria** para `.so`. Sin ella, el linker falla con
+"relocation R_X86_64_32 against `...` can not be used when making a shared object".
+
+## 6.3 Ejecutar con bibliotecas dinámicas
+
+```bash
+LD_LIBRARY_PATH=. ./miapp # Forma 1: variable de entorno
+gcc main.c -o miapp -L. -lmath -Wl,-rpath,'$ORIGIN' # Forma 2: rpath embebido
+ldd miapp # Ver dependencias
+```
+
+> **`$ORIGIN`** = directorio donde vive el ejecutable. La forma más robusta de rutas relativas.
+
+## 6.4 Biblioteca dinámica en el Makefile
+
+```makefile
+DYNAMIC_LIB = libmath.so
+LIB_OBJS := $(patsubst %.c,build/%.o,$(LIB_SRCS))
+
+$(DYNAMIC_LIB): $(LIB_OBJS)
+[TAB]$(CC) -shared -o $@ $^
+
+build/%.o: src/%.c
+[TAB]$(CC) $(CFLAGS) -fPIC -c $< -o $@ # -fPIC obligatorio para .so
+```
+
+Sin `-fPIC` y `-shared`, no tenés una biblioteca dinámica.
+
+## Práctica de la Parte 6
+
+### Ejercicio 21.6 — Enlazar contra una biblioteca estática vs dinámica
+
+**Conceptos** · `-L`, `-l`, `.a` vs `.so`, `ldd`, resolución de símbolos
+
+**Contexto** · Ya sabés crear bibliotecas estáticas (`.a`) y ahora vas a entender cómo cambia el
+enlace cuando usás una biblioteca dinámica (` .so`). Este ejercicio te obliga a compilar y ejecutar
+el mismo programa contra ambas versiones.
+
+**Consigna** · Tenés dos bibliotecas: `libmath.a` (estática) y `libmath.so` (dinámica). Escribí un
+Makefile con dos targets que generen el **mismo ejecutable** enlazado de forma distinta:
+
+1. `./miapp-static` → enlazado contra `libmath.a`
+2. `./miapp-dynamic` → enlazado contra `libmath.so` con rpath
+
+Después, usá `ls -lh`, `ldd` y `time` para comparar ambos ejecutables.
+
+**Entrada** · Un solo `main.c` que llama a funciones de `libmath`.
+**Salida** · Dos ejecutables con tamaños distintos, y un análisis comparativo.
+
+**Escenario de prueba**
+
+```text
+$ make static dynamic
+gcc -Wall -Wextra -c main.c -o main.o
+gcc main.o libmath.a -o miapp-static
+gcc main.o -L. -lmath -Wl,-rpath,'$ORIGIN' -o miapp-dynamic
+
+$ ls -lh miapp-*
+-rwxr-xr-x 1 user user 20K miapp-static # Más grande (código copiado)
+-rwxr-xr-x 1 user user 16K miapp-dynamic # Menor (enlace diferido)
+
+$ ldd miapp-dynamic | grep math
+libmath.so => ./libmath.so (0x...)
+
+$ ldd miapp-static | grep math
+(ninguno — no depende de nada)
+```
+
+**Identificadores sugeridos** · `STATIC_LIB`, `DYNAMIC_LIB`, `miapp-static`, `miapp-dynamic`
+**Restricciones/pistas**
+- No escribas la librería ni el main: asumí que ya existen.
+- El rpath es clave para que el ejecutable dinámico encuentre la `.so`.
+
+**Criterios de aceptación**
+- [ ] `miapp-static` se construye con `gcc main.o libmath.a -o miapp-static`.
+- [ ] `miapp-dynamic` usa `-L. -lmath -Wl,-rpath,'$ORIGIN'`.
+- [ ] `ldd miapp-static` no muestra dependencias de libmath.
+- [ ] `ldd miapp-dynamic` muestra `libmath.so => ...`.
+
+---
+
+### Ejercicio 21.7 — Crear una biblioteca dinámica
+
+**Conceptos** · `-fPIC`, `-shared`, `.so`, `ldd`, `LD_LIBRARY_PATH`
+
+**Consigna** · Con el proyecto del ejercicio 21.5, modificá el Makefile para:
+
+1. Genere objetos con `-fPIC`.
+2. Cree `libmath.so` con `-shared`.
+3. El ejecutable enlaza contra la `.so` con rpath (`-Wl,-rpath,'$ORIGIN'`).
+
+**Escenario de prueba**
+
+```text
+$ make clean && make
+gcc -Wall -Wextra -fPIC -c src/mathops.c -o build/mathops.o
+gcc -shared -o libmath.so build/mathops.o ...
+ldd miapp # muestra: libmath.so => ./libmath.so (0x...)
+```
+
+**Criterios de aceptación**
+- [ ] Se compila con `-fPIC`.
+- [ ] `libmath.so` existe y `ldd` lo reporta como dependencia.
+- [ ] El programa ejecuta sin error "cannot open shared object file".
+
+---
+
+### Ejercicio 21.8 — Estática vs dinámica: medir el impacto
+
+**Consigna** · Con la misma librería compilada como `.a` y como `.so`:
+
+1. Medí tamaño de `libmath.a` vs `libmath.so`.
+2. Medí tamaño del ejecutable enlazado contra cada una.
+3. Cronometrá ambos programas.
+4. Usá `strings`, `nm`, `ldd`.
+
+**Criterios de aceptación**
+- [ ] El ejecutable estático es más grande (código copiado).
+- [ ] Se explicó el "linker selectivo" (copy-on-link).
+- [ ] Ambos programas miden correctamente con `time` o similar.
+
+
+
+# PARTE 7 — Makefiles profesionales
+
+Acá juntamos todo en un Makefile que se parece a los de proyectos reales: sanitizers, targets debug/release,
+dependencias automáticas y variables consultadas al compilador.
+
+## 7.1 Sanitizers en desarrollo, optimización en producción
+
+```makefile
+.PHONY: all clean debug release test coverage
+
+debug: CFLAGS += -fsanitize=address,undefined -g -O0 -DDEBUG
+debug: $(TARGET)
+
+release: CFLAGS += -O3 -flto -DNDEBUG
+release: clean all
+
+test: debug
+[TAB]./$^ --test # asumiendo que el programa soporta --test
+
+coverage: CFLAGS += --coverage -fprofile-arcs -ftest-coverage
+coverage: $(TARGET)
+[TAB]gcov *.c
+```
+
+Cada target redefine `CFLAGS` **solo para sí mismo** con `target: CFLAGS += ...`.
+
+## Variables consultadas al compilador
+
+```makefile
+COMPILER_VERSION := $(shell $(CC) -dumpversion)
+
+info:
+[TAB]@echo "Compiler: $(CC) ($(COMPILER_VERSION))"
+[TAB]@echo "CFLAGS: $(CFLAGS)"
+[TAB]@echo "TARGET: $(TARGET)"
+```
+
+> **Truco: `make info`.** Imprime toda la configuración del build. El primer target que todo Makefile serio
+> debería tener.
+
+## Generación automática de dependencias
+
+Modificar un `.h` no recompila los `.c` que lo incluyen. Solución: generar reglas de dependencia automáticas:
+
+```bash
+gcc -MMD -MP -c main.c -o main.o # genera main.d además de main.o
+```
+
+```makefile
+DEPS := $(OBJS:.o=.d)
+-include $(DEPS)
+```
+
+`-MMD` genera el archivo `.d` con dependencias. `-MP` agrega reglas vacías para cabeceras (evita errores si borrás un header).
+
+> **Para guardar.** `gcc -MMD -MP` soluciona: "compilé un `.c` que depende de un `.h`, pero el `.h` cambió y make no lo supo".
+
+---
+
+### Ejercicio 21.9 — Makefile profesional completo
+
+**Consigna** · Construí un Makefile con targets: `all`, `debug`, `release`, `clean`, `info`, `test`.
+Debe incluir bibliotecas estática Y dinámica, rpath, `-MMD -MP`, y sanitizers en debug.
+
+**Criterios de aceptación**
+- [ ] `make debug` compila con sanitizers activados.
+- [ ] `make info` imprime toda la configuración del build.
+- [ ] Los archivos `.d` se generan e incluyen con `-include $(DEPS)`.
+- [ ] Bibliotecas estática y dinámica se crean correctamente.
+
+---
+
+### Ejercicio 21.10 — Depuración de dependencias rotas
+
+**Consigna** · Con el Makefile del ejercicio 21.9:
+
+1. Compilá con `make debug`.
+2. Borrá los `.d` generados (`rm build/*.d`).
+3. Modificá un header incluido.
+4. ¿Qué pasó al ejecutar `make`? ¿Por qué?
+5. Restablecé y verificá que make detecta el cambio en `.h`.
+
+**Criterios de aceptación**
+- [ ] Se explicó el efecto de borrar los `.d`.
+- [ ] Se explica `-MP` con ejemplo concreto.
+
+---
+
+# PARTE 8 — Integrador final: proyecto multi-subdirectorio
+
+Acá juntamos todo en un proyecto real: múltiples subdirectorios, bibliotecas estáticas, dinámicas, y un
+Makefile que construye todo desde cero con `make`.
+
+## 8.1 Estructura del proyecto
+
+```text
+mi-proyecto/
+├── Makefile # Makefile raiz (orquesta todo)
+├── include/ # Headers publicos
+│ ├── mathops.h
+│ ├── vector.h
+├── src/ # Codigo fuente principal
+│ ├── main.c
+│ └── utils.c
+├── lib/ # Bibliotecas de la aplicacion
+│ ├── Makefile # Makefile para compilar librerias
+│ ├── mathops.c
+│ ├── vector.c
+├── tests/ # Pruebas unitarias
+│ ├── Makefile
+│ └── test_mathops.c
+└── build/ # Objetos compilados (generado)
+```
+
+Cada subdirectorio tiene su propio Makefile. El raiz orquesta todo con `$(MAKE) -C `.
+
+## 8.2 Makefile de la biblioteca (lib/Makefile)
+
+```makefile
+CC = gcc
+CFLAGS := -Wall -Wextra -std=c11 -I../include
+
+%.o: %.c
+[TAB]$(CC) $(CFLAGS) -fPIC -c $< -o $@
+
+libmath.a: mathops.o vector.o
+[TAB]$(AR) rcs $@ $^
+
+libmath.so: mathops.o vector.o
+[TAB]$(CC) -shared -o $@ $^
+
+clean:
+[TAB]$(RM) -f *.o libmath.a libmath.so
+```
+
+## 8.3 Makefile raiz (Makefile)
+
+```makefile
+CC ?= gcc
+CFLAGS ?= -Wall -Wextra -std=c11 -O2 -g
+
+TARGET = miapp
+LIB_DIR = lib
+BUILD_DIR = build
+
+.PHONY: all clean debug release libs test
+
+all: $(TARGET)
+
+$(BUILD_DIR)/%.o: src/%.c
+[TAB]@mkdir -p $(BUILD_DIR)
+[TAB]$(CC) $(CFLAGS) -Iinclude -c $< -o $@
+
+libs:
+[TAB]$(MAKE) -C $(LIB_DIR)
+
+$(TARGET): libs
+[TAB]$(CC) $(BUILD_DIR)/*.o -o $@ -L$(LIB_DIR) -lmath -Wl,-rpath,'$ORIGIN'
+
+debug: CFLAGS += -fsanitize=address,undefined -g -O0
+debug: clean all
+
+release: CFLAGS += -O3 -flto -DNDEBUG
+release: clean all
+
+test: libs
+[TAB]$(MAKE) -C tests
+
+info:
+[TAB]@echo "CC = $(CC)"
+[TAB]@echo "CFLAGS = $(CFLAGS)"
+[TAB]@echo "TARGET = $(TARGET)"
+
+clean:
+[TAB]$(RM) -rf $(BUILD_DIR)/ $(TARGET)
+[TAB]$(MAKE) -C $(LIB_DIR) clean || true
+[TAB]$(MAKE) -C tests clean || true
+```
+
+> **`$(MAKE) -C `** cambia al directorio y ejecuta `make`. Es la forma estándar de builds recursivos.
+> El `-C` es una bandera de GNU make, no del compilador.
+
+## 8.4 Makefile de tests (tests/Makefile)
+
+```makefile
+CC = gcc
+CFLAGS := -Wall -Wextra -std=c11 -g
+
+TESTS = test_mathops
+
+all: $(TESTS)
+
+test_%: %.c ../lib/libmath.a
+[TAB]$(CC) $(CFLAGS) $< -L../lib -lmath -o $@
+
+clean:
+[TAB]$(RM) -f $(TESTS)
+```
+
+---
+
+# PARTE 9 — Instalación, configuraciones y recursos
+
+Ya construiste bibliotecas estáticas, dinámicas, targets de debug/release y dependencias automáticas.
+Pero un proyecto profesional **se instala** en el sistema del usuario: binarios en `/usr/local/bin`,
+bibliotecas en `/usr/local/lib`, headers en `/usr/local/include` y recursos (iconos, datos) en
+`/usr/local/share`. Además, suele ofrecer **opciones de compilación**: "¿querés soporte para Vulkan o
+SDL? Elegí al hacer `make`".
+
+Esta parte cubre todo eso: cómo instalar, desinstalar, elegir backends desde la línea de comandos y
+distribuir recursos propios.
+
+## 9.1 Variables de directorio (`PREFIX`, `BINDIR`, etc.)
+
+La convención de Unix define variables estándar que todos los Makefiles serios respetan. Tu usuario las
+puede sobreescribir desde la línea de comandos:
+
+```makefile
+PREFIX ?= /usr/local
+BINDIR ?= $(PREFIX)/bin
+LIBDIR ?= $(PREFIX)/lib
+INCLUDEDIR?= $(PREFIX)/include
+DATADIR ?= $(PREFIX)/share/miapp
+MANPAGEDIR?= $(PREFIX)/share/man/man1
+
+.PHONY: all install uninstall clean
+```
+
+El `?=` significa que el usuario puede cambiar cualquier directorio sin modificar el Makefile:
+
+```bash
+$ make PREFIX=/opt/miapp BINDIR=/opt/miapp/bin install
+$ make PREFIX=$HOME/.local install # instalación local, sin root
+```
+
+## 9.2 El target `install`
+
+El target `install` copia los archivos a sus destinos. Usa `mkdir -p` para crear directorios y
+`\cp` (copia segura) o `install` (el comando del sistema):
+
+```makefile
+install: $(TARGET)
+[TAB]@mkdir -p $(BINDIR)
+[TAB]@mkdir -p $(LIBDIR)
+[TAB]@mkdir -p $(INCLUDEDIR)
+[TAB]@mkdir -p $(DATADIR)
+[TAB]$(INSTALL_BIN) $(TARGET) $(BINDIR)/$(TARGET)
+[TAB]$(INSTALL_LIB) libmath.a $(LIBDIR)/libmath.a
+[TAB]$(INSTALL_LIB) libmath.so $(LIBDIR)/libmath.so
+[TAB]@install -m 644 include/*.h $(INCLUDEDIR)/
+[TAB]@cp -r resources/* $(DATADIR)/
+```
+
+> **`$(INSTALL_BIN)`, `$(INSTALL_LIB)`**. No uses `cp` a secas. GNU Make define por defecto:
+> `INSTALL = cp`. Pero el target `install` del sistema (el comando `/usr/bin/install`) maneja
+> permisos y tipos correctamente. Puedes usarlo así:
+> ```makefile
+> INSTALL_BIN ?= $(INSTALL) -D -m 755
+> INSTALL_LIB ?= $(INSTALL) -D -m 644
+> ```
+> La bandera `-D` crea directorios padres si no existen. `-m 755` = ejecutable, `-m 644` = lectura
+> universal.
+
+## 9.3 El target `uninstall` y limpieza de recursos
+
+Cada cosa que `install` crea, `uninstall` la elimina:
+
+```makefile
+uninstall:
+[TAB]$(RM) $(BINDIR)/$(TARGET)
+[TAB]$(RM) $(LIBDIR)/libmath.a
+[TAB]$(RM) $(LIBDIR)/libmath.so
+[TAB]$(RM) -rf $(DATADIR)
+[TAB]@echo "Uninstalled from $(PREFIX)"
+```
+
+> **Para guardar.** Si `install` hace algo (ej. actualiza cachés del sistema con `ldconfig`),
+> `uninstall` debe revertirlo. El desinstalador es tan importante como el instalador.
+
+## 9.4 Opciones de compilación: feature toggles
+
+Imaginate una aplicación que puede renderizar gráficos usando **múltiples backends**: SDL2, OpenGL o
+Vulkan. En vez de obligar al usuario a editar el Makefile a mano, le das opciones desde la CLI:
+
+```makefile
+# Por defecto usa SDL2 si no se especifica nada
+RENDERER ?= sdl
+
+ifeq ($(RENDERER),sdl)
+[TAB]CFLAGS += -DRENDERER_SDL -I/usr/include/SDL2
+[TAB]LDLIBS += -lSDL2
+[TAB]@echo ">> Building with SDL2 backend"
+endif
+
+ifeq ($(RENDERER),opengl)
+[TAB]CFLAGS += -DRENDERER_OPENGL -I/usr/include
+[TAB]LDLIBS += -lGL -lm
+[TAB]@echo ">> Building with OpenGL backend"
+endif
+
+ifeq ($(RENDERER),vulkan)
+[TAB]CFLAGS += -DRENDERER_VULKAN -I/usr/include/x86_64-linux-gnu
+[TAB]LDLIBS += -lvulkan-1
+[TAB]@echo ">> Building with Vulkan backend"
+endif
+```
+
+El usuario elige al invocar `make`:
+
+```bash
+$ make # SDL2 (por defecto)
+$ make RENDERER=opengl # OpenGL
+$ make RENDERER=vulkan clean # Vulkan (con limpieza previa)
+```
+
+### Más ejemplos de feature toggles
+
+Esto no es solo para backends gráficos. Podés activar/desactivar:
+
+| Toggle | Qué habilita | Ejemplo de uso |
+|---|---|---|
+| `RENDERER=opengl` | Backend gráfico OpenGL | Aplicaciones visuales |
+| `ENABLE_TESTS=yes` | Compila y corre tests | Desarrollo activo |
+| `LTO=yes` | Link-time optimization | Producción máxima performance |
+| `DEBUG_SYMBOLS=yes` | Símbolos de depuración | Debugging |
+| `FEATURE_X=yes` | Una funcionalidad concreta | Plugins, módulos opcionales |
+
+```makefile
+# Ejemplo: habilitar LTO si el usuario lo pide
+ifeq ($(LTO),yes)
+[TAB]CFLAGS += -flto -ffat-lto-objects
+[TAB]@echo ">> LTO enabled"
+endif
+
+# Ejemplo: tests compilados pero no ejecutados por defecto
+ifeq ($(ENABLE_TESTS),yes)
+[TAB]CFLAGS += -DTESTING_ENABLED
+[TAB]OBJS += tests/main.o
+[TAB]test-targets: $(TARGETS)
+[TAB][TAB]$(MAKE) -C tests
+endif
+```
+
+### Headers condicionales
+
+Las opciones de compilación se reflejan en el código con `#ifdef`:
+
+```c
+#ifdef RENDERER_SDL
+ renderer_init_sdl();
+#elif defined(RENDERER_OPENGL)
+ renderer_init_opengl();
+#elif defined(RENDERER_VULKAN)
+ renderer_init_vulkan();
+#else
+#error "No renderer selected. Use: make RENDERER=sdl|opengl|vulkan"
+#endif
+```
+
+El `#error` es la forma en que el Makefile fuerza al usuario a elegir un backend si no especificó
+ninguno (cuando `RENDERER` viene vacío).
+
+## 9.5 Recursos propios: iconos, datos, configuración
+
+Una aplicación instalada suele necesitar archivos adicionales: íconos, temas, archivos de configuración,
+modelos 3D, texturas, sonidos, etc. Estos se copian a un directorio de datos durante `make install`:
+
+```makefile
+DATADIR ?= $(PREFIX)/share/miapp
+
+install:
+[TAB]@mkdir -p $(DATADIR)
+[TAB]@cp -r resources/* $(DATADIR)/
+[TAB]@echo "Resources installed to $(DATADIR)"
+```
+
+Y en el programa, la ruta a los recursos se construye con una macro del preprocesador:
+
+```makefile
+CPPFLAGS += -DRES_DIR="\"$(DATADIR)\""
+```
+
+```c
+const char *resource_path(const char *filename) {
+ static char path[1024];
+ snprintf(path, sizeof(path), "%s/%s", RES_DIR, filename);
+ return path;
+}
+```
+
+> **Para guardar.** Si los recursos cambian después de la instalación, el usuario solo necesita
+> volver a ejecutar `make install` (no `make` completo). El Makefile debe distinguir entre "compilar"
+> y "instalar datos".
+
+## 9.6 El Makefile de producción: resumen final
+
+```makefile
+# ─── Variables configurables por el usuario ───
+CC ?= gcc
+PREFIX ?= /usr/local
+RENDERER ?= sdl
+ENABLE_TESTS ?= no
+LTO ?= no
+
+BINDIR := $(PREFIX)/bin
+LIBDIR := $(PREFIX)/lib
+INCLUDEDIR:= $(PREFIX)/include
+DATADIR := $(PREFIX)/share/miapp
+MANPAGEDIR:= $(PREFIX)/share/man/man1
+
+INSTALL_BIN ?= $(INSTALL) -D -m 755
+INSTALL_LIB ?= $(INSTALL) -D -m 644
+
+# ─── Banderas del compilador ───
+CFLAGS := -Wall -Wextra -std=c11
+CPPFLAGS += -I./include -DRES_DIR="\"$(DATADIR)\""
+
+ifeq ($(RENDERER),sdl)
+[TAB]CFLAGS += -DRENDERER_SDL -I/usr/include/SDL2
+[TAB]LDLIBS += -lSDL2
+[TAB]@echo ">> Backend: SDL2"
+endif
+
+ifeq ($(RENDERER),opengl)
+[TAB]CFLAGS += -DRENDERER_OPENGL
+[TAB]LDLIBS += -lGL -lm
+[TAB]@echo ">> Backend: OpenGL"
+endif
+
+ifeq ($(RENDERER),vulkan)
+[TAB]CFLAGS += -DRENDERER_VULKAN
+[TAB]LDLIBS += -lvulkan-1
+[TAB]@echo ">> Backend: Vulkan"
+endif
+
+ifeq ($(LTO),yes)
+[TAB]CFLAGS += -flto -ffat-lto-objects
+[TAB]@echo ">> LTO enabled"
+endif
+
+# ─── Archivos ───
+TARGET = renderer
+SRCS := $(wildcard src/*.c)
+OBJS := $(patsubst %.c,build/%.o,$(SRCS))
+
+.PHONY: all install uninstall clean debug release test info
+
+all: $(TARGET)
+
+build/%.o: src/%.c
+[TAB]@mkdir -p build
+[TAB]$(CC) $(CFLAGS) -c $< -o $@
+
+$(TARGET): $(OBJS)
+[TAB]$(CC) $^ -o $@ $(LDFLAGS) $(LDLIBS)
+
+install: all
+[TAB]@mkdir -p $(BINDIR) $(DATADIR)
+[TAB]$(INSTALL_BIN) $(TARGET) $(BINDIR)/$(TARGET)
+[TAB]@cp -r resources/* $(DATADIR)/
+[TAB]@echo "Installed to $(PREFIX)"
+
+uninstall:
+[TAB]$(RM) $(BINDIR)/$(TARGET)
+[TAB]$(RM) -rf $(DATADIR)
+[TAB]@echo "Uninstalled from $(PREFIX)"
+
+debug: CFLAGS += -g -O0 -fsanitize=address,undefined
+debug: clean all
+
+release: CFLAGS += -O3 -DNDEBUG
+release: clean all
+
+test:
+[TAB]$(MAKE) -C tests run
+
+info:
+[TAB]@echo "CC = $(CC)"
+[TAB]@echo "PREFIX = $(PREFIX)"
+[TAB]@echo "RENDERER = $(RENDERER)"
+[TAB]@echo "CFLAGS = $(CFLAGS)"
+[TAB]@echo "TARGET = $(TARGET)"
+
+clean:
+[TAB]$(RM) -rf build/ $(TARGET)
+[TAB]$(MAKE) -C tests clean || true
+```
+
+Con este Makefile tenés un proyecto que el usuario puede:
+1. **Configurar** (`make RENDERER=opengl LTO=yes PREFIX=$HOME/.local`)
+2. **Compilar** en debug o release
+3. **Instalar** con recursos y binarios en los directorios correctos
+4. **Desinstalar** limpiamente
+
+---
+
+## Práctica de la Parte 8
+
+### Ejercicio 21.11 — Proyecto multi-subdirectorio con bibliotecas anidadas
+
+**Conceptos** · `$(MAKE) -C `, `.PHONY`, recursividad, `|| true` en limpieza
+
+**Contexto** · Necesitás un proyecto con tres componentes independientes: una librería matemática (`lib/`),
+un programa principal (`src/`) y tests unitarios (`tests/`). Cada componente tiene su propio Makefile.
+
+**Consigna** · Construí la estructura:
+
+```text
+mi-proyecto/
+├── Makefile # Raíz (orquesta todo)
+├── src/
+│ ├── main.c
+│ └── utils.c
+├── lib/
+│ ├── Makefile # Compila libmath.a y libmath.so
+│ ├── mathops.c
+│ └── vector.c
+├── include/
+│ ├── mathops.h
+│ └── vector.h
+└── tests/
+ ├── Makefile
+ └── test_mathops.c
+```
+
+El Makefile raíz:
+1. Construye la librería con `$(MAKE) -C lib`
+2. Compila el programa principal vinculando contra la `.a` con rpath
+3. Ofrece targets `debug`, `release`, `test`, `clean` que se propagan a subdirectorios
+
+**Escenario de prueba**
+
+```text
+$ make clean && make
+make -C lib clean || true
+gcc -Wall -Wextra -c src/main.c -o build/main.o
+gcc build/main.o -Llib -lmath -Wl,-rpath,'$ORIGIN' -o miapp
+./miapp
+Procesando datos...
+
+$ make test
+make -C tests
+gcc -Wall -Wextra -std=c11 -g test_mathops.c -L../lib -lmath -o test_mathops
+./test_mathops
+Running test_add: OK (0.00s)
+```
+
+**Identificadores sugeridos** · `miapp`, `LIB_DIR`, `BUILD_DIR`
+**Criterios de aceptación**
+- [ ] Cada subdirectorio (`lib/`, `tests/`) tiene su Makefile independiente.
+- [ ] El Makefile raíz usa `$(MAKE) -C ` para orquestar.
+- [ ] `make clean` en el raiz limpia todos los subdirectorios (con `|| true`).
+- [ ] El programa ejecuta correctamente contra la librería local.
+
+---
+
+## Práctica de la Parte 9
+
+### Ejercicio 21.12 — Makefile instalable con feature toggles (ejercicio jefe)
+
+**Conceptos** · `PREFIX`, `BINDIR`, `DATADIR`, `make install/uninstall`, `ifeq`, `-D`, `#ifdef`, recursos,
+`INSTALL_BIN`, `INSTALL_LIB`, variables de directorio configurables
+
+**Contexto** · Trabajás en un equipo que desarrolla una herramienta de línea de comandos llamada `vox`.
+No necesitás implementar la herramienta: su trabajo es crear un Makefile **completamente funcional** para
+construir e instalar el proyecto. Los archivos fuente (`.c`, `.h`) y los recursos (`.txt`, `.dat`) ya existen
+en la estructura del proyecto; tu objetivo es dominar `make` a nivel de distribución profesional.
+
+La aplicación tiene tres "backends" posibles de procesamiento que se activan con una variable de make:
+
+| Variable | Valor | Qué hace |
+|---|---|---|
+| `ENGINE` | `ref` | Motor básico en software (por defecto) |
+| `ENGINE` | `simd` | Usa instrucciones SIMD (SSE2/AVX2) |
+| `ENGINE` | `gpu` | Usa OpenCL para aceleración por GPU |
+
+Además, el programa usa dos bibliotecas externas: `libmath.a` (estática) y `libspectrum.so` (dinámica).
+Al instalarse, copia un binario, un header público y recursos de datos a directorios configurables.
+
+**Consigna** · Escribí **solo el Makefile**. No escribas los `.c`, ni las librerías, ni los recursos.
+Asumí que existen:
+
+- `src/main.c` — incluye `vox.h` y usa `#ifdef ENGINE_REF` / `#ifdef ENGINE_SIMD` / `#ifdef ENGINE_GPU`
+- `src/engine_ref.c`, `src/engine_simd.c`, `src/engine_gpu.c` — implementación de cada motor (condicional)
+- `include/vox.h` — header público a instalar
+- `lib/libmath.a` — biblioteca estática externa (ya compilada, en el proyecto)
+- `lib/libspectrum.so` — biblioteca dinámica externa (ya compilada)
+- `resources/config.txt`, `resources/defaults.dat` — archivos de datos
+
+Tu Makefile debe:
+
+1. **Variables configurables**: `PREFIX ?= /usr/local`, `ENGINE ?= ref`. El usuario debe poder hacer
+ `make ENGINE=simdx86 PREFIX=$HOME/.local install`.
+
+2. **Selección de backend**: usar `ifeq` para activar las banderas correctas:
+
+ ```makefile
+ ifeq ($(ENGINE),simd)
+ CFLAGS += -DENGINE_SIMD -msse2 -mavx2
+ LDLIBS += -lm
+ endif
+ ifeq ($(ENGINE),gpu)
+ CFLAGS += -DENGINE_GPU
+ LDLIBS += -lOpenCL
+ endif
+ # ENGINE=ref no necesita flags extra (código nativo)
+ ```
+
+3. **Compilación condicional**: compilar solo el engine seleccionado:
+
+ ```makefile
+ ifeq ($(ENGINE),simd)
+ OBJS := src/main.o src/engine_simd.o
+ else ifeq ($(ENGINE),gpu)
+ OBJS := src/main.o src/engine_gpu.o
+ else
+ OBJS := src/main.o src/engine_ref.o
+ endif
+ ```
+
+4. **Enlazado**: contra `libmath.a` (estática, ruta directa) y `libspectrum.so` (dinámica, con rpath):
+
+ ```makefile
+ vox: $(OBJS) lib/libmath.a
+ $(CC) $^ -o $@ -Llib -lspectrum -Wl,-rpath,'$ORIGIN/../lib' $(LDLIBS)
+ ```
+
+5. **Instalación**: con `PREFIX` configurable, copiando binario, header y recursos:
+
+ ```makefile
+ PREFIX ?= /usr/local
+ BINDIR := $(PREFIX)/bin
+ INCLUDEDIR:= $(PREFIX)/include/vox
+ LIBDIR := $(PREFIX)/lib/vox
+ DATADIR := $(PREFIX)/share/vox
+ MANPAGEDIR:= $(PREFIX)/share/man/man1
+
+ install: vox
+ @mkdir -p $(BINDIR) $(INCLUDEDIR) $(LIBDIR) $(DATADIR) $(MANPAGEDIR)
+ install -D -m 755 vox $(BINDIR)/vox
+ install -D -m 644 include/vox.h $(INCLUDEDIR)/vox.h
+ cp lib/libmath.a $(LIBDIR)/libmath.a
+ cp lib/libspectrum.so $(LIBDIR)/libspectrum.so
+ cp resources/* $(DATADIR)/
+ ```
+
+6. **Desinstalación**: revertir la instalación:
+
+ ```makefile
+ uninstall:
+ $(RM) $(BINDIR)/vox
+ $(RM) -rf $(INCLUDEDIR) $(LIBDIR) $(DATADIR) $(MANPAGEDIR)/vox.1*
+ @echo "Uninstalled from $(PREFIX)"
+ ```
+
+7. **Target `info`**: imprimir toda la configuración activa:
+
+ ```makefile
+ info:
+ @echo "CC = $(CC)"
+ @echo "PREFIX = $(PREFIX)"
+ @echo "ENGINE = $(ENGINE)"
+ @echo "CFLAGS = $(CFLAGS)"
+ @echo "OBJS = $(OBJS)"
+ ```
+
+8. **Debug / Release**: targets condicionales que redefinen `CFLAGS`:
+
+ ```makefile
+ debug: CFLAGS += -g -O0 -fsanitize=address,undefined
+ debug: clean vox
+
+ release: CFLAGS += -O3 -flto -DNDEBUG
+ release: clean vox
+ ```
+
+**Escenario de prueba esperado** (solo el Makefile se evalúa):
+
+```text
+$ make ENGINE=simd info
+>> Backend SIMD seleccionado
+CC = gcc
+PREFIX = /usr/local
+ENGINE = simd
+CFLAGS = -Wall -Wextra -std=c11 -DENGINE_SIMD -msse2 -mavx2
+OBJS = src/main.o src/engine_simd.o
+
+$ make ENGINE=simd PREFIX=$HOME/.local install
+gcc ... -c src/main.c -o src/main.o
+gcc ... -c src/engine_simd.c -o src/engine_simd.o
+gcc src/main.o src/engine_simd.o lib/libmath.a -o vox \
+ -Llib -lspectrum -Wl,-rpath,'$ORIGIN/../lib' -lm
+install -D -m 755 vox $HOME/.local/bin/vox
+install -D -m 644 include/vox.h $HOME/.local/include/vox/vox.h
+cp lib/libmath.a $HOME/.local/lib/vox/
+cp lib/libspectrum.so $HOME/.local/lib/vox/
+cp resources/* $HOME/.local/share/vox/
+
+$ make ENGINE=gpu uninstall
+rm -f /usr/local/bin/vox
+rm -rf /usr/local/include/vox /usr/local/lib/vox /usr/local/share/vox /usr/local/share/man/man1/vox.1*
+Uninstalled from /usr/local
+```
+
+**Identificadores sugeridos** · `Makefile`, `VOX`, `ENGINE`, `OBJS`
+**Restricciones**
+- **Solo escribís el Makefile.** No implementes los `.c`, ni compiles librerías externas, ni crees recursos.
+- El evaluador revisará que las reglas sean correctas y que todas las variables sean sobreescribibles
+ desde la CLI.
+- Usá `$@`, `$^`, `$<` donde aplique — no escribas nombres de archivo a mano en las recetas.
+
+**Criterios de aceptación**
+- [ ] `PREFIX ?= /usr/local` y se respeta en `install`.
+- [ ] `ENGINE ?= ref` selecciona el engine correcto con `ifeq`.
+- [ ] Se compila solo el `.c` del engine seleccionado (no los tres).
+- [ ] El enlace usa `libmath.a` directo + `libspectrum.so` con rpath.
+- [ ] `make install` copia binario, header y recursos a los directorios correctos.
+- [ ] `make uninstall` elimina todo lo que `install` creó.
+- [ ] `make info` imprime configuración activa completa.
+- [ ] `make debug` y `make release` redefinen `CFLAGS` correctamente.
+- [ ] El usuario puede hacer `make ENGINE=gpu PREFIX=$HOME/.local install`.
+
+---
+
+### Ejercicio 21.13 — Hacer que un header roto no rompa todo
+
+**Conceptos** · `-MMD`, `-MP`, dependencias implícitas, `#include` indirectos, `make clean -a`
+
+**Contexto** · Tu Makefile tiene `-MMD -MP` para generar dependencias automáticas. Pero alguien borra un
+archivo `.h` que fue generado por un script externo y no se regeneró. Hacés `make`, el compilador falla,
+y ahora tenés que limpiar manualmente.
+
+**Consigna** · Con un Makefile que tenga `-MMD -MP`:
+
+1. Creá un archivo `generated.h` simulado (un `echo "int fake = 42;" > generated.h`).
+2. Compilá un `main.c` que incluya ``.
+3. Borrá `generated.h` y ejecutá `make` otra vez — ¿qué pasa?
+4. Usá `-MP` para agregar una regla vacía a `generated.h.d` (si existe).
+5. Intentá `make generated.h` como target phony que regera el archivo.
+
+**Criterios de aceptación**
+- [ ] Se explica por qué `make` falla al borrar un header dependiente.
+- [ ] Se muestra cómo agregar `generated.h:` como target en el Makefile para regenerarlo.
+- [ ] El Makefile permite `make generated.h && make` sin errores.
+
+---
+
+> **Has llegado hasta aquí.** El capítulo más largo del libro. Aprendiste a pasar de "compilar con gcc"
+> a tener un sistema de build profesional con bibliotecas estáticas, dinámicas, sanitizers, tests,
+> instalación configurada y opciones de compilación. En el próximo capítulo (`22`), vamos a ver cómo
+> hacer esto aún mejor con **CMake**.
+
diff --git a/dist/21-makefiles.pdf b/dist/21-makefiles.pdf
new file mode 100644
index 0000000..acb00bd
Binary files /dev/null and b/dist/21-makefiles.pdf differ