tutorial_de_compilacion

Tutorial de compilación

Con anterioridad de la computación hogareña, los sistemas multiusuari@ constituyeron el principal medio para programar.

Por tal motivo, este tutorial te ilustrará con ejemplos básicos cómo se traduce el código fuente de un programa en lenguaje C de alto nivel a un programa ejecutable en este sistema compartido y otros compatibles.

Este tutorial no es uno de programación, utilizaremos código pre-hecho.

Si bien es posible 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 de aprender - están imbuidos de las particularidades del microprocesador en donde se ejecutan. Aprender los lenguajes de alto nivel permite escribir programas para múltiples computadoras, que sean -además - más simples de compartir.

El lenguaje C diseñado por Dennis Ritchie (uno de los creadores de Unix) es uno de tales lenguajes de alto nivel, y se revela especialmente útil para la programación portable. Podrás utilizarlo en Texto-plano no sólo para emprender proyectos avanzados, sino también para aprender los pasos fundamentales para opera con lenguajes de alto nivel: su compilación.

Programación

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

Consideremos el orden de 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

Como puede verse, los últimos 4 pasos maquinales de la programación con lenguajes de alto nivel son practicados por medio de un Compilador, un programa de computadora capaz de traducir el código fuente escrito en un lenguaje en otro lenguaje distinto de operación (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 compilar consiste en llamar a CC es esta manera:

cc codigo_fuente.c –o binario_ejecutable

De esta manera, CC oficiará de compilador automático, y procesará un fichero de código fuente llamado codigo_fuente.c para generar como resultado un fichero de destino ejecutable, llamado binario_ejecutable. Si todo va bien, CC será tan parco que no informará nada, simplemente compilará y creará el ejecutable sin más decir.

Incluso es opcional la opción -o que indica el fichero ejecutable de destino: si no la incluyes , CC almacenará el resultado de la compilación en un fichero ejecutable de nombre llamado genéricamente a.out.

Hola Tercer Mundo!

Si hay un programa canónico 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.

Para compilar este saludo universal es conveniente organizar un directorio de trabajo ~/src/holamundo/ para destinar el código fuente:

mkdir -p ~/src/holamundo ;
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

Si todo marcha sobre ruedas, el compilador CC elaborará un fichero ejecutable a.out.

Comprueba su funcionamiento correcto con:

./a.out

…y en la terminal debería aparecer el saludo:

Hola Tercer Mundo!

Si bien la compilación automática es sumamente simple y conveniente, ¡no nos muestra los pasos intermedios, y tampoco es tan divertida!

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

Compila manualmente ahora un clon del Tetris.

Crea un directorio de trabajo para el juego:

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 par de programador@s, y ocupa 9,6K. No olvides guardarlo y volver al shell.

Ahora compila siguiendo los 4 pasos maquinales de la compilación manual:

1. Preprocesar

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

cc microtetris.c -E -o microtetris.i

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

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.i ~/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 no son tan simples: suelen estar compuestos por más de un fichero de código fuente, por lo que habría que compilar varios de ellos de forma encadenada para generar un único binario ejecutable. Esto se puede hacer indicándole a CC varios ficheros de código fuente y un ejecutable destino, de esta manera:

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

También es muy corriente que los ficheros fuente de un mismo proyecto se encuentren desperdigados en distintos directorios, y que conforme el proyecto crezca, existan muchos ficheros de cabeceras (de extensión .h). Para evitar problemas a la hora de tratar con proyectos semejantes, es posible hacer uso de la opción de inclusión de CC -I, que incluye los ficheros necesarios.

En caso de tener todos los ficheros fuente de un proyecto 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

También puede darse el caso que algunos fuentes dispongan de opciones de compilación diferenciadas entre sí. En tal caso es muy útil generar separadamente los respectivos códigos objeto, y una vez que todos estén compilados, enlazarlos a todos para obtener el ejecutable binario final:

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

Muchos programas de cierta entidad suelen hacer uso de librerías de funciones (realmente son “bibliotecas”), que contienen funciones pre-programadas. Para poder reutilizar estas librerías estándares del sistema es necesario indicarlo a CC con la opción -l.

Por ejemplo, si nuestro fichero programa.c requiere que esté instalada la librería curses o ncurses en el sistema (la librería se llamará casi con seguridad libncurses), utilizarías:

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

Si la librería no es una librería estándar del sistema, sino una que pertenece únicamente a nuestro proyecto, podremos indicar la ruta empleando la opción -L y especificando su ruta:

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

Sabiendo todo esto, introduce y compila el código fuente de Arkurses. Se trata de un clon del juego Arkanoid que utiliza la funciones de las librerías de gráficas de terminal ncurses y panel. Aprovecharemos para indicar también el comando time, que nos indicará el tiempo de procesador requerido por cada tarea de cómputo.

Para afrontar las dificultades indicadas anteriormente, compílalo automáticamente de esta manera:

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

…o bien podrás realiza los 4 pasos de la compilación manual, específicandole a CC las librerías ncurses y panel necesarias. Podrás hacerlo con:

#preprocesa y muestra el código parseado
time cc arkurses.c -E -o arkurses.i  -march=native ; cat arkurses.i ;
 
#compila arkurses.c con sus librerías y muestra el ensamblador resultante:
time cc arkurses.c -lpanel -lncurses -Os -S -march=native ; cat arkurses.s ;
 
#ensambla
time cc arkurses.s -lpanel -lncurses -c -Os -march=native -o arkurses.o ;
 
#enlaza todo y crea el binario de salida
time 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 obtenido el fichero binario ejecutable destino, evalúalo con:

./arkurses

(Puedes salir del juego con la tecla q).

Luego elimina los sobrantes y deja el ejecutable, con

rm arkurses.c arkurses.i arkurses.s arkurses.o

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

  • tutorial_de_compilacion.txt
  • Última modificación: 2022/09/06 02:20
  • por peron