¿Cuál es el código de una librería?

Integrando Librerías de Terceros en Proyectos con CMake

27/11/2024

Valoración: 4.51 (9467 votos)

En el vasto universo del desarrollo de software, la eficiencia y la optimización son pilares fundamentales. Una de las estrategias más inteligentes para lograrlo es la reutilización de código. En lugar de construir cada funcionalidad desde cero, los desarrolladores experimentados recurren a las librerías de terceros, colecciones de código ya escritas, probadas y optimizadas que implementan funcionalidades específicas. Pero surge una pregunta crucial: ¿cómo se integran estas librerías externas en nuestros proyectos de manera organizada y efectiva? La respuesta a menudo reside en el uso de herramientas de construcción avanzadas como CMake.

¿Cómo instalar la librería?
Primero debemos instalar la librería en nuestro sistema para después poder utilizarlo. En los paquetes de código fuente que se comparten en GitHub, y muchos otros sitios, es típico que se incluya el archivo “README.md” o en español “LEERME.md” donde el creador da instrucciones específicas de cómo compilar y usar su código.

Este artículo te guiará a través del proceso de inclusión de una librería de terceros en tu proyecto utilizando CMake, centrándonos en un caso de uso real: la conexión a una base de datos PostgreSQL mediante la librería pqxx. Aprenderás a estructurar tu proyecto, configurar los archivos de CMake, compilar y ejecutar tu aplicación, todo ello con una explicación detallada de cada paso.

Índice de Contenido

¿Por Qué Usar Librerías de Terceros?

La adopción de librerías de terceros no es una moda, sino una práctica consolidada que aporta múltiples beneficios a cualquier proyecto de software:

  • No reinventar la rueda: El tiempo es oro en el desarrollo. ¿Por qué dedicar horas o días a implementar una funcionalidad que ya ha sido resuelta y perfeccionada por otros? Las librerías nos permiten aprovechar el trabajo de la comunidad y de expertos.
  • Ahorro de tiempo en la depuración: Las librerías populares y bien mantenidas suelen haber pasado por rigurosos procesos de prueba y depuración. Esto reduce significativamente la probabilidad de encontrar errores de bajo nivel en tu código, permitiéndote centrarte en la lógica de nuestro proyecto principal.
  • Mayor robustez y rendimiento: Muchas librerías están optimizadas para ofrecer el mejor rendimiento y estabilidad, superando lo que un desarrollador individual podría lograr en un tiempo limitado.
  • Acceso a funcionalidades complejas: Algunas tareas, como la criptografía, la manipulación de imágenes o la conectividad de red, son intrínsecamente complejas. Las librerías proporcionan interfaces sencillas para acceder a estas funcionalidades sin necesidad de entender los detalles de su implementación interna.

Preparando el Terreno: Estructura del Proyecto

Antes de sumergirnos en la configuración de CMake, es fundamental establecer una estructura de proyecto clara y organizada. Para nuestro ejemplo, crearemos un proyecto llamado "accessToDB":

$ mkdir -p accessToDB/{build,modules,thirdparty}
$ touch main.cpp CMakeLists.txt
$ cd accessToDB/

Esta serie de comandos crea la carpeta principal del proyecto y, dentro de ella, tres subdirectorios esenciales:

  • build/: Donde CMake generará los archivos de construcción y donde se compilará tu ejecutable. Mantener los archivos generados separados del código fuente es una buena práctica.
  • modules/: Un lugar para tus propios módulos de código reutilizable, si los tuvieras.
  • thirdparty/: El directorio clave donde alojaremos todo el código de terceros que utilicemos, como frameworks o librerías externas.

Finalmente, creamos los archivos main.cpp (nuestro código fuente principal) y CMakeLists.txt (el archivo de configuración de CMake) en la raíz del proyecto.

Obteniendo e Instalando la Librería de Terceros

Para nuestro caso de uso, utilizaremos la librería pqxx, que facilita la conexión a bases de datos PostgreSQL. La forma más común de obtener librerías es a través de sus repositorios de código fuente, como GitHub.

$ cd thirdparty

Desde su repositorio en GitHub (https://github.com/jtv/libpqxx/), descargamos el archivo ZIP que contiene el código fuente. Una vez descargado, lo descomprimimos dentro de la carpeta thirdparty:

$ unzip libpqxx-master.zip

Ahora que tenemos el código fuente de la librería, el siguiente paso es compilarla e instalarla en nuestro sistema. La mayoría de los paquetes de código fuente incluyen un archivo README.md (o LEERME.md en español) con instrucciones específicas sobre cómo compilar y usar el código. Es crucial leer este archivo para seguir las indicaciones del autor de la librería.

Generalmente, la instalación implica ejecutar una serie de comandos de compilación (a menudo usando CMake o Make directamente). Una vez compilada e instalada, la librería se copia en directorios estándar del sistema. Por ejemplo, en sistemas Linux Ubuntu 18.04, es común que se instale en /usr/local/include/ (para los archivos de cabecera) y /usr/local/lib/ (para los archivos binarios de la librería).

Aunque la carpeta de código fuente descargada podría eliminarse una vez instalada la librería, es recomendable conservarla si planeas compilar tu programa para diferentes plataformas (Windows, Mac, Linux). Esto se debe a que la compilación cruzada para distintas arquitecturas a menudo requiere acceso directo al código fuente de la librería. Este es un tema más avanzado que exploraremos en futuros artículos, pero es importante tenerlo en cuenta.

Configurando CMakeLists.txt para Incluir la Librería

Con la librería instalada en el sistema, el siguiente paso es indicarle a CMake cómo encontrarla y enlazarla con nuestro proyecto. Comenzaremos con un archivo CMakeLists.txt básico:

cmake_minimum_required(VERSION 3.10.2)
enable_language(CXX)
project(accessToDB)
add_executable(accessToDB main.cpp)

Este es el punto de partida. Ahora, para integrar libpqxx, necesitamos añadir las siguientes líneas antes de add_executable(accessToDB main.cpp):

# --- INCLUDE libpqxx LIBRARY TO PROJECT
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpqxx -lpq")
set(PQXX /usr/local/include/pqxx)
find_library(PQXX_LIB pqxx)
find_library(PQ_LIB pq)

Y luego, después de add_executable(accessToDB main.cpp), enlazamos la librería con nuestro ejecutable:

target_link_libraries(accessToDB ${PQXX_LIB} ${PQ_LIB})

Explicación Detallada de las Líneas de CMake

Comprender cada línea de configuración es fundamental para adaptar estos conceptos a tus propios proyectos. Vamos a desglosar las instrucciones añadidas:

1. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpqxx -lpq")

Esta línea modifica la variable CMAKE_CXX_FLAGS, que contiene los parámetros que se pasan al compilador C++ cuando construye tu programa. Al añadir "${CMAKE_CXX_FLAGS}" al principio, nos aseguramos de preservar cualquier configuración o bandera que ya existiera. Luego, añadimos -lpqxx y -lpq. Estas son banderas del enlazador (linker) que le indican que debe buscar e incluir las librerías llamadas pqxx y pq durante el proceso de enlazado. El prefijo -l es un estándar para especificar librerías.

2. set(PQXX /usr/local/include/pqxx)

Esta línea almacena la ruta donde se encuentran los archivos de cabecera (headers) de la librería pqxx en una variable llamada PQXX. Aunque find_library se encarga de localizar las librerías binarias, a veces es necesario especificar rutas de inclusión para que el compilador encuentre los archivos .h o .hpp de la librería. En este caso, se asume que los headers de pqxx están en /usr/local/include/pqxx.

3. find_library(PQXX_LIB pqxx) y find_library(PQ_LIB pq)

Estas instrucciones le indican a CMake que busque las librerías binarias llamadas pqxx y pq. CMake las buscará en las rutas por defecto donde el sistema operativo guarda las librerías (como /usr/lib, /usr/local/lib, etc.). Si las encuentra, sus rutas completas (por ejemplo, /usr/local/lib/libpqxx.a o /usr/lib/x86_64-linux-gnu/libpq.so) se almacenarán en las variables PQXX_LIB y PQ_LIB, respectivamente. Estas variables son cruciales porque contienen la ubicación exacta de las librerías que se usarán para el enlazado.

Para verificar los valores que toman estas variables durante la configuración de CMake, puedes añadir temporalmente las siguientes líneas después de find_library:

message("PQXX_LIB: ${PQXX_LIB}")
message("PQ_LIB: ${PQ_LIB}")

Estas líneas imprimirán las rutas en la salida de la terminal cuando ejecutes CMake, lo cual es útil para depuración.

4. target_link_libraries(accessToDB ${PQXX_LIB} ${PQ_LIB})

Esta es la línea final y una de las más importantes. Se usa para especificar qué librerías deben enlazarse con un ejecutable o una librería. En este caso, le decimos a CMake que el ejecutable accessToDB (el que creamos con add_executable) debe enlazarse con las librerías cuyas rutas están almacenadas en ${PQXX_LIB} y ${PQ_LIB}. El enlazado es el proceso que conecta el código de tu programa con el código de las librerías, permitiendo que tu programa utilice las funciones que estas proporcionan.

¿Cómo instalar la librería?
Primero debemos instalar la librería en nuestro sistema para después poder utilizarlo. En los paquetes de código fuente que se comparten en GitHub, y muchos otros sitios, es típico que se incluya el archivo “README.md” o en español “LEERME.md” donde el creador da instrucciones específicas de cómo compilar y usar su código.

Revisión del Archivo CMakeLists.txt Completo

Después de todas las modificaciones, tu archivo CMakeLists.txt debería verse así:

cmake_minimum_required(VERSION 3.10.2)

enable_language(CXX)
project(accessToDB)

# --- INCLUDE libpqxx LIBRARY TO PROJECT
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpqxx -lpq")
set(PQXX /usr/local/include/pqxx) # Esto podría no ser estrictamente necesario si los headers están en una ruta estándar.
find_library(PQXX_LIB pqxx)
find_library(PQ_LIB pq)

## --- OPTIONAL (para depuración)
message("PQXX_LIB: ${PQXX_LIB}")
message("PQ_LIB: ${PQ_LIB}")
## --- END OPTIONAL CODE

# --- make our executable file
add_executable(accessToDB main.cpp)

# --- link with pqxx library
target_link_libraries(accessToDB ${PQXX_LIB} ${PQ_LIB})

Código de Prueba: main.cpp

Para verificar que todo funciona correctamente, necesitamos un código de prueba en main.cpp que intente conectarse a una base de datos PostgreSQL y realizar una consulta simple. Asegúrate de adaptar los datos de conexión (dbname, hostaddr, port, user, password) y la consulta SQL a tu propia configuración de base de datos.

#include <iostream>
#include <pqxx/pqxx>

/*
* Archivo de prueba de una conexión con C++ estándar con PostgreSQL
*/
int main(int argc, char *argv[]) {
try {
pqxx::connection connect(
"dbname=pegasus hostaddr=127.0.0.1 port=5432 user=postgres password=580140*");

// Recuerda cambiar el nombre de la base de datos, usuario y contraseña
std::cout << "Connected to " << connect.dbname() << std::endl;

pqxx::work W(connect);
pqxx::result R = W.exec("SELECT var_name FROM conf_resources order by id asc;");
// Recuerda cambiar la consulta para que se ajuste a lo que tengas tu en tu propia base de datos

std::cout << "Found " << R.size() << " resources: " << std::endl;
for (auto row: R)
std::cout << row[0].c_str() << std::endl;

std::cout << "Making changes definite: ";
W.commit();
std::cout << "OK." << std::endl;

} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}

Compilación y Ejecución del Proyecto

Con el CMakeLists.txt configurado y el main.cpp listo, es hora de compilar y ejecutar nuestro programa. Asegúrate de estar en la carpeta raíz de tu proyecto (accessToDB/).

Primero, le indicamos a CMake que genere los archivos de construcción dentro de la carpeta build/. Esto se conoce como el paso de "configuración":

$ cmake -H. -Bbuild

La salida de este comando mostrará cómo CMake detecta el compilador, el sistema operativo y, crucialmente, los valores de PQXX_LIB y PQ_LIB si incluiste las líneas message(). Una vez que la configuración se complete exitosamente, verás mensajes como -- Configuring done y -- Generating done.

A continuación, procedemos a la compilación del proyecto utilizando los archivos generados por CMake. Este es el paso de "construcción":

$ cmake --build build/

Este comando invocará el sistema de construcción subyacente (como Make en Linux) para compilar tu código fuente y enlazarlo con las librerías. Si todo está configurado correctamente, verás el progreso de la compilación y, finalmente, un mensaje indicando que el objetivo ha sido construido con éxito.

Finalmente, para ejecutar tu programa, navega a la carpeta build/ y ejecuta el binario generado. El nombre del ejecutable será el que especificaste en add_executable en tu CMakeLists.txt (en nuestro caso, accessToDB, aunque el autor en su ejemplo lo llama testPSQL):

$ ./build/accessToDB

Si todo ha sido configurado correctamente y tu base de datos PostgreSQL está accesible con los datos de conexión proporcionados, deberías ver una salida similar a esta:

Connected to pegasus
Found 10 resources:
SYS
AL99999
AL99998
AL99997
AL99996
AL99995
AL99994
AL99993
AL99992
AL99991
Making changes definite: OK.

¡Felicidades! Has integrado y utilizado con éxito una librería de terceros en tu proyecto C++ usando CMake.

Resumen de Comandos Clave en CMake

Para consolidar lo aprendido, aquí tienes una tabla que resume los comandos de CMake que hemos utilizado y su propósito:

Comando CMakePropósitoEjemplo en este Artículo
cmake_minimum_requiredDefine la versión mínima de CMake requerida para el proyecto.cmake_minimum_required(VERSION 3.10.2)
enable_languageHabilita el soporte para un lenguaje de programación específico.enable_language(CXX)
projectDefine el nombre del proyecto.project(accessToDB)
add_executableCrea un objetivo ejecutable a partir de los archivos fuente.add_executable(accessToDB main.cpp)
setEstablece el valor de una variable de CMake.set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpqxx -lpq")
find_libraryBusca una librería en las rutas estándar del sistema y almacena su ruta.find_library(PQXX_LIB pqxx)
target_link_librariesEnlaza un objetivo (ejecutable o librería) con otras librerías.target_link_libraries(accessToDB ${PQXX_LIB} ${PQ_LIB})
messageImprime mensajes en la salida de la consola de CMake (útil para depuración).message("PQXX_LIB: ${PQXX_LIB}")
Tabla de Comandos Clave de CMake

Preguntas Frecuentes (FAQ)

¿Por qué es importante usar CMake para gestionar librerías?

CMake es un generador de sistemas de construcción que simplifica el proceso de compilación de proyectos complejos, especialmente aquellos que dependen de librerías externas o que necesitan ser portables entre diferentes sistemas operativos y entornos de desarrollo. Permite definir cómo se construye tu proyecto de una manera independiente de la plataforma, generando archivos de proyecto para Makefiles, Visual Studio, Xcode, etc. Esto evita la necesidad de configurar manualmente las rutas de inclusión y enlazado para cada librería en cada entorno.

¿Qué significa 'reinventar la rueda' en el contexto de las librerías?

Significa desarrollar desde cero una funcionalidad o característica que ya ha sido implementada, probada y está disponible en forma de librería. Por ejemplo, si necesitas conectar tu aplicación a una base de datos, en lugar de escribir todo el código para manejar las conexiones, consultas SQL, etc., desde cero (reinventar la rueda), puedes usar una librería existente como pqxx que ya ofrece esa funcionalidad.

¿Cuál es la diferencia entre set(CMAKE_CXX_FLAGS) y target_link_libraries?

CMAKE_CXX_FLAGS es una variable global que contiene banderas (opciones) que se pasan al compilador C++ para todas las unidades de compilación en el proyecto. Se utiliza para configurar el comportamiento del compilador, como optimizaciones, estándares de C++, o incluso para añadir rutas de inclusión globales (aunque para rutas de inclusión específicas de objetivos se prefiere target_include_directories). En este tutorial, se usa para añadir banderas de enlazador (-l) de forma más global, aunque para el enlazado específico de objetivos, target_link_libraries es el método preferido y más moderno.

target_link_libraries es un comando que especifica las librerías con las que un objetivo específico (como un ejecutable o una librería propia) debe enlazarse. Es más granular y se recomienda para gestionar dependencias de librerías, ya que CMake puede propagar automáticamente las propiedades de las librerías enlazadas (como rutas de inclusión) a los objetivos que las utilizan.

¿Qué hago si find_library no encuentra mi librería?

Si find_library no logra localizar la librería, las variables (PQXX_LIB, PQ_LIB) quedarán vacías o no contendrán las rutas esperadas, lo que provocará errores de enlazado más adelante. Las razones comunes incluyen:

  • La librería no está instalada en tu sistema o no está en una ruta que CMake busca por defecto.
  • El nombre de la librería que estás buscando (ej. pqxx) no coincide con el nombre de archivo real de la librería (ej. libpqxx.a o libpqxx.so).
  • Permisos incorrectos que impiden a CMake acceder a los directorios de la librería.

Para solucionarlo, puedes:

  • Verificar que la librería esté correctamente instalada y dónde.
  • Especificar rutas de búsqueda adicionales a find_library usando el argumento PATHS.
  • Asegurarte de que el nombre de la librería en find_library sea el correcto (sin el prefijo 'lib' ni la extensión).

¿Es mejor instalar las librerías globalmente o incluirlas directamente en mi proyecto?

Ambas opciones tienen ventajas y desventajas. Instalar librerías globalmente (en /usr/local/lib, por ejemplo) es conveniente porque el sistema ya las conoce y pueden ser compartidas por múltiples proyectos, ahorrando espacio. Sin embargo, puede llevar a problemas de compatibilidad si diferentes proyectos requieren distintas versiones de la misma librería.

Incluir las librerías directamente en tu proyecto (en una carpeta thirdparty como hicimos) asegura que tu proyecto siempre use la versión específica de la librería con la que fue desarrollado y probado, lo que es ideal para la reproducibilidad y la compilación cruzada. Sin embargo, esto puede aumentar el tamaño de tu repositorio y requiere que cada proyecto gestione sus propias dependencias. Para proyectos grandes y complejos, o aquellos que necesitan ser altamente portables, la inclusión local o el uso de gestores de paquetes específicos para C++ (como Conan o vcpkg) suelen ser la opción preferida.

La capacidad de integrar librerías de terceros de manera efectiva es una habilidad invaluable para cualquier desarrollador. Al dominar CMake, no solo simplificas la gestión de dependencias, sino que también construyes una base sólida para proyectos de software más robustos, eficientes y mantenibles. ¡Sigue explorando y construyendo!

Si quieres conocer otros artículos parecidos a Integrando Librerías de Terceros en Proyectos con CMake puedes visitar la categoría Librerías.

Subir