Capítulo 1. Prepárate para una compilación fácil
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Cuidado cariño porque estoy usando tecnología.
Iggy Pop, "Buscar y destruir"
La biblioteca estándar de C no es suficiente para hacer un trabajo serio.
En cambio, el ecosistema de C se ha expandido fuera del estándar, lo que significa que saber cómo llamar fácilmente a funciones de bibliotecas comunes pero que no forman parte del estándar ISO es esencial si quieres pasar de hacer ejercicios de libro de texto. Si quieres trabajar con un archivo XML, una imagen JPEG o un archivo TIFF, necesitarás libxml, libjpeg o libtiff, que están disponibles gratuitamente pero no forman parte del estándar. Desgraciadamente, este es el punto en el que la mayoría de los libros de texto se detienen y te dejan que lo resuelvas por ti mismo, razón por la que puedes encontrar detractores de C que dirán cosas autodisonantes como que C tiene 40 años, por lo que tienes que escribirlo todo desde cero en él;nunca averiguaron cómo enlazar con una biblioteca.
Este es el orden del día del capítulo:
- Instala las herramientas necesarias
- Esto es mucho más fácil que en los oscuros días en que tenías que buscar cada componente. Puedes montar un sistema completo con todos los extras en unos 10 ó 15 minutos (más todo el tiempo de descarga para cargar tantas cosas buenas).
- Compilar un programa en C
- Sí, ya sabes cómo hacerlo, pero necesitamos una configuración que tenga ganchos para las bibliotecas y sus ubicaciones; escribir simplemente
cc
myfile.c
ya no sirve. Make es casi el sistema más sencillo para facilitar la compilación de programas, por lo que proporciona un buen modelo para la discusión. Te mostraré el makefile más pequeño posible que ofrece suficiente espacio para crecer. - Configurar variables y añadir nuevas bibliotecas
- Sea cual sea el sistema que utilicemos, se basará en un pequeño conjunto de variables similares a las de entorno, así que hablaré de lo que hacen y de cómo configurarlas. Una vez que dispongamos de toda esa maquinaria de compilación, añadir nuevas bibliotecas será cuestión de ajustar las variables que ya hemos configurado.
- Establecer un sistema de compilación
- Como extra, podemos utilizar todo lo que hemos hecho hasta ahora para configurar un sistema de compilación aún más sencillo, que nos permitirá cortar y pegar código en el símbolo del sistema.
Una nota especial para los usuarios de IDE: puede que no seas usuario de make
, pero esta sección te resultará relevante, porque por cada receta que make
ejecuta al compilar código, tu IDE tiene una receta análoga. Si sabes lo que hace make
, te resultará fácil ajustar tu IDE.
Utiliza un gestor de paquetes
Si no utilizas un gestor de paquetes, te lo estás perdiendo.
Menciono los gestores de paquetes por varias razones: en primer lugar, puede que algunos de vosotros no tengáis instaladas las herramientas básicas. Para ti, he puesto esta sección en primer lugar en el libro, porque necesitas conseguir estas herramientas, y rápido. Un buen gestor de paquetes te instalará rápidamente un subsistema POSIX completo, compiladores para todos los lenguajes que conozcas, una gama medio decente de juegos, las herramientas habituales de productividad ofimática, unos cientos de bibliotecas C, etc.
En segundo lugar, como autores de C, el gestor de paquetes es un medio clave por el que podemos obtener bibliotecas para incorporarlas a nuestro trabajo.
En tercer lugar, si estás dando el salto de ser alguien que descarga paquetes a ser alguien que produce un paquete, este libro te llevará a mitad de camino, mostrándote cómo preparar tu paquete para una fácil autoinstalación, de modo que cuando el administrador de un repositorio de paquetes decida incluir tu código en el repositorio, no tenga ningún problema para construir el paquete final.
Si eres usuario de Linux, habrás configurado tu ordenador con un gestor de paquetes y ya habrás visto lo fácil que puede ser el proceso de obtención de software. Para los usuarios de Windows, cubriré Cygwin más adelante. Los usuarios de Mac tienen varias opciones, como Fink, Homebrew y Macports. Todas las opciones de Mac dependen del paquete Xcode de Apple, disponible gratuitamente a través de (según la añada de tu Mac) el CD de instalación del SO, el directorio de programas instalables, la App Store de Apple o registrándote como desarrollador en Apple.
¿Qué paquetes necesitarás? Aquí tienes un rápido resumen de los elementos básicos para el desarrollo en C. Dado que cada sistema tiene un esquema de organización diferente, algunos de ellos pueden estar agrupados de forma distinta, instalados por defecto en un paquete base o tener nombres extraños. En caso de duda sobre un paquete, instálalo, porque ya han pasado los días en que instalar demasiadas cosas podía causar de algún modo inestabilidad o ralentización del sistema. Sin embargo, probablemente no dispongas del ancho de banda (o tal vez ni siquiera del espacio en disco) para instalar todos los paquetes que se ofrecen, por lo que será necesario cierto criterio. Si ves que te falta algo, siempre puedes volver atrás y conseguirlo más tarde. Paquetes que definitivamente debes conseguir:
-
Un compilador. Instala definitivamente
gcc
;clang
puede estar disponible. -
GDB, un depurador.
-
Valgrind, para comprobar si hay errores de uso de memoria en C.
-
gprof
un perfilador. -
make
para que nunca tengas que llamar directamente a tu compilador. -
pkg-config
para encontrar bibliotecas. -
Doxygen, para generar documentación.
-
Un editor de texto. Hay literalmente cientos de editores de texto entre los que elegir. Aquí tienes algunas recomendaciones subjetivas:
-
Emacs y vim son los favoritos de los frikis empedernidos. Emacs es muy inclusivo (la E es de extensible); vim es más minimalista y es muy amigable para los mecanógrafos táctiles. Si esperas pasarte cientos de horas mirando un editor de texto, merece la pena dedicar tiempo a aprender uno de ellos.
-
Kate es amigable y atractiva, y proporciona un buen subconjunto de las comodidades que esperamos como programadores, como el resaltado de sintaxis.
-
Como último recurso, prueba nano, que es agresivamente sencillo y está basado en texto, por lo que funciona incluso cuando tu GUI no lo hace.
-
-
Si te gustan los IDE, hazte con uno, o con varios. De nuevo, hay muchos entre los que elegir; aquí tienes algunas recomendaciones:
-
Anjuta: en la familia GNOME. Amigable con Glade, el constructor de GUI de GNOME.
-
KDevelop: en la familia KDE.
-
XCode: El IDE de Apple para OS X.
-
Code::blocks: relativamente sencillo, funciona en Windows.
-
Eclipse: el coche de lujo con muchos posavasos y mandos adicionales. También multiplataforma.
-
En capítulos posteriores, hablaré de estas herramientas más pesadas:
-
Autotools: Autoconf, Automake, libtool
-
Git
-
Conchas alternativas, como la concha Z.
Y, por supuesto, están las bibliotecas C que te ahorrarán el trabajo de reinventar la rueda (o, para ser más metafóricamente exactos, reinventar la locomotora). Puede que quieras más, pero aquí están las bibliotecas que se utilizarán a lo largo de este libro:
-
libcURL
-
libGLib
-
libGSL
-
libSQLite3
-
libXML2
No hay consenso sobre los esquemas de denominación de los paquetes de bibliotecas, y tendrás que averiguar cómo le gusta a tu gestor de paquetes diseccionar una única biblioteca en subpartes. Normalmente hay un paquete para los usuarios y un segundo para los autores que utilizarán la biblioteca en su propio trabajo, así que asegúrate de seleccionar tanto el paquete base como los paquetes -dev
o -devel
. Algunos sistemas separan la documentación en otro paquete. Algunos requieren que descargues los símbolos de depuración por separado, en cuyo caso GDB debería guiarte por los pasos la primera vez que lo ejecutes en algo que carezca de símbolos de depuración.
Si utilizas un sistema POSIX, después de instalar los elementos anteriores, tendrás un sistema de desarrollo completo y estarás listo para empezar a codificar. Para los usuarios de Windows, daremos un breve rodeo para comprender cómo interactúa la configuración con el sistema principal de Windows.
Compilar C con Windows
En la mayoría de los sistemas, C es el lenguaje central, VIP, que todas las demás herramientas trabajan para facilitar; en un equipo Windows, C es extrañamente ignorado.
Así que necesito dedicar un poco de tiempo a discutir cómo configurar un sistema Windows para escribir código en C. Si no estás escribiendo en un sistema Windows ahora, puedes saltarte este segmento y pasar a "¿Por dónde se va a la biblioteca?
POSIX para Windows
Como C y Unix coevolucionaron, es difícil hablar de uno y no del otro. Creo que es más fácil empezar por POSIX. Además, aquellos de vosotros que estéis intentando compilar en un equipo Windows código que escribisteis en otro sitio, encontraréis que ésta es la ruta más natural.
Por lo que yo sé, el mundo de las cosas con sistemas de archivos se divide en dos clases ligeramente superpuestas:
-
Sistemas compatibles con POSIX
-
La familia de sistemas operativos Windows
El cumplimiento de POSIX no significa que un sistema tenga que parecer una caja Unix. Por ejemplo, el usuario típico de Mac no tiene ni idea de que está utilizando un sistema BSD estándar con una interfaz atractiva, pero los entendidos pueden ir a la carpeta Utilidades (dentro de la carpeta Aplicaciones), abrir el programa Terminal y ejecutar ls
, grep
y make
hasta saciarse.
Además, dudo que muchos sistemas cumplan el 100% de los requisitos de la norma (como tener un compilador Fortran '77). Para nuestros propósitos, necesitamos un intérprete de comandos que pueda comportarse como el intérprete de comandos POSIX básico, un puñado de utilidades (sed, grep, make, etc.), un compilador C99 y adiciones a la biblioteca C estándar como fork
y iconv
. Éstas pueden añadirse como complemento al sistema principal. Los scripts subyacentes del gestor de paquetes, las Autotools y casi cualquier otro intento de codificación portátil dependerán de estas herramientas en cierta medida, por lo que, aunque no quieras pasarte el día mirando el símbolo del sistema, te será útil tener estas herramientas para las instalaciones.
En los SO de clase servidor y en las ediciones completas de Windows 7, Microsoft ofrece lo que antes se llamaba INTERIX y ahora se denomina Subsistema para Aplicaciones basadas en Unix (SUA), que proporciona las llamadas al sistema POSIX habituales, el shell Korn y gcc
. Normalmente, el subsistema no se proporciona por defecto, sino que puede instalarse como componente adicional. Pero el SUA no está disponible para otras ediciones actuales de Windows y no lo estará para Windows 8, por lo que no podemos depender de que Microsoft proporcione un subsistema POSIX para sus sistemas operativos.
Si tuvieras que reconstruir Cygwin desde cero, éste sería tu programa:
-
Escribe una biblioteca C para Windows que proporcione todas las funciones POSIX. Esto tendrá que suavizar algunas incongruencias de Windows/POSIX, como que Windows tiene unidades distintas como C: mientras que POSIX tiene un sistema de archivos unificado. En este caso, alias C: como /cygdrive/c, D: como /cygdrive/d, etc.
-
Ahora que puedes compilar programas POSIX estándar enlazándolos con tu biblioteca, hazlo: genera versiones para Windows de
ls
,bash
,grep
,make
,gcc
,X
,rxvt
,libglib
,perl
,python
, etc. -
Una vez que tengas cientos de programas y bibliotecas construidos, configura un gestor de paquetes que permita a los usuarios seleccionar los elementos que quieren instalar.
Como usuario de Cygwin, todo lo que tienes que hacer es descargar el gestor de paquetes desde el enlace de instalación del sitio web de Cygwin y elegir paquetes. Sin duda necesitarás la lista anterior, además de un terminal decente (prueba mintty, o instala el subsistema X y utiliza el xterm, porque ambos son mucho más amigables que el de Windows cmd.exe
), pero verás que prácticamente todos los lujos familiares de un sistema de desarrollo están ahí en alguna parte.
En "Rutas", hablo de diversas variables de entorno que afectan a la compilación, incluidas las rutas para buscar archivos. Esto no es sólo para POSIX: Windows también tiene variables de entorno, que puedes encontrar en el segmento de configuración del sistema del panel de control. Cygwin es mucho más utilizable si añades su directorio bin
(probablemente c:\cygwin\bin
) al de Windows PATH
.
Ahora puedes ponerte a compilar código C.
Compilar C con POSIX
Microsoft proporciona un compilador de C++, en forma de Visual Studio, que tiene un modo de compatibilidad con C89 (comúnmente denominado ANSI C, aunque C11 es la norma ANSI actual). Éste es el único medio de compilar código C que proporciona actualmente Microsoft. Muchos representantes de la empresa han dejado claro que no está previsto nada más allá de la compatibilidad con algunas funciones de C99 (por no hablar de la compatibilidad con C11). Visual Studio es el único compilador importante que sigue estancado en C89, así que tendremos que encontrar ofertas alternativas en otra parte.
Por supuesto, Cygwin proporciona gcc
, y si has seguido e instalado Cygwin, entonces ya tienes un entorno de compilación completo.
Por defecto, los programas que compiles con Cygwin dependerán de su biblioteca de funciones POSIX, cygwin1.dll (tanto si tu código incluye realmente alguna llamada POSIX como si no). Si ejecutas tu programa en un equipo con Cygwin instalado, no tendrás ningún problema. Los usuarios podrán hacer clic en el ejecutable y ejecutarlo como se espera, porque el sistema debería ser capaz de encontrar la DLL de Cygwin. Un programa compilado con Cygwin puede ejecutarse en equipos que no tengan Cygwin instalado si distribuyes cygwin1.dll con tu código. En mi máquina, ésta es la ruta a cygwin /bin/cygwin1.dll. El archivo cygwin1. dll tiene una licencia similar a la GPL (véase "La barra lateral legal"), en el sentido de que si distribuyes la DLL por separado de Cygwin en su conjunto, entonces estás obligado a publicar el código fuente de tu programa.1
Si esto es un problema, tendrás que encontrar la forma de recompilar sin depender de cygwin1.dll, lo que significa eliminar cualquier función específica de POSIX (como fork
o popen
) de tu código y utilizar MinGW, como se explica más adelante. Puedes utilizar cygcheck
para averiguar de qué DLL depende tu programa, y verificar así que tu ejecutable enlaza o no con cygwin1.dll.
Nota
Para ver de qué otras bibliotecas depende un programa o una biblioteca enlazada dinámicamente:
-
Cygwin:
cygcheck
libxx
.dll -
Linux:
ldd
libxx
.so -
Mac:
otool -L
libxx
.dylib
Compilar C sin POSIX
Si tu programa no necesita las funciones POSIX, puedes utilizar MinGW (GNU Minimalista para Windows), que proporciona un compilador C estándar y algunas herramientas básicas asociadas. MSYS es un complemento de MinGW que proporciona un intérprete de comandos y otras utilidades útiles.
MSYS proporciona un intérprete de comandos POSIX (y puedes encontrar los terminales mintty o RXVT para ejecutar tu intérprete de comandos), o deja el símbolo del sistema por completo y prueba Code::blocks, un IDE que utiliza MinGW para la compilación en Windows. Eclipse es un IDE mucho más extenso que también puede configurarse para MinGW, aunque requiere un poco más de configuración.
O si te sientes más cómodo en una línea de comandos POSIX, configura Cygwin de todos modos, consigue los paquetes que proporcionan las versiones MinGW de gcc
, y úsalos para la compilación en lugar de la versión por defecto de Cygwin gcc
que enlaza con POSIX.
Si aún no conoces las Autotools, pronto las conocerás. La firma de un paquete creado con Autotools es su instalación de tres comandos: ./configure && make && make install
. MSYS proporciona suficiente maquinaria para que esos paquetes tengan muchas posibilidades de funcionar. O si has descargado los paquetes para construirlos desde el símbolo del sistema de Cygwin, puedes utilizar lo siguiente para configurar el paquete de modo que utilice el compilador Mingw32 de Cygwin para producir código libre de POSIX:
.
/
configure
--
host
=
mingw32
A continuación, ejecuta make && make install
como de costumbre.
Una vez que has compilado con MinGW, mediante la compilación desde la línea de comandos o con Autotools, tienes un binario nativo de Windows. Como MinGW no sabe nada de cygwin1.dll, y tu programa no hace llamadas POSIX de todos modos, ahora tienes un programa ejecutable que es un programa Windows de buena fe, que nadie sabrá que has compilado desde un entorno POSIX.
Sin embargo, MinGW tiene actualmente una escasez de bibliotecas precompiladas.2 Si quieres librarte de cygwin1.dll, no puedes utilizar la versión de libglib.dll que viene con Cygwin. Tendrás que recompilar GLib desde el código fuente a una DLL nativa de Windows, pero GLib depende de gettext
de GNU para la internacionalización, así que tendrás que compilar primero esa biblioteca. El código moderno depende de las bibliotecas modernas, así que puede que pases mucho tiempo configurando el tipo de cosas que en otros sistemas son una llamada de una sola línea al gestor de paquetes. Volvemos al tipo de cosas que hacen que la gente hable de que C tiene 40 años, así que tienes que escribirlo todo desde cero.
Así pues, ahí están las advertencias. Microsoft se ha alejado de la conversación, dejando que otros implementen un compilador y un entorno C post-grunge. Cygwin lo hace y proporciona un gestor de paquetes completo con suficientes bibliotecas para hacer parte o todo tu trabajo, pero está asociado a un estilo de escritura POSIX y a la DLL de Cygwin. Si eso es un problema, tendrás que trabajar más para construir el entorno y las bibliotecas que necesitarás para escribir un código decente.
¿Por dónde se va a la biblioteca?
Vale, ya tienes un compilador, una cadena de herramientas POSIX y un gestor de paquetes que instalará fácilmente unos cientos de bibliotecas. Ahora podemos pasar al problema de utilizarlas al compilar nuestros programas.
Tenemos que empezar con la línea de comandos del compilador, que rápidamente se convertirá en un lío, pero acabaremos con tres (a veces tres y medio) pasos relativamente sencillos:
-
Establece una variable que enumere las banderas del compilador.
-
Establece una variable que enumere las bibliotecas con las que enlazar. El paso intermedio es que a veces tienes que establecer sólo una variable para enlazar mientras compilas, y a veces tienes que establecer dos para enlazar en tiempo de compilación y en tiempo de ejecución.
-
Configura un sistema que utilice estas variables para orquestar la compilación.
Para utilizar una biblioteca, tienes que decirle al compilador que vas a importar funciones de la biblioteca dos veces: una para la compilación y otra para el enlazador. Para una biblioteca en una ubicación estándar, las dos declaraciones se producen mediante un #include
en el texto del programa y una bandera -l
en la línea del compilador.
El Ejemplo 1-1 presenta un rápido programa de ejemplo que realiza algunas operaciones matemáticas divertidas (al menos para mí; si la jerga estadística te suena a chino, no pasa nada). La función de error estándar de C99, erf(x)
, está estrechamente relacionada con la integral de cero a x de la distribución Normal con media cero y desviación típica √2. Aquí, utilizamos erf
para verificar un área popular entre los estadísticos (el intervalo de confianza del 95% para una prueba de hipótesis estándar large-n ). Llamemos a este archivo erf.c.
Ejemplo 1-1. Una sola línea de la biblioteca estándar. (erf.c)
#include <math.h>
//erf, sqrt
#include <stdio.h>
//printf
int
main
(){
printf
(
"The integral of a Normal(0, 1) distribution "
"between -1.96 and 1.96 is: %g
\n
"
,
erf
(
1.96
*
sqrt
(
1
/
2.
)));
}
Las líneas #include
deberían resultarte familiares. El compilador pegará aquí math.h y stdio.h en el archivo de código, y por tanto pegará declaraciones para printf
, erf
y sqrt
. La declaración en math.h no dice nada sobre lo que hace erf
, sólo que recibe un double
y devuelve un double
. Ésa es información suficiente para que el compilador compruebe la coherencia de nuestro uso y produzca un archivo objeto con una nota que diga al ordenador: "cuando llegues a esta nota, ve a buscar la función erf
y sustituye esta nota por el valor de retorno de erf
".
Es tarea del enlazador conciliar esa nota encontrando realmente erf
, que está en una biblioteca en algún lugar de tu disco duro.
Las funciones matemáticas que se encuentran en math.h están divididas en su propia biblioteca, y tendrás que indicárselo al enlazador añadiendo una bandera -lm
. Aquí, -l
es la bandera que indica que hay que enlazar una biblioteca, y la biblioteca en este caso tiene un nombre de una sola letra, m
. Obtienes printf
gratis, porque hay un -lc
implícito que pide al enlazador que enlace el libc
estándar asumido al final del comando de enlace. Más adelante, veremos que GLib 2.0 se enlaza a través de -lglib-2.0
, la Biblioteca Científica de GNU se enlaza a través de -lgsl
, y así sucesivamente.
Así, si el archivo se llamara erf.c, la línea de comandos completa utilizando el compilador gcc
, incluyendo varias banderas adicionales que se discutirán en breve, tendría este aspecto:
gcc erf.c -o erf -lm -g -Wall -O3 -std=gnu11
Así que le hemos dicho al compilador que incluya funciones matemáticas mediante un #include
en el programa, y le hemos dicho al enlazador que enlace con la biblioteca matemática mediante el -lm
en la línea de comandos.
La bandera -o
da el nombre de salida; de lo contrario, obtendríamos el nombre de ejecutable por defecto de a.out.
Algunas de mis banderas favoritas
Verás que siempre utilizo algunas banderas del compilador, y te recomiendo que tú también lo hagas.
-
-g
añade símbolos para la depuración. Sin ella, tu depurador no podrá darte nombres de variables o funciones. No ralentizan el programa, y no nos importa si el programa es un kilobyte más grande, así que hay pocas razones para no utilizarlo. Funciona paragcc
,clang
, yicc
(Compilador Intel C). -
-std=gnu11
es específico declang
- ygcc
-, y especifica que el compilador debe permitir código conforme a los estándares C11 y POSIX (además de algunas extensiones GNU). A partir de este momento,clang
utilizará por defecto el estándar C11, ygcc
el estándar C89. Si tu copia degcc
,clang
, oicc
es anterior a C11, utiliza-std=gnu99
para que utilice C99. El estándar POSIX especifica quec99
esté presente en tu sistema, por lo que la versión agnóstica del compilador de la línea anterior para compilar código C99 sería:c99 erf.c -o erf -lm -g -Wall -O3
En los siguientes makefiles, consigo este efecto estableciendo la variable
CC=c99
.Advertencia
Dependiendo de la antigüedad de tu Mac,
c99
puede ser una versión especialmente pirateada degcc
, que probablemente no es lo que quieres. Si tienes una versión dec99
que se detiene con la bandera-Wall
, o si falta por completo, crea la tuya propia. Pon un script bash llamadoc99
en el directorio de la cabecera de tu ruta con el textogcc --std=gnu99 $*
o
clang $*
como prefieras. Hazlo ejecutable a través de
chmod +x c99
. -
-O3
indica el nivel de optimización tres, que intenta todos los trucos para construir un código más rápido. Si, al ejecutar el depurador, descubres que se han optimizado demasiadas variables para que puedas seguir lo que ocurre, cámbialo a-O0
. Esto será un ajuste habitual en la variableCFLAGS
, más adelante. Esto funciona paragcc
,clang
, yicc
. -
-Wall
añade advertencias del compilador. Esto funciona paragcc
,clang
, yicc
. Paraicc
, puedes preferir-w1
, que muestra las advertencias del compilador, pero no sus observaciones.
Advertencia
Utiliza siempre las advertencias de tu compilador. Puede que seas meticuloso y conozcas el estándar C al dedillo, pero no eres más meticuloso ni tienes más conocimientos que tu compilador. Los antiguos libros de texto de C estaban llenos de páginas en las que se te advertía que tuvieras cuidado con la diferencia entre =
y ==
, o que comprobaras que todas las variables estuvieran inicializadas antes de usarlas. Como autor de libros de texto más modernos, lo tengo fácil, porque puedo resumir todas esas amonestaciones en un solo consejo: utiliza las advertencias de tu compilador, siempre.
Si tu compilador te aconseja un cambio, no le des vueltas ni pospongas la corrección. Haz todo lo necesario para (1) entender por qué recibiste una advertencia y (2) arreglar tu código para que se compile sin advertencias ni errores. Los mensajes del compilador son famosamente obtusos, así que si tienes problemas con el paso (1), pega el mensaje de advertencia en tu motor de búsqueda de Internet para ver cuántos miles de personas se confundieron con esta advertencia antes que tú. Tal vez quieras añadir -Werror
a las banderas de tu compilador para que éste trate las advertencias como errores.
Caminos
Tengo más de 700.000 archivos en mi disco duro, y uno de ellos tiene las declaraciones de sqrt
y erf
, y otro es el archivo objeto que contiene las funciones compiladas.3 El compilador tiene que saber en qué directorios buscar para encontrar la cabecera y el archivo objeto correctos, y el problema no hará más que complicarse cuando utilicemos bibliotecas que no forman parte del estándar C.
En una configuración típica, hay al menos tres lugares donde se pueden instalar las bibliotecas:
-
El proveedor del sistema operativo puede definir uno o dos directorios estándar en los que instale las bibliotecas.
-
Puede haber un directorio para que el administrador del sistema local instale paquetes que no deberían sobrescribirse en la próxima actualización del sistema operativo por parte del proveedor. Por ejemplo, el administrador del sistema podría tener una versión especialmente pirateada de una biblioteca que debería anular la versión por defecto.
-
Normalmente, los usuarios no tienen derechos de escritura en estas ubicaciones, por lo que deberían poder utilizar las bibliotecas de sus directorios personales.
La ubicación estándar del sistema operativo no suele causar problemas, y el compilador debería saber que debe buscar en esos lugares para encontrar la biblioteca estándar de C, así como todo lo que se instale junto a ella. El estándar POSIX se refiere a estos directorios como "los lugares habituales".
Pero para las demás cosas, tienes que decirle al compilador dónde buscar. Esto se va a poner bizantino: no hay una forma estándar de encontrar bibliotecas en ubicaciones no estándar, y ocupa un lugar destacado en la lista de cosas que frustran a la gente sobre C. Por el lado positivo, tu compilador sabe cómo buscar en las ubicaciones habituales, y los distribuidores de bibliotecas tienden a poner las cosas en las ubicaciones habituales, por lo que puede que nunca necesites especificar una ruta manualmente. Otra ventaja es que existen algunas herramientas que te ayudan a especificar las rutas. Y por último, una vez que hayas localizado las ubicaciones no estándar en tu sistema, puedes establecerlas en una variable del shell o makefile y no volver a pensar en ellas.
Supongamos que tienes una biblioteca llamada Libuseful instalada en tu ordenador, y sabes que sus distintos archivos se colocaron en el directorio /usr/local/, que es la ubicación destinada oficialmente a las bibliotecas locales de tu administrador del sistema. Ya has puesto #include <useful.h>
en tu código; ahora tienes que ponerlo en la línea de comandos:
gcc -I/usr/local/include use_useful.c -o use_useful -L/usr/local/lib -luseful
-
-I
añade la ruta dada a la ruta de búsqueda de inclusión, en la que el compilador busca los archivos de cabecera que#include
d en tu código. -
-L
añade a la ruta de búsqueda de la biblioteca. -
El orden importa. Si tienes un archivo llamado specific. o que depende de la biblioteca Libbroad, y Libbroad depende de Libgeneral, entonces necesitarás:
gcc specific.o -lbroad -lgeneral
Cualquier otro orden, como
gcc -lbroad -lgeneral specific.o
, probablemente fallará. Puedes pensar que el enlazador mira el primer elemento,specific.o
, y anota una lista de nombres de funciones, estructuras y variables sin resolver. A continuación, pasa al siguiente elemento,-lbroad
, y busca los elementos de la lista que aún faltan, mientras añade potencialmente nuevos elementos no resueltos, y luego comprueba en-lgeneral
los elementos que siguen en la lista de los que faltan. Si al final de la lista aún quedan nombres sin localizar (incluido el-lc
implícito del final), el enlazador se detiene y entrega al usuario lo que queda de su lista de elementos que faltan.
Bien, volvamos al problema de la ubicación: ¿dónde está la biblioteca a la que quieres enlazar? Si se instaló mediante el mismo gestor de paquetes que utilizaste para instalar el resto de tu sistema operativo, lo más probable es que esté en los lugares habituales y no tengas que preocuparte por ello.
Puede que tengas una idea de dónde suelen estar tus propias bibliotecas locales, como /usr/local o /sw u /opt. Sin duda tienes a mano un medio para buscar en el disco duro, como una herramienta de búsqueda en tu escritorio o el POSIX:
find /usr -name 'libuseful*'
para buscar en /usr archivos cuyos nombres empiecen por libuseful. Cuando encuentres que el archivo de objetos compartidos de Libuseful está en /alguna/ruta/lib, es casi seguro que las cabeceras estén en /alguna/ruta/include.
A todo el mundo también le resulta molesto buscar bibliotecas en el disco duro, y pkg-config
lo soluciona manteniendo un repositorio de las banderas y ubicaciones que los paquetes autodeclaran necesarias para la compilación. Escribe pkg-config
en tu línea de comandos; si aparece un error sobre la especificación de nombres de paquetes, entonces estupendo, tienes pkg-config
y puedes utilizarlo para que investigue por ti. Por ejemplo, en mi PC, escribiendo estos dos comandos en la línea de comandos:
pkg-config --libs gsl libxml-2.0 pkg-config --cflags gsl libxml-2.0
me da estas dos líneas de salida:
-lgsl -lgslcblas -lm -lxml2 -I/usr/include/libxml2
Éstas son exactamente las banderas que necesito para compilar utilizando GSL y LibXML2. Las banderas -l
revelan que GNU Scientific Library depende de una biblioteca de Subprogramas Básicos de Álgebra Lineal (BLAS), y la biblioteca BLAS de GSL depende de la biblioteca matemática estándar. Parece que todas las bibliotecas están en los lugares habituales, porque no hay banderas -L
, pero la bandera -I
indica la ubicación de los archivos de cabecera de LibXML2.
Volviendo a la línea de comandos, cuando rodeas un comando con puntos suspensivos, el intérprete de comandos sustituye el comando por su salida. Es decir, cuando escribo
gcc `pkg-config --cflags --libs gsl libxml-2.0` -o specific specific.c
que ve el compilador:
gcc -I/usr/include/libxml2 -lgsl -lgslcblas -lm -lxml2 -o specific specific.c
Así que pkg-config
hace gran parte del trabajo por nosotros, pero no es lo suficientemente estándar como para que podamos esperar que todo el mundo lo tenga o que todas las bibliotecas estén registradas con él. Si no tienes pkg-config
, tendrás que hacer este tipo de investigación tú mismo, leyendo el manual de tu biblioteca o buscando en tu disco, como vimos anteriormente.
Advertencia
A menudo existen variables de entorno para las rutas, como CPATH
o LIBRARY_PATH
o C_INCLUDE_PATH
. Deberías establecerlas en tu .bashrc
u otra lista de variables de entorno específica del usuario. No son estándar:gcc
en Linux y gcc
en Mac utilizan variables diferentes, y cualquier otro compilador puede utilizar otras. Creo que es más fácil establecer estas rutas por proyecto en el archivo makefile o su equivalente, utilizando las banderas -I
y -L
. Si prefieres estas variables de ruta, consulta al final de la página de manual de tu compilador la lista de variables relevantes para tu situación.
Incluso con pkg-config
, la necesidad de algo que ensamble todo esto por nosotros es cada vez más evidente. Cada elemento es bastante fácil de entender, pero es una lista larga y mecánica de piezas tediosas.
Enlace en tiempo de ejecución
Las bibliotecas estáticas son enlazadas por el compilador copiando efectivamente el contenido de la biblioteca en el ejecutable final. De modo que el propio programa funciona como un sistema más o menos independiente. Las bibliotecas compartidas se enlazan a tu programa en tiempo de ejecución, lo que significa que volvemos a tener el mismo problema de encontrar la biblioteca que teníamos en tiempo de compilación en tiempo de ejecución. Y lo que es peor, los usuarios de tu programa pueden tener este problema.
Si la biblioteca está en una de las ubicaciones habituales, todo va bien y el sistema no tendrá problemas para encontrar la biblioteca en tiempo de ejecución. Si tu biblioteca está en una ruta no estándar, tendrás que encontrar la forma de modificar la ruta de búsqueda de bibliotecas en tiempo de ejecución. Opciones:
-
Si empaquetaste tu programa con Autotools, Libtool sabe cómo añadir las banderas adecuadas, y no tienes que preocuparte por ello.
-
Cuando compiles el programa con
gcc
,clang
, oicc
basándote en una biblioteca de libpath, añade:LDADD=-L
libpath
-Wl,-Rlibpath
al makefile posterior. La bandera
-L
indica al compilador dónde buscar bibliotecas para resolver los símbolos; la bandera-Wl
pasa sus banderas degcc
/clang
/icc
al enlazador, y el enlazador incorpora la-R
dada en la ruta de búsqueda en tiempo de ejecución de bibliotecas con las que enlazar. Por desgracia,pkg-config
a menudo no conoce las rutas en tiempo de ejecución, por lo que puede que tengas que introducir estas cosas manualmente. -
En tiempo de ejecución, el enlazador utilizará otra ruta más para encontrar bibliotecas que no estén en los lugares habituales y que no estén anotadas en un ejecutable a través de
-Wl,R...
. Esta ruta puede establecerse en el script de inicio de tu shell (.bashrc
,.zshrc
, o lo que resulte apropiado). Para asegurarte de que se busca libpath para las bibliotecas compartidas en tiempo de ejecución, utiliza:export LD_LIBRARY_PATH=
libpath
:$LD_LIBRARY_PATH #Linux, Cygwin export DYLD_LIBRARY_PATH=libpath
:$DYLD_LIBRARY_PATH #OS XHay quien advierte contra el uso excesivo de
LD_LIBRARY_PATH
(¿y si alguien pone una biblioteca impostora maliciosa en la ruta, sustituyendo así a la biblioteca real sin que tú lo sepas?), pero si todas tus bibliotecas están en un mismo lugar, no está de más añadir a la ruta un directorio bajo tu control ostensible.
Utilizar Makefiles
El makefile proporciona una solución a todos estos interminables ajustes. Básicamente es un conjunto organizado de variables y secuencias de comandos de shell de una línea. El programa estándar POSIX make
lee el makefile en busca de instrucciones y variables, y luego ensambla las largas y tediosas líneas de comandos por nosotros. Después de este segmento, habrá pocas razones para llamar directamente al compilador.
En "Makefiles vs. Shell Scripts", cubriré algunos detalles más sobre el makefile; aquí, voy a mostrarte el makefile más pequeño practicable que compilará un programa básico que dependa de una biblioteca. Aquí lo tienes, las seis líneas:
P=program_name
OBJECTS=
CFLAGS = -g -Wall -O3
LDLIBS=
CC=c99
$(P): $(OBJECTS)
Utilización:
-
Una vez cada vez: Guárdalo (con el nombre makefile) en el mismo directorio que tus archivos .c. Si utilizas GNU Make, tienes la opción de poner en mayúsculas el nombre a Makefile si crees que hacerlo le ayudará a destacar entre los demás archivos. Pon el nombre de tu programa en la primera línea (utiliza
progname
y noprogname
.c). -
Cada vez que necesites recompilar: Escribe
make
.
Variables de ajuste
Pronto llegaremos al funcionamiento real del makefile, pero cinco de las seis líneas de este makefile tratan sobre la configuración de variables (dos de las cuales están actualmente en blanco), lo que indica que deberíamos tomarnos un momento para considerar las variables de entorno con un poco más de detalle.
Advertencia
Históricamente, ha habido dos hilos principales de la gramática del shell: uno basado principalmente en el shell Bourne, y otro basado principalmente en el shell C. El intérprete de comandos C tiene una sintaxis ligeramente diferente para las variables, por ejemplo, set CFLAGS="-g -Wall -O3”
para establecer el valor de CFLAGS
. Pero el estándar POSIX está escrito en torno a la sintaxis de establecimiento de variables de tipo Bourne, por lo que es en ella en la que me centraré durante el resto de este libro.
El shell y make
utilizan $
para indicar el valor de una variable, pero el shell utiliza $
var
, mientras que make
quiere cualquier nombre de variable de más de un carácter entre paréntesis: $(
var
)
. Así que, dado el makefile anterior, $(P): $(OBJECTS)
se evaluará para significar
program_name
:
Hay varias formas de informar a make
sobre una variable:
-
Establece la variable desde el shell antes de llamar a
make
, y exporta la variable, lo que significa que cuando el shell genere un proceso hijo, tendrá la variable en su lista de variables de entorno. Para establecerCFLAGS
desde una línea de comandos POSIX estándar:export CFLAGS='-g -Wall -O3'
En casa, omito la primera línea de este makefile,
P=
program_name
y en su lugar la establezco una vez por sesión medianteexport P=
program_name
lo que significa que tengo que editar el propio makefile con menos frecuencia. -
Puedes poner estos comandos de exportación en el script de inicio de tu shell, como
.bashrc
o.zshrc
. Esto garantiza que cada vez que te conectes o inicies un nuevo shell, la variable se establecerá y exportará. Si estás seguro de que tuCFLAGS
será siempre el mismo, puedes configurarlo aquí y no volver a pensar en ello. -
Puedes exportar una variable para un único comando colocando la asignación justo antes del comando. El comando
env
enumera las variables de entorno que conoce, de modo que cuando ejecutes lo siguiente:PANTS=kakhi env | grep PANTS
deberías ver la variable correspondiente y su valor. Por eso el shell no te deja poner espacios alrededor del signo igual: el espacio es la forma que tiene de distinguir entre la asignación y la orden.
Utilizando esta forma se establecen y exportan las variables dadas para una sola línea. Después de probar esto en la línea de comandos, intenta ejecutar de nuevo
env | grep PANTS
para verificar quePANTS
ya no es una variable exportada.Puedes especificar tantas variables como quieras:
PANTS=kakhi PLANTS="ficus fern" env | grep 'P.*NTS'
Esta forma forma parte de la descripción de comandos simples de la especificación del shell, lo que significa que la asignación debe ir antes de un comando real. Esto será importante cuando lleguemos a las construcciones del shell que no son comandos. Escribir:
VAR=val if [ -e afile ] ; then ./program_using_VAR ; fi
fallará con un oscuro error de sintaxis. La forma correcta es
if [ -e afile ] ; then VAR=val ./program_using_VAR ; fi
-
Como en el makefile anterior, puedes establecer la variable en la cabecera del makefile, con líneas como
CFLAGS=
. En el makefile, puedes tener espacios alrededor del signo igual sin que nada se rompa. -
make
te permitirá establecer variables en la línea de comandos, independientemente del shell. Por tanto, estas dos líneas son casi equivalentes:make CFLAGS="-g -Wall" Set a makefile variable. CFLAGS="-g -Wall" make Set an environment variable visible to make and its children.
Todos estos medios son equivalentes, por lo que respecta a tu makefile, con la excepción de que los programas hijos llamados por make
conocerán nuevas variables de entorno pero no conocerán ninguna variable del makefile.
make
también ofrece varias variables incorporadas. Éstas son las (estándar POSIX) que necesitarás para leer las siguientes reglas:
$@
-
El nombre completo del archivo de destino. Por objetivo me refiero al archivo que hay que construir, como un archivo .o que se compila a partir de un archivo .c o un programa hecho enlazando archivos .o.
$*
-
El archivo de destino con el sufijo cortado. Así, si el objetivo es prog.o,
$*
es prog, y$*.c
se convertiría en prog.c. $<
-
El nombre del archivo que ha provocado que se active y se haga este objetivo. Si estamos haciendo prog.o, probablemente sea porque prog.c se ha modificado recientemente, así que
$<
es prog.c.
Las normas
Ahora, centrémonos en los procedimientos que ejecutará el makefile, y luego veamos cómo influyen en ello las variables.
Dejando a un lado las variables, los segmentos del makefile tienen la forma:
target
:dependencies
script
Si se llama al objetivo, a través del comando make
target
entonces se comprueban las dependencias. Si el objetivo es un archivo, las dependencias son todos los archivos, y el objetivo es más reciente que las dependencias, entonces el archivo está actualizado y no hay nada que hacer. En caso contrario, el procesamiento del objetivo se pone en espera, se ejecutan o generan las dependencias, probablemente a través de otro objetivo, y cuando los scripts de las dependencias están todos terminados, se ejecuta el script del objetivo.
Por ejemplo, antes de que esto fuera un libro, era una serie de consejos publicados en un blog. Cada entrada del blog tenía una versión HTML y otra PDF, todas generadas mediante LaTeX. Estoy omitiendo muchos detalles en aras de un ejemplo sencillo (como las muchas opciones de latex2html
), pero éste es el tipo de makefile que se podría escribir para el proceso.
Advertencia
Si estás copiando cualquiera de estos fragmentos de makefile desde una versión en tu pantalla o en papel a un archivo llamado makefile, no olvides que el espacio en blanco de la cabecera de cada línea debe ser un tabulador, no espacios. Échale la culpa a POSIX.
all: html doc publish doc: pdflatex $(f).tex html: latex -interaction batchmode $(f) latex2html $(f).tex publish: scp $(f).pdf $(Blogserver)
Establezco f
en la línea de comandos mediante un comando como export f=tip-make
. Cuando a continuación escribo make
en la línea de comandos, se comprueba el primer objetivo, all
. Es decir, el comando make
por sí solo equivale a make
first_target
. Eso depende de html
, doc
, y publish
, por lo que esos objetivos son llamados en secuencia. Si sé que aún no está listo para copiar al mundo, entonces puedo llamar a make html doc
y hacer sólo esos pasos.
En el makefile sencillo de antes, sólo teníamos un grupo objetivo/dependencia/script. Por ejemplo
P=domath OBJECTS=addition.o subtraction.o $(P): $(OBJECTS)
Esto sigue una secuencia de dependencias y scripts similar a la que hacía mi makefile del blog, pero los scripts son implícitos. Aquí, P=domath
es el programa que hay que compilar, y depende de los archivos objeto adición .o y sustracción.o. Como adición.o no figura como objetivo, make
utiliza una regla implícita, que se indica a continuación, para compilar desde el archivo .c al .o. Luego hace lo mismo con sustracción . o y domath.o (porque GNU make
asume implícitamente que domath
depende de domath. o, dada la configuración aquí expuesta). Una vez construidos todos los objetos, no tenemos ningún script para construir el objetivo $(P)
, así que GNU make
rellena su script por defecto para enlazar archivos . o en un ejecutable.
El estándar POSIX make
tiene esta receta para compilar un archivo objeto .o a partir de un archivo de código fuente .c:
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c
La variable $(CC)
representa tu compilador de C; el estándar POSIX especifica por defecto CC=c99
, pero las ediciones actuales de GNU make
establecen CC=cc
, que suele ser un enlace a gcc
. En el makefile mínimo que encabeza este segmento, $(CC)
se establece explícitamente en c99
, $(CFLAGS)
se establece en la lista de banderas anterior, y $(LDFLAGS)
se desestablece y, por tanto, se sustituye por nada. Así que si make
determina que necesita producir your_program
.o, entonces éste es el comando que se ejecutará, dado ese makefile:
c99 -g -Wall -O3 -oyour_program
.oyour_program
.c
Cuando GNU make
decide que tienes un programa ejecutable para construir a partir de archivos objeto, utiliza esta receta:
$(CC) $(LDFLAGS)first.o
second.o
$(LDLIBS)
Recuerda que el orden importa en el enlazador, por lo que necesitamos dos variables de enlazador. En el ejemplo anterior, necesitábamos
cc specific.o -lbroad -lgeneral
como parte relevante del comando de enlace. Comparando el comando de compilación correcto con la receta, vemos que tenemos que poner LDLIBS=-lbroad -lgeneral
.
Nota
Si quieres ver la lista completa de reglas y variables por defecto incorporadas a tu edición de make
, prueba:
make -p > default_rules
Así que ése es el juego: encontrar las variables correctas y establecerlas en el makefile. Todavía tienes que investigar cuáles son las banderas correctas, pero al menos puedes escribirlas en el makefile y no volver a pensar en ellas.
Si utilizas un IDE, o CMAKE, o cualquiera de las otras alternativas al estándar POSIX make
, vas a jugar al mismo juego de encontrar las variables. Voy a seguir hablando del makefile mínimo anterior, y no deberías tener ningún problema para encontrar las variables correspondientes en tu IDE.
-
La variable
CFLAGS
es una costumbre arraigada, pero la variable que tendrás que establecer para el enlazador cambia de un sistema a otro. InclusoLDLIBS
no es estándar POSIX, pero es lo que utiliza GNUmake
. -
Las variables
CFLAGS
yLDLIBS
son donde vamos a enganchar todas las banderas del compilador que localizan e identifican bibliotecas. Si tienespkg-config
, pon aquí las llamadas de retroceso. Por ejemplo, el makefile de mi sistema, en el que utilizo Apophenia y GLib para casi todo, tiene el siguiente aspecto:CFLAGS=`pkg-config --cflags apophenia glib-2.0` -g -Wall -std=gnu11 -O3 LDLIBS=`pkg-config --libs apophenia glib-2.0`
O bien, especifica manualmente las banderas
-I
,-L
, y-l
, como:CFLAGS=-I
/home/b/root/include
-g -Wall -O3 LDLIBS=-L/home/b/root/lib
-lweirdlib
-
Después de que añadas una biblioteca y sus ubicaciones a las líneas
LDLIBS
yCFLAGS
y sepas que funciona en tu sistema, hay pocas razones para eliminarla nunca. ¿Realmente te importa que el ejecutable final pueda ser 10 kilobytes más grande que si personalizaras un nuevo makefile para cada programa? Eso significa que puedes escribir un makefile que resuma dónde están todas las bibliotecas de tu sistema y copiarlo de proyecto a proyecto sin necesidad de reescribirlo. -
Si tu programa requiere un segundo (o más) archivo C, añade
second
.o,third
.o, y así sucesivamente a la líneaOBJECTS
(sin comas, sólo espacios entre los nombres) en el archivo makefile que encabeza esta sección. -
Si tienes un programa que es un solo archivo .c, puede que no necesites un makefile en absoluto. En un directorio sin makefile y con el erf.c de antes, prueba a utilizar tu shell para
export CFLAGS='-g -Wall -O3 -std=gnu11' export LDLIBS='-lm' make erf
y observa cómo
make
utiliza sus conocimientos de compilación en C para hacer el resto.
Utilizar bibliotecas desde el origen
Hasta ahora, se ha tratado de compilar tu propio código utilizando make
. Compilar código proporcionado por otros suele ser una historia diferente.
Probemos un paquete de ejemplo. La Biblioteca Científica de GNU incluye un montón de rutinas de cálculo numérico.
La GSL se empaqueta mediante Autotools, un conjunto de herramientas que prepararán una biblioteca para su uso en cualquier máquina, comprobando todas las peculiaridades conocidas e implementando la solución adecuada. En "Empaquetar tu código con Autotools" se detallará cómo puedes empaquetar tus propios programas y bibliotecas con Autotools. Pero por ahora, podemos empezar como usuarios del sistema y disfrutar de la facilidad de instalar rápidamente bibliotecas útiles.
El GSL suele proporcionarse precompilado a través del gestor de paquetes, pero para que puedas seguir los pasos de la compilación, a continuación te explicamos cómo obtener el GSL como código fuente y configurarlo, suponiendo que tengas privilegios de root en tu ordenador.
wget ftp://ftp.gnu.org/gnu/gsl/gsl-1.16.tar.gz tar xvzf gsl-*gz cd gsl-1.16 ./configure make sudo make install
-
Descarga el archivo comprimido. Pide a tu gestor de paquetes que instale
wget
si no lo tienes, o escribe esta URL en tu navegador. -
Descomprime el archivo:
x
=extraer,v
=verbose,z
=descomprimir a través degzip
,f
=nombre de archivo. -
Determina las peculiaridades de tu máquina. Si el paso
configure
te da un error sobre un elemento que falta, utiliza tu gestor de paquetes para obtenerlo y vuelve a ejecutarconfigure
. -
Instálalo en la ubicación correcta, si tienes permisos.
Si estás probando esto en casa, probablemente tengas privilegios de root, y esto funcionará bien. Si estás en el trabajo y utilizas un servidor compartido, las probabilidades de que tengas derechos de superusuario son bajas, por lo que no podrás proporcionar la contraseña necesaria para realizar el último paso del script como superusuario. En ese caso, aguanta la respiración hasta la siguiente sección.
¿Se ha instalado? El Ejemplo 1-3 proporciona un breve programa para intentar encontrar ese intervalo de confianza del 95% utilizando las funciones GSL; pruébalo a ver si consigues enlazarlo y ponerlo en marcha:
Ejemplo 1-3. Rehacer el Ejemplo 1-1 con la GSL (gsl_erf.c)
#include <gsl/gsl_cdf.h>
#include <stdio.h>
int
main
(){
double
bottom_tail
=
gsl_cdf_gaussian_P
(
-
1.96
,
1
);
printf
(
"Area between [-1.96, 1.96]: %g
\n
"
,
1
-
2
*
bottom_tail
);
}
Para utilizar la biblioteca que acabas de instalar, tendrás que modificar el makefile de tu programa que utiliza la biblioteca para especificar las bibliotecas y sus ubicaciones.
Dependiendo de si tienes a mano pkg-config
, puedes hacer una de estas cosas:
LDLIBS=`pkg-config --libs gsl` #or LDLIBS=-lgsl -lgslcblas -lm
Si no se instaló en una ubicación estándar y pkg-config
no está disponible, tendrás que añadir rutas a las cabeceras de estas definiciones, como CFLAGS=-I/usr/local/include
y LDLIBS=-L/usr/local/lib -Wl,-R/usr/local/lib
.
Utilizar bibliotecas desde el código fuente (aunque el administrador del sistema no quiera)
Puede que no tengas acceso root si utilizas un ordenador compartido en el trabajo, o en casa si tienes una pareja especialmente controladora. Entonces tendrás que pasar a la clandestinidad y crear tu propio directorio raíz privado.
El primer paso es simplemente crear el directorio:
mkdir ~/root
Ya tengo un directorio ~/tech donde guardo toda mi logística técnica, manuales y fragmentos de código, así que he creado un directorio ~/tech/root. El nombre no importa, pero utilizaré ~/root
como directorio ficticio.
Nota
Tu intérprete de comandos sustituye la tilde por la ruta completa a tu directorio personal, lo que te ahorra mucho trabajo. El estándar POSIX sólo exige que el intérprete de comandos haga esto al principio de una palabra o justo después de dos puntos (lo que necesitarías para una variable de tipo ruta), pero la mayoría de los intérpretes de comandos expanden también las tildes a mitad de palabra. Otros programas, como make
, pueden o no reconocer la tilde como tu directorio personal. En estos casos, puedes utilizar la variable de entorno HOME exigida por POSIX, como en los ejemplos siguientes.
El segundo paso es añadir la parte correcta de tu nuevo sistema raíz a todas las rutas relevantes. Para los programas, es el PATH
en tu .bashrc (o equivalente):
PATH=~/root/bin
:$PATH
Colocando el subdirectorio bin de tu nuevo directorio antes del original PATH
, se buscará primero en él, y se encontrará primero tu copia de cualquier programa. Así, puedes sustituir en tu versión preferida cualquier programa que ya esté en los directorios compartidos estándar del sistema.
Para las bibliotecas que vayas a incorporar a tus programas en C, anota las nuevas rutas de búsqueda en el makefile anterior:
LDLIBS=-L$(HOME)/root/lib
(plus the other flags, like -lgsl -lm ...) CFLAGS=-I$(HOME)/root/include
(plus -g -Wall -O3 ...)
Ahora que tienes una raíz local, puedes utilizarla también para otros sistemas, como el de Java CLASSPATH
.
El último paso es instalar programas en tu nueva raíz. Si tienes el código fuente y utiliza Autotools, todo lo que tienes que hacer es añadir --prefix=
$HOME/root
en el lugar adecuado:
./configure --prefix=$HOME/root
&& make && make install
No necesitaste sudo
para hacer el paso de instalación, porque ahora todo está en territorio que tú controlas.
Como los programas y bibliotecas están en tu directorio personal y no tienen más permisos que los tuyos, tu administrador de sistemas no puede quejarse de que son una imposición para los demás. Si tu administrador se queja de todos modos, entonces, por triste que sea, puede que haya llegado el momento de separarse.
Compilar programas C mediante el documento Here
Llegados a este punto, habrás visto el patrón de compilación unas cuantas veces:
-
Establece una variable que exprese las banderas del compilador.
-
Establece una variable que exprese las banderas del enlazador, incluida una bandera
-l
para cada biblioteca que utilices. -
Utiliza
make
o las recetas de tu IDE para convertir las variables en comandos completos de compilación y enlace.
El resto de este capítulo hará todo esto por última vez, utilizando una configuración absolutamente mínima: sólo el intérprete de comandos. Si eres un aprendiz cinético que aprendió lenguajes de programación cortando y pegando fragmentos de código en el intérprete, podrás hacer lo mismo pegando código C en tu símbolo del sistema.
Incluir archivos de cabecera desde la línea de comandos
gcc
y clang
tienen una cómoda bandera para incluir cabeceras. Por ejemplo:
gcc -include stdio.h
equivale a poner
#include <stdio.h>
en la cabecera de tu archivo C; lo mismo para clang -include stdio.h
.
Añadiendo eso a nuestra invocación del compilador, por fin podemos escribir hola.c como la única línea de código que debería ser:
int
main
(){
printf
(
"Hello, world.
\n
"
);
}
que compila bien mediante:
gcc -include stdio.h hello.c -o hi --std=gnu99 -Wall -g -O3
o comandos del shell como
export CFLAGS='-g -Wall -include stdio.h' export CC=c99 make hello
Este consejo sobre -include
es específico del compilador e implica trasladar información del código a las instrucciones de compilación. Si crees que esto es de mala educación, pues sáltate este consejo.
La Cabecera Unificada
Permíteme que divague unos párrafos sobre el tema de los archivos de cabecera. Para ser útil, un archivo de cabecera debe incluir las tipodefiniciones, definiciones de macros y declaraciones de funciones de los tipos, macros y funciones que utiliza el archivo de código que incluye la cabecera. Además, no debe incluir tipodefiniciones, definiciones de macros y declaraciones de funciones que el archivo de código no vaya a utilizar.
Para cumplir realmente estas dos condiciones, tendrías que escribir una cabecera distinta para cada archivo de código, con exactamente las partes relevantes para el archivo de código actual. En realidad, nadie lo hace.
Hubo un tiempo en que los compiladores tardaban varios segundos o minutos en compilar incluso programas relativamente sencillos, por lo que reducir el trabajo que tiene que hacer el compilador tiene un beneficio perceptible para el ser humano. Mis copias actuales de stdio.h y stdlib. h tienen cada una unas 1.000 líneas (prueba wc -l /usr/include/stdlib.h
) y time.h otras 400, lo que significa que este programa de siete líneas:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int
main
(){
srand
(
time
(
NULL
));
// Initialize RNG seed.
printf
(
"%i
\n
"
,
rand
());
// Make one draw.
}
es en realidad un programa de ~2.400 líneas.
Tu compilador ya no cree que 2.400 líneas sean gran cosa, y esto se compila en menos de un segundo. Así que la tendencia ha sido ahorrar tiempo a los usuarios eligiendo cabeceras, incluyendo más elementos en una sola cabecera.
Más adelante verás ejemplos que utilizan GLib, con un #include <glib.h>
en la parte superior. Esa cabecera incluye 74 subcabeceras, que cubren todas las subsecciones de la biblioteca GLib. Se trata de un buen diseño de interfaz de usuario por parte del equipo de GLib, porque quienes no queremos perder el tiempo eligiendo las subsecciones adecuadas de la biblioteca podemos recorrer rápidamente el papeleo de cabeceras en una sola línea, y quienes desean un control detallado pueden elegir exactamente las cabeceras que necesitan. Estaría bien que la biblioteca estándar de C tuviera una cabecera rápida y fácil como ésta; no era la costumbre en los años 80, pero es fácil hacer una.
Si estás escribiendo una cabecera pública para otros usuarios, entonces, según la regla de que una cabecera no debe incluir elementos innecesarios, tu cabecera probablemente no debería tener una lectura de #include "allheads.h"
en todas las definiciones y declaraciones de la biblioteca estándar; de hecho, es plausible que tu cabecera pública no tenga ningún elemento de la biblioteca estándar en absoluto. Esto es generalmente cierto: tu biblioteca puede tener un segmento de código que utilice las listas enlazadas de GLib para funcionar, pero eso significa que necesitas #include <glib.h>
en ese archivo de código, no en la cabecera de la biblioteca pública.
Volviendo a la idea de configurar una compilación rápida en la línea de comandos, la cabecera unificada hace que escribir programas rápidos sea más rápido. Una vez que tienes una cabecera unificada, incluso una línea como #include <allheads.h>
es superflua si eres usuario de gcc
o clang
, porque en su lugar puedes añadir -include allheads.h
a tu CFLAGS
y no volver a pensar en qué cabeceras fuera de proyecto incluir.
Aquí Documentos
Aquí los documentos son una característica de los shells estándar POSIX que puedes utilizar para C, Python, Perl o cualquier otra cosa, y harán que este libro sea mucho más útil y divertido. Además, si quieres tener un script multilingüe, los documentos de aquí son una forma fácil de hacerlo. Haz un análisis sintáctico en Perl, haz las operaciones matemáticas en C, luego haz que Gnuplot produzca las imágenes bonitas, y tenlo todo en un archivo de texto.
He aquí un ejemplo de Python. Normalmente, le dirías a Python que ejecute un script mediante:
python your_script.py
Python te permite dar el nombre de archivo -
para utilizar stdin como archivo de entrada:
echo "print 'hi.'" | python -
En teoría, podrías poner algunos guiones largos en la línea de comandos a través de echo
, pero verás rápidamente que se producen un montón de pequeños análisis no deseados; por ejemplo, podrías necesitar \"hi\"
en lugar de "hi"
.
Así, el documento aquí, que no hace ningún análisis sintáctico. Prueba con esto:
python - <<"XXXX" lines=2 print "\nThis script is %i lines long.\n" %(lines,) XXXX
-
Aquí los documentos son una función estándar del shell, por lo que deberían funcionar en cualquier sistema POSIX.
-
El
"XXXX"
es cualquier cadena que desees;"EOF"
también es popular, y"-----"
queda bien siempre que consigas que el recuento de guiones coincida en la parte superior e inferior. Cuando el intérprete de comandos vea la cadena que has elegido sola en una línea, dejará de enviarla a la entrada estándar del programa. Eso es todo el análisis que ocurre. -
También hay una variante que empieza por
<<-
. Esta variante elimina todas las tabulaciones en la cabecera de cada línea, por lo que puedes colocar un documento here en una sección indentada de un script de shell sin romper el flujo de indentación. Por supuesto, esto sería desastroso para un documento here de Python. -
Como otra variante, existe una diferencia entre
<<"XXXX"
y<<XXXX
. En la segunda versión, el intérprete de comandos analiza ciertos elementos, lo que significa que puedes hacer que el intérprete de comandos inserte el valor de$
shell_variables
por ti. El shell se basa en gran medida en el$
para sus variables y otras expansiones; el$
es uno de los pocos caracteres de un teclado estándar que no tiene un significado especial para C. Es como si la gente que escribió Unix lo diseñara desde cero para que fuera fácil escribir scripts de shell que produjeran código C....
Compilar desde stdin
Bien, volvamos a C: podemos utilizar aquí documentos para compilar código C pegado en la línea de comandos a través de gcc
o clang
, o tener unas cuantas líneas de C en un script multilingüe.
No vamos a utilizar el makefile, así que necesitamos un único comando de compilación. Para hacernos la vida menos penosa, pongámosle un alias. Pega esto en tu línea de comandos, o añádelo a tu .bashrc, .zshrc, o donde corresponda:
go_libs="-lm"
go_flags="-g -Wall -include allheads.h
-O3"
alias go_c="c99 -xc - $go_libs $go_flags"
donde allheads.h
es la cabecera agregada que habías montado antes. Utilizar el indicador -include
significa una cosa menos en la que pensar al escribir el código C, y he descubierto que el historial de bash se vuelve confuso cuando hay #
s en el código C.
En la línea de compilación, reconocerás que -
significa que en lugar de leer de un archivo con nombre, utiliza stdin. El -xc
identifica esto como código C, porque gcc significa GNU Compiler Collection, no GNU C Compiler, y sin ningún nombre de archivo de entrada que termine en .c para avisar, tenemos que tener claro que esto no es Java, Fortran, Objective C, Ada o C++ (y lo mismo para clang
, aunque su nombre pretenda invocar el lenguaje C).
Lo que hayas hecho para personalizar LDLIBS
y CFLAGS
en tu makefile, hazlo aquí.
Ahora estamos navegando y podemos compilar código C en la línea de comandos:
go_c << '---' int main(){printf("Hello from the command line.\n");} --- ./a.out
Podemos utilizar un documento aquí para pegar programas C cortos en la línea de comandos, y escribir pequeños programas de prueba sin complicaciones. No sólo no necesitas un makefile, sino que ni siquiera necesitas un archivo de entrada.4
No esperes que este tipo de cosas sea tu modo principal de trabajo. Pero cortar y pegar fragmentos de código en la línea de comandos puede ser divertido, y poder tener un único paso en C dentro de un script de shell más largo es bastante fabuloso.
1 Cygwin es un proyecto dirigido por Red Hat, Inc. que también permitirá a los usuarios comprar el derecho a no distribuir su código fuente según la GPL.
2 Aunque MinGW tiene un gestor de paquetes que instala los elementos básicos del sistema y proporciona una serie de bibliotecas (sobre todo las necesarias para el propio MinGW), este puñado de bibliotecas precompiladas palidece en comparación con los cientos de paquetes que proporciona el gestor de paquetes típico. De hecho, el gestor de paquetes de mi máquina Linux tiene más bibliotecas compiladas de MinGW que el gestor de paquetes de MinGW. Esto es en el momento de escribir esto; para cuando leas esto, puede que usuarios como tú hayan contribuido con más paquetes al repositorio de MinGW.
3 Puedes probar find / -type f | wc -l
para obtener un recuento aproximado de archivos en cualquier sistema POSIX estándar.
4 Existe la costumbre POSIX de que si la primera línea de file
es #!
aninterpreter
entonces cuando ejecutes file
desde el intérprete de comandos, éste ejecutará aninterpreter
file
. Esto funciona bien para lenguajes interpretados como Perl o Python (sobre todo teniendo en cuenta que toman #
como marcador de comentario y, por tanto, ignoran la primera línea). Dadas las pistas de este segmento, podrías escribir un script (llamémoslo c99sh
) que hiciera lo correcto con un archivo C que empezara por #!c99sh
: arreglar la primera línea, enviar el resto del archivo por una tubería al compilador, y luego ejecutar el programa resultante. Sin embargo, Rhys Ulerich ya ha escrito un c99sh
de este tipo para ti, y ha publicado el script en GitHub.
Get C del siglo XXI, 2ª edición now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.