Una de las grandes ventajas que nos ha traído Internet es el acceso a un vasto repositorio de bibliotecas que podemos utilizar para acelerar nuestro desarrollo en C, sin tener que programar todo desde cero. Por ejemplo, si queremos crear una aplicación gráfica, no necesitamos construir nuestro propio sistema de ventanas; podemos apoyarnos en bibliotecas como GTK o QT para disponer de ventanas, botones y otros elementos gráficos listos para usar.
Estas bibliotecas suelen estar disponibles a través de los gestores de paquetes de nuestro sistema operativo. Sin embargo, instalar solo la biblioteca dinámica no es suficiente para desarrollar nuestros propios programas. Necesitamos también los archivos de cabecera y otros recursos de desarrollo, que normalmente vienen en paquetes adicionales con sufijos como -devel o -dev. Estos paquetes incluyen, entre otras cosas, archivos de cabecera y archivos de configuración para herramientas como pkg-config.
Aquí es donde pkg-config se vuelve fundamental. El problema con las dependencias en C es que los archivos de cabecera y las bibliotecas pueden estar instalados en diferentes ubicaciones, y algunas dependencias pueden requerir otras, lo que complica la tarea de indicar al compilador dónde buscar todo. pkg-config actúa como una base de datos que conoce la ubicación de estos archivos y las opciones necesarias para compilar y enlazar correctamente.
Cuando instalamos la versión de desarrollo de una biblioteca, esta suele incluir un archivo .pc que describe dónde están sus archivos de cabecera y bibliotecas, y qué flags deben usarse. Así, con pkg-config podemos pedir directamente las opciones que necesitamos para compilar nuestro programa sin preocuparnos por las rutas exactas.
Por ejemplo, si queremos usar GTK 3.0, podemos listar las dependencias instaladas con:
pkg-config --list-all
Y luego obtener las opciones para el compilador con:
pkg-config --cflags gtk+-3.0
Esto nos devolverá una serie de flags -I que indican dónde están los archivos de cabecera, como:
-I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 ...
Estas rutas pueden ser muchas y complejas, ya que GTK depende de varias otras bibliotecas. Intentar ponerlas a mano sería tedioso y propenso a errores.
Para el enlazador, usamos:
pkg-config --libs gtk+-3.0
Que nos devuelve las opciones -l necesarias para enlazar con las bibliotecas dinámicas, por ejemplo:
-lgtk-3 -lgdk-3 -lpango-1.0 -lcairo ...
Podemos combinar ambas opciones para obtener todo de una vez:
pkg-config --cflags --libs gtk+-3.0
Y para compilar nuestro programa window.c que utiliza GTK, podemos usar la sustitución de comandos en Bash para pasar directamente estas opciones a gcc:
gcc -o window window.c $(pkg-config --cflags --libs gtk+-3.0)
Esto hace que el compilador reciba todas las rutas y bibliotecas necesarias sin que tengamos que escribirlas manualmente.
Por otro lado, algunas bibliotecas como SDL no usan pkg-config, sino que proporcionan su propia herramienta similar llamada sdl2-config. Su uso es muy parecido: con sdl2-config --cflags obtenemos las rutas de los archivos de cabecera, y con sdl2-config --libs las opciones para enlazar.
En definitiva, estas herramientas nos permiten gestionar las dependencias de manera sencilla y evitar complicaciones al compilar y enlazar, lo que acelera nuestro desarrollo y nos permite centrarnos en escribir código en lugar de en configurar rutas y flags manualmente. Para profundizar más, siempre podemos consultar el manual con:
man pkg-config
y descubrir todas las opciones que nos ofrece esta útil herramienta.