tutorial_de_compilacion

¡Esta es una revisión vieja del documento!


Tutorial de compilación

Antes de la computación hogareña, los sistemas multiusuairi@ constituyeron el principal medio para programar.

Si bien puedes crear programas utilizando lenguajes de bajo nivel como el ensamblador, normalmente querrás utilizar lenguajes de alto nivel.

La diferencia radica en que los lenguajes de alto nivel son portables, mientras que los de bajo nivel - además de ser más difíciles - están imbuidos de las particularidades del microprocesador en donde se ejecutan. Es por ello que aprender los lenguajes de alto nivel te permite escribir programas para múltiples computadoras, más simples de compartir.

El lenguaje C diseñado por Dennis Ritchie (uno de los creadores de Unix) es uno de tales lenguajes, es especialmente útil para la programación portable.

Este tutorial te enseñará lo más básico para comprender cómo se traduce el código de programa en lenguaje C de alto nivel a un programa ejecutable en este u otro sistema.

La programación es un arte. Involucra una serie de procesos mujer/máquina para resolver un problema determinado.

Consideremos los procesos de la programación:

Acción realizada: Genera: Opción de CC para detener en este paso:
Imaginar Percepción de un problema
Considerar un problema de programación código in mente (esto es Cerebral)
Edición del código fuente código fuente (para esto usarás un editor)
1. Preprocesado código fuente preprocesado -E
2. Compilación código ensamblador -S
3. Ensamblado código objeto -c
4. Enlazado binario ejecutable
Este tutorial no es uno de programación, utilizaremos código pre-hecho.

Como vemos, los últimos 4 pasos maquinales de la programación con lenguajes de alto nivel son llevados a cabo por un Compilador, un programa de computadora que traduce el código fuente escrito en un lenguaje (el código fuente) en otro lenguaje (el código destino).

En nuestro sistema compartido utilizaremos el conjunto de compiladores CC (puedes también utilizar GCC, en GNU/Linux). Este super-compilador por línea de comandos es capaz de afrontar en traducir tu código o cualquiera que te hayan compartido.

La manera más simple de llamar a CC es esta:

cc codigo_fuente.c –o binario_ejecutable

De esta manera, el CC compilará un fichero de código fuente llamado codigo_fuente.c y generará un fichero llamado binario_ejecutable. Si todo va bien, CC es tan parco que no te dice nada, simplemente compila y crea el ejecutable sin siquiera saludar.

Incluso la opción -o que indica el fichero ejecutable de destino es opcional: si no se indicas nada, cc almancenará el resultado de la compilación en el fichero ejecutable de nombre genérico llamado a.out.

Hola Tercer Mundo!

Si hay un programa obligatorio a la hora de empezar a programar en cualquier lenguaje de programación, ese es el mítico “Hola, Tercer Mundo!”.

La manía de utilizar un programa que dé salida en un terminal la cadena “Hola, Tercer Mundo!” para ejemplificar el funcionamiento del proceso de compilación en un determinado lenguaje se remonta –una vez más- a los orígenes del UNIX, el lenguaje C, con Kerningan, Ritchie, Thompson y compañía haciendo de las suyas.

Compilemos este saludo universal. Para organizarte, primero sírvete de crearte un directorio ~/src/holamundo/ para el código fuente de la siguiente manera:

mkdir ~/src/holamundo -p ;
cd ~/src/holamundo/

Ya en tu directorio ~/src/holamundo, usa tu editor favorito para crear un fichero llamado holamundo.c, e incorpórale el siguiente código fuente en lenguaje C:

// * * * Programa simple en C para mostrar "Hola Tercer Mundo!" * * *
#include <stdio.h>
 
int main()
{
    printf("Hola Tercer Mundo!");
 
    return 0;
}

Guarda los cambios y vuelve al Shell.

La manera más simple de resolver los cuatro pasos maquinales con el compilador automático CC (o GCC en GNU/Linux), es utilizar la siguiente sintaxis:

cc holamundo.c

¡Listo! El compilador CC creará un fichero ejecutable a.out. ¡listo el pollo y pelada la gallina!.

Córrelo con:

./a.out
Hola Tercer Mundo!

A pesar de que la compilación automática es sumamente simple y conveniente, ¡no es tan divertida! Además, el código en C que le aplicamos sólo saluda…

¡Aprenderemos, en cambio, los cuatro pasos de la compilación con un juego!

Compilaremos ahora un clon del Tetris para continuar aprendiendo!

Crea un directorio para trabajar, con:

mkdir -p ~/src/microtetris/ ; cd ~/src/microtetris/

…ya en este directorio, crea el fichero microtetris.c. El código fuente en lenguaje C requirió un gran trabajo de estudio a un@ programador@, pero pesa 9,6K. No olvides guardar el fruto de su trabajo, y volver al shell).

Ahora compila, pero siguiendo los 4 pasos maquinales de la compilación.:

1. Preprocesar

Primero preprocesa este código fuente microtetris.c, analizándolo con el párser de CC:

cc microtetris.c -E -o microtetris.p

Obtendrás así un fichero de código fuente preprocesado de microtetris.p (en este caso “se inflará” pensando unos 41K). Si deseas curiosear el código preprocesado (o “parseado”), utiliza cat microtetris.p.

2. Compilar

Una vez parseado, podrás compilar tu código fuente con:

cc microtetris.c -S

La compilación del código fuente traducirá el código fuente de alto nivel en un código fuente de bajo nivel específico para el procesador de la máquina, llamado código de lenguaje ensamblador (el fiuchero se llamara micreotetris.s).

Puedes revisar la compilación lenguaje ensamblador con cat microtetris.s. Verás un conjunto de mnemónicos que representan simbólicamente instrucciones básicas para el microprocesador. Dependiendo del tipo de arquitectura, este código puede resultar más inflado aún, en este caso de 48K.

Recuerda, puedes programar en ensamblador, pero como ves ¡es 5 veces más pesado (y difícil) de hacerlo!

3. Ensamblar

Ensambla el código en lenguaje ensamblador:

cc microtetris.s -c -o microtetris.o

Obtendrás ahora el código objeto microtetris.o, que corresponda a la arquitectura del sistema. Este se ha reducido (en este caso, hasta unos 17K).

El código objeto es el lenguaje interpretable directamente por una computadora o microcontrolador digital.

4. Enlazar

Para crear el ejecutable código máquina binario debes entrelazar este código objeto (incluyendo sus librerías, si las tuviese), y sumarle los encabezados. De todo ello se encarga el enlazador de CC:

cc microtetris.o -o microtetris.out

Lograrás así final el fichero ejecutable de salida, consistente en el binario de código máquina microtetris.out (ya de 15K, los que requerirá para cargarse en la memoria de texto-plano).

Si utilizas cat para revisar el código objeto microtetris.o o el código máquina microtetris.out, muy probablemente recibirás en tu terminal caracteres ininteligibles. No es basura, ¡sólo una máquina de esta arquitectura podrá ejecutarlo! (Que un humano comprenda el código máquina es un cliché típico de la Ciencia Ficción).

Ejecutar

Con todo cocinado, evalúa correr el fichero del código máquina de salida ejecutándolo en tu sistema:

./microtetris.out

¡Felicitaciones! El programa corrió con éxito.

Limpiar y ordenar

Opcionalmente puedes limpiar tu ambiente de programación.

Podrías querer omitir la extensión .out del binario final para simplificarla (no es común que se use .out para los ejecutables distribuidos):

mv microtetris.out microtetris

Elimina los ficheros intermedios del proceso de compilación:

rm ~/src/microtetris.p ~/src/microtetris.s ~/src/microtetris.o

Si lo deseas, también puedes eliminar el código fuente.

rm ~/src/microtetris.c

¡Ya has programado, compilado y probado!


La mayoría de los proyectos de software están formados por más de un fichero de código fuente, por lo que habrá que compilar varios de ellos de forma enlazada para generar un único binario ejecutable. Esto se puede hacer indicándole a CC varios ficheros de código fuente y un ejecutable destino:

cc menu.c backend.c programa.c –o juego

Sin embargo es bastante probable que todos los ficheros fuente de un mismo proyecto no se encuentren en el mismo directorio, y que conforme el proyecto crezca, existan muchos ficheros de cabeceras (de extensión .h) y se alojen en directorios diferentes. Para evitar problemas a la hora de tratar con proyectos semejantes, podemos hacer uso de la opción de inlcusión -I, e incluir los ficheros que nos sean necesarios.

Imaginemos que tenemos un proyecto en el que todos los ficheros fuente están dentro del directorio ~/src/ y todos los ficheros de cabeceras están en el directorio include. Podríamos compilar el proyecto de la siguiente manera:

cc ./src/*.c –I include –o juego

Cuando un proyecto involucra muchos ficheros es bastante normal que no todas sus partes tengan las mismas opciones de compilación. Por ello es muy útil generar separadamente los respectivos códigos objeto, y cuando ya estén todos generados, enlazarlos para obtener el ejecutable:

cc –c backend.c –o backend.o
cc –c programa.c –lgraficos –o programa.o
cc –c menu.c –lcurses –o menu.o
cc kackend.o programa.o menu.o –o juego

Librerías

Conforme un proyecto va ganando entidad se hace casi irremediable el uso de librerías (realmente son “bibliotecas”) de funciones, que permiten reutilizar código de manera cómoda y eficiente. Para utilizar librerías estándar en el sistema es necesario emplear la opción -l a la hora de llamar a CC.

cc –c programa.c –lcurses –o programa.o

La compilación de este fichero (programa.c) requiere que esté instalada la librería curses o ncurses en el sistema, por ejemplo (la librería se llamará casi con seguridad libncurses). Si la librería no es una librería estándar en el sistema, sino que pertenece únicamente a nuestro proyecto, podremos indicar la ruta empleando la opción -L:

cc –c programa.c –L./libs/librería-programa –o programa.o

Optimizaciones

CC permite optimizar el código generado. Existen 3 etapas de optimización de código objeto:

  • -O1 optimizaciones en bloques repetitivos, operaciones con coma flotante, reducción de saltos, optimización de manejo de parámetros en pila, etc.
  • -O2 A las optimizaciones de etapa -O1 suma las mejoras en el abastecimiento de instrucciones al procesador, optimizaciones con respecto al retardo ocasionado al obtener datos del “heap” o de la memoria, etc.
  • -O3 A las optimizaciones de etapa -O2 suma el desenrollado de bucles y otras prestaciones muy vinculadas con el tipo de procesador.

Sabiendo todo esto, introduce y compila el código fuente de Arkurses. Se trata de un clon del juego Arkanoid que utiliza la características de la librería de gráficas de terminal ncurses y panel.

Para afrontar las dificultades indicadas anteriormente, debes compilarlo de esta manera:

cc arkurses.c -lpanel -lncurses -o arkurses -Os -march=native

…o bien podrás realizar cada paso de la compilación, agregando las librerías ncurses y panel, introduciendo todo esto:

#preprocesa y muestra el código parseado
cc arkurses.c -E -o arkurses.p  -march=native ; cat arkurses.p ;
 
#compila arkurses.c con sus librerías y muestra el ensamblador resultante:
cc arkurses.c -lpanel -lncurses -Os -S -march=native ; cat arkurses.s ;
 
#ensambla
cc arkurses.s -lpanel -lncurses -c -Os -march=native -o arkurses.o ;
 
#enlaza todo y crea el binario de salida
cc arkurses.o -lpanel -lncurses -o arkurses.out -Os -march=native ;
 
#Renombra el fichero de salida de compilación
mv arkurses.out arkurses

Una vez compilado, debes ejecutarlo con:

./arkurses

(Puedes salir del juego con la tecla q).

Luego elimina los sobrantes y deja el ejecutable, con

rm arkurses.c arkurses.p arkurses.s arkurses.o

Preprocesar, base para compilar. Compilar, base para ensamblar. Ensamblar, base para enlazar. Y enlazar, base para Ejecutar.

  • tutorial_de_compilacion.1660875532.txt.gz
  • Última modificación: 2022/08/19 02:18
  • por peron