Mostrando entradas con la etiqueta linux. Mostrar todas las entradas
Mostrando entradas con la etiqueta linux. Mostrar todas las entradas

miércoles, 18 de septiembre de 2013

Autotools(1). Hola mundo a la autotools.

En lo que sique(incluido este como el primero) estaré publicando una serie de posts relacionados con la creación y configuración de proyectos basados en el grupo de herramientas de autotools(antes es aconsejable revisar el directorio /usr/share/doc/gnu-standards, de este no existir ejecuta apt-get install gnu-standards y además ver también este enlace a wikipedia) y algunas otras que se le integran muy bien, además mencionar que me basaré en los lenguajes C/C++ pero que bien se adapta a otro tipo de lenguajes como son C, C++, Java, Python, Fortran, Yacc, awk, etc.
Primero que todo veamos la estructura básica de un proyecto, los directorios y archivos principales que lo componen y después veremos como editamos estos últimos para lograr un proyecto muy básico, nuetro “hola mundo”.
Estructura básica:
Escojemos un directorio en el cual relizaremos nuestro trabajo y ejecutamos la siguiente secuencia de comandos para crear las bases del proyecto.
mkdir -p hola_mundo/src
cd hola_mundo/
touch Makefile.am configure.ac src/Makefile.am src/hello-world.c NEWS AUTHORS ChangeLog README MAINTAINERS HACKING VERSION THANKS TODO

Ejecutando el comando tree -v(man tree) ya visualizamos la estructura actual del proyecto (Fig 1) donde tenemos los archivos principales de información, (compuesto por 2 grupos, los obligatorios Fig 2 (aquí faltan COPYING e INSTALL lo que no los pondré ya que autotools te propone los correspondientes que deberás editar para adptarlo a las necesidades de tu proyecto) y los opcionales (Fig 3)), los archivos inherentes a las herramientas de autotools también llamados de configuración (Fig 4), y por su puesto los archivos/directorios donde estará el código fuente(en este caso solo uno).



Como contenido de estos archivos (los de información) se tiene una explicación sobre si mismos que se puede ver en un link que dejo al final con un ejemplo.
Edición de los archivos de configuración de autotools:
#configure.ac
AC_PREREQ([2.69])
AC_INIT([hola-mundo],[0.0.0], [denisacostaq@gmail.com], [hola_mundo], [blogspoot.com/embelinux])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.10.1])
AC_PROG_CC
AC_OUTPUT([Makefile src/Makefile])
Comentaré superficialmente el contenido de este archivo, para una información más detallada ver /usr/share/doc/autoconf-doc/ (apt-get install autoconf-doc), este archivo se escribe en M4.
- AC_PREREQ advierte sobre la verción mínima de autoconf con que debe contar tu sistema (autoconf –version, para saber que valor poner en tu caso).
- AC_INIT tiene como parámetros el paquete, la verción, el e-mail de reporte de bugs, el nombre de empaquetamiento y la url del proyecto en ese orden y no siendo obligatorios todos estos parámetros.
- AC_CONFIG_HEADERS dice donde poner el archivo config.h(que puede tener otro nombre) el cual puede ser usado en el código de nuestro proyecto.
- AM_INIT_AUTOMAKE automake y especificamos que verción (automake –version). Se pueden escribir macros para automake, estas comienzan con el prefijo AM_ .
- AC_PROG_CC especificamos que usaremos el compilador de c y se le pudiera pasar un lista de comprobación, sino la pasamos autoconf comprueba usando una que tiene internamente.
- AC_OUTPUT especificamos los lugares en que queremos un Makefile(también se pueden obtener otros ficheros) como salida.
#Makefile.am
SUBDIRS = src
EXTRA_DIST =  README THANKS TODO HACKING MAINTAINERS
Para este archivo se puede leer el manual GNU Automake.
- SUBDIRS especifica los directorios por los que se debe mover make, es aproximadamente el orden que sequirá el “topological sort”.
- EXTRA_DIST es para incluir en el empaquetado archivos que no son indispensables en el proyecto.
#src/Makefile.am
bin_PROGRAMS = hola-mundo
hola_mundo_SOURCES = hello-world.c
En la variable bin_PROGRAMS especificamos el o los programas binarios que se van a crear. Después como muestra hello_world_SOURCES especificamos el código fuente a partir del cual se creará cada binario fijandote en tranformar el nombre del binario a su forma canónica, ej:hola-mundo -> hola_mundo y agregandole el sufijo _SOURCES.
Una vez editados estos archivos de configuración de proyecto ponemos el código, ej: printf (“hello world\n”); y por último coenzamos a invocar a las herramientas de autoconstrucción.
aclocal
autoheader
autoconf
automake -a (con la opcion -a no pone el archivo COPYING(enlace a /usr/share/automake-1.13/COPYING) e INSTALL(enlace a /usr/share/automake-1.13/INSTALL) como ya había mencionado)
./configure (en este punto se chequea si nuesto sistema está preparado para compilar el programa, por ejemplo desintala el compilador y verás lo que sucede).
make (ya podemos ejecutar nuestro programa que se llama hello_world y está en src).
La llamada a aclocal busca todas las macros m4 en todos los archivos *.m4 en el directorio donde se ejecuta y finalmente en configure.ac poniendolas todas juntas en el archivo aclocal.m4 que genera. El comando autoheader crea el archivo.h.in, autoconf es el responsable de crear el archivo(scrip) configure, automake genera los archivos Makefile.in ,y por ultimo ./configure es quien genera los Makefile a partir de Makefile.in y config.h a partir de config.h.in. A modo de resumen, se muestra en el siguiente diagrama el flujo del funcionamiento de las herramientas (Fig 5).
Fig 5
Adicionalmente podemos ejecutar como root make install que instala nuestro programa en el sistema, por defecto en /usr/local, podemos modificar esto volviendo a ejecutar ./configure pero esta vez con el parametro –prefix=PATH. También podemos ejecutar make dist y nos empaqueta el proyecto.
Como nota adicional comentar que debes tener todas estas herramientas en tu sistema para trabajar con autotools pero una vez que tengas el empaquetado solo necesitas los compiladores, bibliotecas, frameworks y todo de lo que dependa tu programa para compilarse pero no las herramientas con las cuales creates el paquete, o sea ya podrás ejecutar el clásico ;) (./configure ; make ; sudo make install).

git clone https://github.com/denisacostaq/EmbeLinux --branch hola-mundo

lunes, 16 de septiembre de 2013

Entendiendo la escencia de las bibliotecas mediante ejemplos de su creación y utilización.


Como ejemplo tomaremos la creación de una biblioteca para cálculos matemáticos, primero la haremos y usaremos estática y después compartida.
Biblioteca estática:
En una biblioteca estática tenemos una serie de procedimientos que son comunes pero cada programa que compilemos a partir de ella incluirá los archivos de los que use cualquier cosa dentro de su propio código atentando contra el tamaño del archivo binario final, no será necesario tener la biblioteca en el entorno en que va a correr el programa.

//plus.c
int plus (int sum1, int sum2)
{ 
    return (sum1 + sum2); 
}

//mult.c
int mult (int fact1, int fact2)
{
    return (fact1 * fact2);
} 
Para obtener el código objeto de estas dos funciones y/o archivos ejecutamos: gcc plus.c mult.c -c, con la opción -c le decimos a gcc que compile o ensamble nuestro código pero que no lo elnace (gcc se refiere a GNU Collection Compilers y/o a GNU C Compiler, pero en este último caso aclarar que no es en realidad gcc quien compila sino que es más bién un driver, o sea encarga a otros programas a hacer el trabajo). Para crear la bibliteca ejecutamos ar -r libmat.a plus.o mult.o, con esto libmat.a queda lista para ser usada como una biblioteca estática (rm *.o *.c).

//main .c
#include <stdio.h>
int plus (int, int);
int mult (int, int);
int main (int argc, char *argv[])
{
    int val1 = 2;
    int val2 = 5;
    printf (“%d + %d = %d\n”, val1, val2, plus (val1, val2));
    printf (“%d * %d = %d\n”, val1, val2, mult (val1, val2));
    return 0;
}
gcc main.c -lmat -o program (produce el siguiente error)
/usr/bin/ld: cannot find -lmat
collect2: error: ld returned 1 exit status 
Ahora ejecutamos gcc main.c libmat.a -o program que sí compila correctamente pero le estamos pasando la ruta de la lib de forma explícita, la manera elegante sería diciendole a gcc enlazate a mat(-lmat) para que él solo la busque pero no lo hemos conseguido porque el enlazador(ld) ha buscado en los directorios que tiene por defecto normalmente(/lib y /usr/lib más cat /etc/ld.so.conf.d/*) así que podemos agregar nustro directorio actual a las rutas de búsqueda con gcc main.c -L. -lmat -o program. Una forma más elegante de compilar es usando la variable de entorno LIBRARY_PATH, que sería así:
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmat -o program
Además para ver lo que decía sobre que gcc es un driver podrias ejecutar gcc -### -lmat main.c -o program con lo que gcc te dice lo que haría si le quitas -### pero no lo hace.
Biblioteca compartida:
A diferencia de una biblioteca estática los programas que se compilan con bibliotecas compartidas (shared object, extención .so) no incluyen el código de estas dentro de sí, sino información de con que lib ha sido enlazado el programa de manera que cuando el programa se va a ejecutar el linkeador (ld-linux, no el que se usa para compilar) determina de qué bibliotecas depende nuestro programa para estar en RAM y de faltar alguna la carga, por ejemplo para el programa que habiamos compilado hasta ahora podemos saber de que depende usando ldd program, deberás notar que no depende de libmat.
Para crear una biblioteca comartida solo tenemos que especificarle esto a gcc con shared, usando todo el codigo que hasta ahora teniamos solo cambiaremos la manera en que creamos la lib y el programa y veremos el impacto:
gcc -c -fPIC mult.c plus.c
gcc -shared mult.o plus.o -o libmat.so
gcc main.c -lmat -o program (que tendrás un error, así que resuelvelo... ;) )
./prgram (te generará el siguiente error, ./program: error while loading shared libraries: libmat.so: cannot open shared object file: No such file or directory, si ejecutamos ldd program verás que a diferencia de el caso pasado este si depende de libmat y que además “not found”, para resolver esto tenemos que agregar como ruta de busqueda al elnlazador ld-linux(no el de compilar insisto), la ruta donde esté nuestra lib o mejor una solución más pacífica export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. , con lo que puedes volver a ejecutar ldd para ver el cambio y además ya puedes ejecutar a ./program).


Bibliotecas dinámicas:
En este punto solo mencianaré que las bibliotecas dinámicas son una “forma” de las bibliotecas compartidas la diferencia es que en ahora el SO no autodetecta las bibliotecas de que depende el programa y las carga de forma automática, es el programador quien tiene esta responsabilidad, básicamente se cargan bajo demanda, tiene la ventaja de que la aplicación se carga en RAM más rápidamente y el inconveniente de que es el programador quien tiene la responsabilidad de hacer entonces ese trabajo, pudiera ser por ejemplo usando la lib ltdl que cuenta con funciones como dlopen.
En algún que otro post relacionado con autotools+libtool veremos como los humanOS hacemos esto, ya que hasta ahora solo vimos el método de los  “dinosauriOS” :-D .