miércoles, 26 de septiembre de 2012

Compilación y bibliotecas (parte 4): utilizar bibliotecas desde ZinjaI

En este post voy a tratar de ser bien práctico y dar instrucciones precisas de qué hacer para utilizar bibliotecas no estándar en ZinjaI. Lo primero que hay que saber es qué se necesita para utilizar una biblioteca. Repasando los posts anteriores se llega a la conclusión de que se necesitan dos cosas, los archivos de cabecera (.h o .hpp), y los binarios (.dll, .lib, .so, o .a). Los primeros son necesarios para compilar las partes del programa cliente que hacen uso de la biblioteca. Estos fuentes deberán incluirlos (con "#include") para que el compilador no se queje de que no conoce las clases o funciones de la biblioteca al querer utilizarlas. Los segundos son necesarios para enlazar el ejecutable final, ya que los primeros solo decían qué hace la biblioteca, pero no cómo. En algunos casos particulares puede que esté todo en los .h y no haya binarios. Esto suele ocurrir cuando hay templates involucrados (como la biblioteca CImg por ejemplo), o cuando por decisión de diseño porque la biblioteca no es tan compleja, el que la desarrolló lo hizo así para facilitar su utilización (por ejemplo, la biblioteca EasyBMP), pero son excepciones (y aún así requieren binarios del sistema, bibliotecas de Windows por ejemplo), normalmente se requieren las dos cosas (como con GLUT, wxWidgets, SFML, QT, etc, etc, etc).

Hay que aclarar que un binario en particular será específico para una combinación compilador/sistema operativo. En el caso de ZinjaI en Windows, el conjunto de herramientas de compilación se conoce como MinGW32, por lo que al descargar los binarios de la biblioteca se debe elegir la versión para MinGW32. En el caso de GNU/Linux, el compilador será normalmente gcc, y dado que las bibliotecas se descargan casi siempre con el gestor de paquetes de la distribución no hay mucho para elegir, solo se debe tener cuidado de instalar la versión de desarrollo de la misma (la que dice dev o devel). Si la biblioteca no está en el gestor de paquetes, la instalación desde fuentes dependerá de cada una, aunque usualmente consiste en ejecutar "configure; make; sudo make install", pero los detalles son tema de otro post. En una biblioteca más o menos compleja tendremos varios binarios (y tal vez dependa de otros binarios más, como los de las bibliotecas de Windows), y puede que tengamos también varias versiones, (debug o release, estática o dinámica, etc), y cual elegir dependerá del uso que le demos.

Una vez que tenemos todos los archivos necesarios, procedemos a configurar un proyecto. En ZinjaI se puede trabajar con programas simples o con proyectos.
  1. Lo recomendable en estos casos es crear un proyecto. Para eso vamos al menú "Archivo"->"Nuevo proyecto...", y elegimos un nombre y una plantilla. Si ya existe una plantilla para la biblioteca que queremos utilizar (como puede ser el caso de wxWidgets por ejemplo), no hay más que hacer. 
  2. Si no existe la plantilla adecuada, después de crear el proyecto tendremos que configurarlo para que encuentre todos esos archivos que mencionaba antes. Para esto hay que ir al menú "Ejecución" y elegir "Opciones...". En el cuadro de diálogo que aparece nos van a interesar dos pestañas: "Compilación" y "Enlazado". 
  3. En "Compilación" es donde tenemos que decirle dónde debe buscar los archivos de cabecera, indicando la carpeta que los contiene en el campo "Directorios adicionales para buscar cabeceras". Cuales archivos de cabecera utilizar se dice después en los fuentes con "#include". 
  4. En "Enlazado" es donde tenemos que decirle cuáles binarios utilizar y también dónde buscarlos. Cuáles binarios utilizar va en "Bibliotecas a enlazar", mientras que el directorio que los contiene va en "Directorios adicionales para buscar bibliotecas". La lista de binarios va sin extensión, y sin el prefijo "lib". Por ejemplo, si un binario es "libpng.so", se pone solo "png". 
Para poner más de un archivo o de una carpeta, se ingresan separados por espacios o por comas, da igual. Si el nombre tiene espacios (como "Documents and settings") va entre comillas, pero lo más recomendable es usar nombres y direcciones sin espacios para evitar problemas. Lo único que puede faltar, es definir alguna constante de preprocesador en la pestaña compilación. Son pocos los casos que lo requieren, y para saberlo hay que leer la documentación de la biblioteca. Por ejemplo, cuando se usa la biblioteca freeglut en versión estática se debe definir una constante, mientras que cuando se usa en versión dinámica no (ya que hay algún detalle de implementación que cambia entre una versión y otra, y el .h es el mismo, entonces un "#ifdef" en algún lado hará la diferencia).
Ejemplo de configuración para utilizar wxWidgets en Windows
 (click en la imagen para ampliarla)

A todo esto queda una pregunta interesante: ¿donde se guardan esas cosas? Es decir, cuando bajo los archivos binarios y cabeceras, ¿donde los pongo? Acá hay que pensar que "visibilidad" quiero que tengan.
  • Si queremos que la biblioteca quede instalada para usar desde cualquier proyecto, en GNU/Linux no hay que hacer nada ya que se instala así por defecto, y las rutas donde se instalan ya son consideradas por el compilador sin decirle nada, entonces solo hay que completar la lista de binarios. En Windows, lo que recomiendo es poner los archivos en la carpeta del compilador (que usualmente será c:\donde_sea_que_instalaron_zinjai\mingw). Allí es conveniente hacer una carpeta para la biblioteca, y dentro de la carpeta hacer dos subcarpetas, una "include" para las cabeceras, y una "lib" para los binarios. Desde el proyecto de ZinjaI, en la ruta, donde pongamos "${MINGW_DIR}" se reemplazará por la ruta completa de esa instalación de ZinjaI, y de esta forma la configuración funciona sin importar donde instalo ZinjaI (o más precisamente MinGW). 
  • Si queremos que la biblioteca sirva solo para ese proyecto se puede poner en una subcarpeta dentro de la carpeta de proyecto, y en las rutas ingresar paths relativos pensando en la carpeta de proyecto como base. Es decir, si pongo "C:\programa files\zinjai\mingw\wx\lib" o mejor "${MINGW_DIR}\wx\lib" son direcciones absolutas, pero si pongo "wx\lib" es relativa y suponiendo que el proyecto esté en "D:\MiProyecto", la dirección real que se formará será "D:\MiProyecto\wx\lib".

Vamos ahora a los posibles errores que uno puede encontrar. Supongamos que foo es un elemento (clase o función) de la biblioteca, y bar el nombre de los archivos de la misma.
  • Si al querer compilar obtenemos cosas como "foo was not declared in this scope", es porque nos falta agregar el #include. Al agregar el #include, hay que saber que si ponemos el nombre del archivo entre comillas lo busca primero en la misma carpeta que el fuente que lo incluye, mientras que si lo ponemos entre signos mayor y menor los busca en las carpetas del compilador y las definidas en la configuración del proyecto directamente.
  • Si al hacer el #include obtenemos algo como "bar.h: No such file or directory" es porque no está encontrando el archivo de cabecera. Hay que repasar el campo "Directorios adicionales para buscar cabeceras", o ver si el nombre del archivo está bien escrito. En algunos casos, el nombre lleva una subcarpeta, como por ejemplo GLUT, donde el #include correcto es "#include <GL/glut.h>", porque el .h está en una subcarpeta "GL". Si están en GNU/Linux recuerden que se distinguen mayúsculas de minúsculas, tanto en los #include como en la configuración del proyecto. Podemos corroborar estas cosas abriendo con el explorador a la carpeta "include" de la biblioteca o del sistema para ver qué subcarpetas y archivos hay.
  • Si vemos un error como "cannot find -lbar", es porque no está encontrando los binarios, y hay que repasar el campo "Directorios adicionales para buscar bibliotecas". Podemos corroborar estas cosas abriendo con el explorador a la carpeta "lib" de la biblioteca o del sistema para ver qué archivos hay.
  • Si en la etapa de enlazado vemos un error como "undefined reference to foo", quiere decir que está faltando agregar algún archivo binario a la lista de "bibliotecas a enlazar". En el caso de Windows, puede que el archivo esté, pero que haya más de uno y el orden no sea el adecuado. Se deben colocar de más específicos a más generales. En realidad, la regla es que si hay dos: lib1 y lib2; y lib2 usa cosas de lib1, va primero lib2. Usualmente esto implica un poco de prueba y error, si estamos seguros de que están todos pero no tenemos claro el orden.
  • Si al querer ejecutar vemos algún error diciendo de alguna manera que no encuentra un .dll o un .so, es porque enlazamos dinámicamente y entonces tenemos que tener los .dll o los .so para poder ejecutar. Si se tienen estos archivos, deben colocarse en algún lugar apropiado. Para que estén disponibles para todo el sistema, en GNU/Linux suelen ir a "/usr/lib" o "/usr/local/lib" (o lib64), mientras que en Windows a "C:\Windows\System32". Si queremos que solo estén disponibles para nuestro programa, lo mejor es colocarlos en la misma carpeta que el ejecutable. En el caso de Windows, los busca siempre primero ahí, y si no los encuentra busca donde diga la variable de entorno PATH. En el caso de GNU/Linux, busca donde diga la variable de entorno LD_LIBRARY_PATH (que debe incluir explícitamente "." si queremos que busque en la carpeta de trabajo del ejecutable). Estas variables todavía no se pueden configurar desde ZinjaI, así que deben configurarse desde afuera y estar definidas al cargar ZinjaI. Sin embargo, en la mayoría de los casos los .so estarán en "/usr/lib" si es GNU/Linux, o los .dll se pueden copiar a la carpeta del proyecto en Windows, y entonces no es necesario modificar esas variables.
  • Si aparece otro problema, Google es tu amigo, y sino prueba en los foros de ZinjaI (que estarán habilitados muy pronto).
Para los que les interese el detalle de la llamada al compilador, los argumentos interesantes se pasan con: -L para las carpetas que contienen las bibliotecas, -l (L minúscula) para los nombres de la bibliotecas, -I (i mayúscula) para las rutas que contienen las cabeceras, y -D para las constantes de preprocesador. Da igual si en la configuración del proyecto, en lugar de configurar cada campo se colocan los argumentos completos con estos modificadores en los campos "Parámetros extra para la compilación" y "Parámetros extra para el enlazado". La ventaja de estos campos, es que podemos poner ahí directamente lo que sale de herramientas como pkg-config (o sus variantes específicas como por ejemplo wx-config), que suelen estar presentes en sistema GNU/Linux y cuyo objetivo es decirnos exactamente qué argumentos pasarle al compilador para usar una biblioteca. Además, se puede poner directamente la llamada a estas herramientas entre caracteres de acentos invertidos (`así`, como cuando se invocan subcomandos en un makefile), para que ZinjaI las ejecute y las reemplace por la salida. Un ejemplo de esto está en la configuración de la plantilla de wxWidgets para GNU/Linux.

 Ejemplo de configuración para utilizar wxWidgets en GNU/Linux 
(click en la imagen para ampliarla)

Como siempre, lo ideal sería que el IDE gestione todo esto de forma más automática. Por ahora lo que hay para ello son las plantillas de proyectos, pero solo funcionan si quieren hacer un proyecto que utilice alguna de las bibliotecas que tienen plantillas, que son pocas (oficialmente: wxWidgets, OpenGL+GLUT y SFML). Siempre pueden crear ustedes sus propias plantillas (en otro post explicaré mejor cómo, pero básicamente es configurar un proyecto y copiar su carpeta en la carpeta "templates" de ZinjaI), y además en las próximas versiones de ZinjaI incluiré algún mecanismo más fácil para combinar plantillas, de forma que teniendo por ejemplo la de SFML y la de wxWidgets, podamos con tres clicks configurar un proyecto que use simultáneamente SFML y wxWidgets. Por ahora eso es todo. No dejen de pedirme plantillas para las bibliotecas populares, o compartir las que hagan ustedes si se animan, la idea es que todo eso vaya engordando de a poco la sección de descarga de complementos de ZinjaI.

Este post es continuación de Compilación y bibliotecas (parte 3): ese oscuro y mágico proceso y sigue en Compilación y bibliotecas (parte 5): crear bibliotecas con ZinjaI

8 comentarios:

  1. Hola! soy un inútil... intenté preparar un proyecto con Box2D y no me funcó... el cmake me tiró la bronca con el tipo de proyecto... te lo puedo pedir al complemento??? aguante ZinJai, quedaría buenísimo! Saludos!

    ResponderEliminar
  2. Hola, antes que nada quería agradecerle por este gran IDE. Realmente me vino como anillo al dedo, puesto que está en español y posee una interfaz bastante intuitiva. Quisiera pedirle si puede agregar una plantilla para proyectos SDL, específicamente SDL2, ya que intento agregar los directorios correspondientes a mi proyecto pero siempre me da error de compilación. Desde ya, muchísimas gracias!

    ResponderEliminar
  3. En primer lugar gracias por este valioso aporte, llevo ya 1 año usándolo y pues me encanta mucho este IDE, ayúdeme con algo estoy en un proyecto de la Universidad, necesito hacer uso de la función gotoxy pero no he podido, ya tengo agregada la biblioteca conio.h pero me dice que no ha sido declarada. Agradezco tu pronta ayuda.

    ResponderEliminar
    Respuestas
    1. La bibioteca conio no es estandar, por eso no está disponible en todos los compiladores, o al menos no en todas las versiones. Debería buscar alguna otra alternativa.

      Eliminar
    2. Creas tu funcion goto
      copias en un block de notas y lo guardas con extencion .h
      abres c:archivos de programa/zinjai/mingw/include
      y lo pegas ahi.

      Eliminar
  4. Hola, muy buena la IDE, estudio Ing. Electrónica y en 1er año el profe nos la mostró para usar en Informática I. En Informática II nos quisieron meter Eclipse pero nos resistimos y seguimos con Zinjai jeje.
    Mi consulta: Tengo que programar una comunicación paralela básica nada complicado algo como enviar y recibir datos, o prender unos leds. En los apuntes que nos paso el profe nos habla de la librería dos.h y outportb pero por lo que leí eso no se usa más desde turboC en win98 (xq desde WinXP el SO no da más acceso al vector de interrupciones de la BIOS para manejar el hardware, corrijan me si estoy equivocado), hay alguna librería o función que me de acceso al puerto paralelo? Ya se que el puerto paralelo es viejo y se esta dejando de usar pero por lo que se (se poco) todavía se usa un poco para electrónica. Gracias por todo. Saludos

    PD: Cualquier cosa que diga y parezca una burrada corrijan me que estoy estudiando para rendir y e conseguido muy poca información sobre el tema "programación de comunicación SERIE/PARALELO". Gracias!!!

    ResponderEliminar
    Respuestas
    1. Hola Felipe. Gracias por el comentario.

      Respecto a lo del puerto paralelo. Efectivamente los sistemas operativos modernos ya no ofrecen esa misma interfaz que se usaba desde dos y turboc ni nada parecido (ni se usa tampoco el vector de interrupciones). Por eso ya no funcionan, ni casi existen, aquellas bibliotecas. Pero seguramente sí ofrecen alguna otra interfaz, tal vez de un poco más alto nivel, para hacerlo, aunque no se cual es y dependerá de cada sistema operativo. No es mi fuerte la parte de hardware y drivers, así que no tengo experiencia como para recomendarte alguna, pero seguramente googleando algo se encuentra.

      En cualquier caso es más una pregunta de C/C++ y sistemas operativos que de ZinjaI, pero si encontrás algun ejemplo con alguna api o lib para C/C++ y tenés problemas para configurar el proyecto desde zinjai, ingresá al foro y continuamos ahí.

      Eliminar
    2. Ok, muchas gracias por tomarte el tiempo para responder. Voy a seguir buscando a ver que encuentro. Por lo pronto he visto unos vídeos de youtube donde agregan una biblioteca llamada input32.dll a system32, pero solo con eso ya funciona. Por lo que leí sobre librerías aquí necesitaría un archivo más además del .dll ... Cunado yo sigo los pasos del vídeo me sale LoadLibrary Failed.
      No sé si puedo poner el link del vídeo aquí.

      Eliminar