Articles

GCC y Make

GCC (GNU Compiler Collection)

Una breve historia e introducción a GCC

El compilador de C original de GNU (GCC) fue desarrollado por Richard Stallman, el fundador del Proyecto GNU. Richard Stallman fundó el proyecto GNU en 1984 para crear un sistema operativo completo tipo Unix como software libre, para promover la libertad y la cooperación entre los usuarios y programadores de ordenadores.

GCC, anteriormente para «GNU C Compiler», ha crecido con el tiempo para soportar muchos lenguajes como C (gcc), C++ (g++), Objective-C Objective-C++, Java (gcj), Fortran (gfortran), Ada (gnat), Go (gccgo), OpenMP, Cilk Plus y OpenAcc. Ahora se denomina «GNU Compiler Collection». El sitio madre de GCC es http://gcc.gnu.org/. La versión actual es GCC 7.3, publicada el 2018-01-25.

GCC es un componente clave de la llamada «GNU Toolchain», para desarrollar aplicaciones y escribir sistemas operativos. La GNU Toolchain incluye:

  1. GNU Compiler Collection (GCC): una suite de compiladores que soporta muchos lenguajes, como C/C++ y Objective-C/C++.
  2. GNU Make: una herramienta de automatización para compilar y construir aplicaciones.
  3. GNU Binutils: una suite de herramientas de utilidad binaria, incluyendo enlazador y ensamblador.
  4. GNU Debugger (GDB).
  5. GNU Autotools: Un sistema de construcción que incluye Autoconf, Autoheader, Automake y Libtool.
  6. GNU Bison: un generador de parser (similar a lex y yacc).
  7. GCC es portable y se ejecuta en muchas plataformas operativas. GCC (y GNU Toolchain) está actualmente disponible en todos los Unix. También están portados a Windows (por Cygwin, MinGW y MinGW-W64). GCC es también un compilador cruzado, para producir ejecutables en diferentes plataformas.

    Versiones de GCC

    Las distintas versiones de GCC son:

  • GCC versión 1 (1987): Versión inicial que soporta C.
  • GCC versión 2 (1992): soporta C++.
  • GCC versión 3 (2001): incorpora ECGS (Experimental GNU Compiler System), con mejora de la optimización.
  • Versión 4 de GCC (2005):
  • Versión 5 de GCC (2015):
  • Versión 6 de GCC (2016):
  • Versión 7 de GCC (2017):
Soporte de estándares C++

Existen varios estándares C++:

  • C++98
  • C++11 (aka C++0x)
  • C++14 (aka C++1y)
  • C++17 (aka C++1z)
  • C++2a (próximo estándar previsto en 2020)
  • El modo por defecto es C++98 para las versiones de GCC anteriores a la 6.1, y C++14 para GCC 6.1 y superiores. Puede utilizar la bandera de línea de comandos -std para especificar explícitamente el estándar C++. Por ejemplo,

    • -std=c++98, o -std=gnu++98 (C++98 con extensiones GNU)
    • -std=c++11, o -std=gnu++11 (C++11 con extensiones GNU)
    • -std=c++14, o -std=gnu++14 (C++14 con extensiones GNU), modo por defecto para GCC 6.1 y superiores.
    • -std=c++17, o -std=gnu++17 (C++17 con extensiones GNU), experimental.
    • -std=c++2a, o -std=gnu++2a (C++2a con extensiones GNU), experimental.
      • Instalación de GCC en Unixes

        La cadena de herramientas GNU, incluyendo GCC, está incluida en todos los Unixes. Es el compilador estándar para la mayoría de los sistemas operativos tipo Unix.

        Instalación de GCC en Mac OS X

        Abra un Terminal, e introduzca «gcc --version«. Si gcc no está instalado, el sistema le pedirá que instale gcc.

$ gcc --version......Target: x86_64-apple-darwin14.5.0 // 64-bit target codesThread model: posix

Instalar GCC en Windows

Para Windows, puede instalar Cygwin GCC, MinGW GCC o MinGW-W64 GCC. Lea «Cómo instalar Cygwin y MinGW».

  • Cygwin GCC: Cygwin es un entorno similar a Unix y una interfaz de línea de comandos para Microsoft Windows. Cygwin es enorme e incluye la mayoría de las herramientas y utilidades de Unix. También incluye el comúnmente utilizado shell Bash.
  • MinGW: MinGW (Minimalist GNU for Windows) es una adaptación de GNU Compiler Collection (GCC) y GNU Binutils para su uso en Windows. También incluye MSYS (Minimal System), que es básicamente un shell Bourne (bash).
  • MinGW-W64: un fork de MinGW que soporta tanto ventanas de 32 bits como de 64 bits.
    • Varios GCCs bajo Cygwin

      Hay muchos GCCs bajo Cygain/MinGW. Para diferenciar estas variaciones, es necesario entender lo siguiente:

      • Windows/Intel utiliza estos conjuntos de instrucciones: x86 es un conjunto de instrucciones de 32 bits; i868 es una versión mejorada de 32 bits de x86; x86_64 (o amd64) es un conjunto de instrucciones de 64 bits.
      • Los compiladores/programas de 32 bits pueden ejecutarse en Windows de 32 bits o de 64 bits (compatible con versiones anteriores), pero el compilador de 64 bits sólo puede ejecutarse en Windows de 64 bits.
      • Los compiladores de 64 bits pueden producir objetivos de 32 bits o de 64 bits.
      • Si se utiliza GCC de Cygwin, el objetivo puede ser Windows nativo o Cygwin. Si el objetivo es nativo de Windows, el código puede ser distribuido y ejecutado bajo Windows. Sin embargo, si el objetivo es Cygwin, para distribuirlo, necesita distribuir el entorno de ejecución de Cygwin (cygwin1.dll). Esto se debe a que Cygwin es un emulador de Unix bajo Windows.
      MinGW-W64 Target 32/64-bit Native Windows

      El MinGW-W64 (un fork de MinGW, disponible en http://mingw-w64.org/doku.php) soporta el target de Windows nativo de 32 y 64 bits. Puede instalar «MinGW-W64» en «Cygwin» seleccionando estos paquetes (en la categoría «devel»):

      • mingw64-x86_64-gcc-core: compilador de C de 64 bits para el objetivo de Windows nativo de 64 bits. El ejecutable es «x86_64-w64-mingw32-gcc«.
      • mingw64-x86_64-gcc-g++: compilador de C++ de 64 bits para objetivo de Windows nativo de 64 bits. El ejecutable es «x86_64-w64-mingw32-g++«.
      • mingw64-i686-gcc-core: compilador de C de 64 bits para objetivo de Windows nativo de 32 bits. El ejecutable es «i686-w64-mingw32-gcc«.
      • mingw64-i686-gcc-g++: compilador de C++ de 64 bits para objetivo de Windows nativo de 32 bits. El ejecutable es «i686-w64-mingw32-g++«.

      Notas:

      • Le sugiero que instale «mingw64-x86_64-gcc-core» y «mingw64-x86_64-gcc-g++» para proporcionar códigos nativos de Windows de 64 bits, pero omita «mingw64-i686-gcc-core» y «mingw64-i686-gcc-g++«, a menos que necesite producir aplicaciones Windows de 32 bits.
      • Para JNI (Java Native Interface) en Java de 64 bits, es necesario utilizar «x86_64-w64-mingw32-gcc» o «x86_64-w64-mingw32-g++» para producir código nativo de Windows de 64 bits.

      Ejecuta los ejecutables y comprueba las versiones:

// Target 64-bit native Windows$ x86_64-w64-mingw32-gcc --version
x86_64-w64-mingw32-gcc (GCC) 6.4.0$ x86_64-w64-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=x86_64-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-w64-mingw32/6.4.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: .....
Thread model: posix
gcc version 6.4.0 (GCC)$ x86_64-w64-mingw32-g++ --version
x86_64-w64-mingw32-g++ (GCC) 6.4.0// Target 32-bit native Windows$ i686-w64-mingw32-gcc --version
i686-w64-mingw32-gcc (GCC) 6.4.0
$ i686-w64-mingw32-g++ --version
i686-w64-mingw32-g++ (GCC) 6.4.0
Otros GCC en Cygwin

Otros paquetes GCC en Cygwin son:

  • gcc-core, gcc-g++: Compilador C/C++ básico de 64 bits dirigido a Cygwin de 64 bits. Probablemente deberías instalar también estos dos paquetes. Sin embargo, para distribuir el código producido, necesita distribuir Cygwin Runtime Environment (cygwin1.dll). Esto se debe a que Cygwin es un emulador de Unix bajo Windows.
  • cygwin32-gcc-core, cygwin32-gcc-g++: Compilador C/C++ de 32 bits más antiguo para el objetivo de Cygwin de 32 bits (Obsoleto por gcc-code y gcc-g++?).
  • mingw-gcc-core, mingw-gcc-g++: Compilador C/C++ de 32 bits de MinGW más antiguo para Windows de 32 bits (¿Obsoleto por los paquetes MinGW-W64?).

Post Instalación

Versiones

Podría mostrar la versión de GCC mediante la opción --version:

$ gcc --versiongcc (GCC) 6.4.0$ g++ --version g++ (GCC) 6.4.0

Se pueden obtener más detalles a través de la opción -v, por ejemplo,

$ gcc -vUsing built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/6.4.0/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: ......
Thread model: posix
gcc version 6.4.0 (GCC)$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/6.4.0/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: ......
Thread model: posix
gcc version 6.4.0 (GCC)
Ayuda

Puedes obtener el manual de ayuda a través de la opción --help. Por ejemplo,

$ gcc --help
Páginas de manual

Puede leer las páginas de manual de GCC (o páginas de manual) a través de la utilidad man:

$ man gcc// or$ man g++// Press space key for next page, or 'q' to quit.

Leer las páginas de manual bajo CMD o Bash shell puede ser difícil. Podrías generar un archivo de texto a través de:

$ man gcc | col -b > gcc.txt

La utilidad col es necesaria para eliminar el retroceso. (En el caso de Cygwin, está disponible en el paquete «Utils», «util-linux».)

Alternativamente, podrías buscar una página man online, por ejemplo, http://linux.die.net/man/1/gcc.

Las páginas man de GCC se guardan bajo «usr/share/man/man1«.

$ whereis gccgcc: /usr/bin/gcc.exe /usr/lib/gcc /usr/share/man/man1/gcc.1.gz

Cómo empezar

Los compiladores GNU C y C++ se llaman gcc y g++, respectivamente.

Compilar/vincular un programa C simple – hello.c

A continuación se muestra el programa en C Hola-mundo hello.c:

.

1234567
// hello.c#include <stdio.h> int main() { printf("Hello, world!\n"); return 0;}

Para compilar el hello.c:

> gcc hello.c // Compile and link source file hello.c into executable a.exe (Windows) or a (Unixes)

El ejecutable de salida por defecto se llama «a.exe» (Windows) o «a.out» (Unixes y Mac OS X).

Para ejecutar el programa:

// (Windows) In CMD shell> a// (Unixes / Mac OS X) In Bash Shell - include the current path (./)$ chmod a+x a.out$ ./a.out

Nota para Unixes y Bash Shell:

  • En Bash shell, el PATH por defecto no incluye el directorio de trabajo actual. Por lo tanto, es necesario incluir la ruta actual (./) en el comando. (Windows incluye el directorio actual en el PATH automáticamente; mientras que los Unix no lo hacen – es necesario incluir el directorio actual explícitamente en el PATH.)
  • También es necesario incluir la extensión del archivo, si la hay, es decir, «./a.out«.
  • En Unixes, el archivo de salida podría ser «a.out» o simplemente «a«. Además, hay que asignar el modo de archivo ejecutable (x) al archivo ejecutable «a.out«, mediante el comando «chmod a+x filename» (añadir el modo de archivo ejecutable «+x» a todos los usuarios «a+x«).

Para especificar el nombre del archivo de salida, utilice la opción -o:

// (Windows) In CMD shell> gcc -o hello.exe hello.c // Compile and link source file hello.c into executable hello.exe> hello // Execute hello.exe under CMD shell// (Unixes / Mac OS X) In Bash shell$ gcc -o hello hello.c$ chmod a+x hello$ ./hello

NOTA para Unixes:

  • En Unixes, normalmente omitimos la extensión de archivo .exe (pensada sólo para Windows), y simplemente nombramos el ejecutable de salida como hello (mediante el comando «gcc -o hello hello.c«.
  • Es necesario asignar el modo de archivo ejecutable a través del comando «chmod a+x hello«.
Compilar/enlazar un programa C++ simple – hello.cpp
12345678
// hello.cpp#include <iostream>using namespace std; int main() { cout << "Hello, world!" << endl; return 0;}

Es necesario utilizar g++ para compilar el programa C de la siguiente manera. Utilizamos la opción -o para especificar el nombre del archivo de salida.

// (Windows) In CMD shell> g++ -o hello.exe hello.cpp // Compile and link source hello.cpp into executable hello.exe> hello // Execute under CMD shell// (Unixes / Mac OS X) In Bash shell$ g++ -o hello hello.cpp$ chmod a+x hello$ ./hello

Más opciones del compilador GCC

Algunas opciones del compilador GCC de uso común son:

$ g++ -Wall -g -o Hello.exe Hello.cpp
  • -o: especifica el nombre del archivo ejecutable de salida.
  • -Wall: imprime «all» Mensajes de advertencia.
  • -g: genera información adicional de depuración simbólica para usar con el gdb depurador.
Compilar y enlazar por separado

El comando anterior compila el archivo fuente en archivo objeto y link con otros archivos objeto y bibliotecas del sistema en un solo paso. Puede separar la compilación y el enlace en dos pasos como sigue:

// Compile-only with -c option> g++ -c -Wall -g Hello.cpp// Link object file(s) into an executable> g++ -g -o Hello.exe Hello.o

Las opciones son:

  • -c: Compilar en el archivo de objetos «Hello.o«. Por defecto, el archivo objeto tiene el mismo nombre que el archivo fuente con extensión «.o» (no es necesario especificar la opción -o). No se enlaza con otros archivos de objetos o bibliotecas.
  • El enlace se realiza cuando el archivo de entrada son archivos de objetos «.o» (en lugar del archivo fuente «.cpp» o «.c«). GCC utiliza un programa enlazador separado (llamado ld.exe) para realizar el enlace.
Compilar y enlazar múltiples archivos fuente

Suponga que su programa tiene dos archivos fuente: file1.cppfile2.cpp. Podrías compilarlos todos en un solo comando:

> g++ -o myprog.exe file1.cpp file2.cpp 

Sin embargo, solemos compilar cada uno de los archivos fuente por separado en un archivo objeto, y enlazarlos en la etapa posterior. En este caso, los cambios en un archivo no requieren volver a compilar los otros archivos.

> g++ -c file1.cpp> g++ -c file2.cpp> g++ -o myprog.exe file1.o file2.o
Compilar en una biblioteca compartida

Para compilar y enlazar el programa C/C++ en una biblioteca compartida (".dll" en Windows, ".so" en Unixes), utilice la opción -shared. Lee «Interfaz nativa de Java» por ejemplo.

Proceso de compilación de GCC

GCC compila un programa C/C++ en un ejecutable en 4 pasos como se muestra en el diagrama anterior. Por ejemplo, un «gcc -o hello.exe hello.c» se realiza de la siguiente manera:

  1. Preprocesamiento: A través del preprocesador GNU C (cpp.exe), que incluye las cabeceras (#include) y expande las macros (#define
    > cpp hello.c > hello.i

    El archivo intermedio resultante «hello.i» contiene el código fuente expandido.

  2. Compilación: El compilador compila el código fuente preprocesado en código ensamblador para un procesador específico.
    > gcc -S hello.i

    La opción -S especifica producir código ensamblador, en lugar de código objeto. El archivo de ensamblaje resultante es «hello.s«.

  3. Asamblaje: El ensamblador (as.exe) convierte el código ensamblador en código máquina en el fichero objeto «hello.o
    > as -o hello.o hello.s
  4. Enlazador: Finalmente, el enlazador (ld.exe) enlaza el código objeto con el código de la biblioteca para producir un archivo ejecutable «hello.exe
    > ld -o hello.exe hello.o ...libraries...

Modo verbose (-v)

Puedes ver el proceso de compilación detallado activando la opción -v (verbose). Por ejemplo,

> gcc -v -o hello.exe hello.c
Definir macro (-D)

Puede utilizar la opción -Dname para definir una macro, o -Dname=value para definir una macro con un valor. El value debe ir entre comillas dobles si contiene espacios.

Cabeceras (.h), Bibliotecas estáticas (.lib, .a) y Bibliotecas compartidas (.dll, .so)

Biblioteca estática vs. Biblioteca compartida

Una biblioteca es una colección de archivos de objetos precompilados que pueden ser enlazados en sus programas a través del enlazador. Ejemplos son las funciones del sistema como printf() y sqrt().

Hay dos tipos de bibliotecas externas: biblioteca estática y biblioteca compartida.

  1. Una biblioteca estática tiene la extensión de archivo «.a» (archivo comprimido) en Unixes o «.lib» (biblioteca) en Windows. Cuando su programa se enlaza con una biblioteca estática, el código máquina de las funciones externas utilizadas en su programa se copia en el ejecutable. Una biblioteca estática puede crearse mediante el programa de archivo «ar.exe«.
  2. Una biblioteca compartida tiene la extensión de archivo «.so» (objetos compartidos) en Unixes o «.dll» (biblioteca de enlace dinámico) en Windows. Cuando su programa se enlaza con una biblioteca compartida, sólo se crea una pequeña tabla en el ejecutable. Antes de que el ejecutable comience a funcionar, el sistema operativo carga el código máquina necesario para las funciones externas, un proceso conocido como enlace dinámico. La vinculación dinámica hace que los archivos ejecutables sean más pequeños y ahorra espacio en el disco, porque una copia de una biblioteca puede ser compartida por varios programas. Además, la mayoría de los sistemas operativos permiten que una copia de una biblioteca compartida en memoria sea utilizada por todos los programas en ejecución, con lo que se ahorra memoria. Los códigos de la biblioteca compartida pueden actualizarse sin necesidad de recompilar su programa.
    1. Debido a la ventaja del enlazado dinámico, GCC, por defecto, enlaza con la biblioteca compartida si está disponible.

      Puede listar el contenido de una biblioteca a través de «nm filename«.

      Búsqueda de archivos de cabecera y bibliotecas (-I, -L y -l)

      Al compilar el programa, el compilador necesita los archivos de cabecera para compilar los códigos fuente; el enlazador necesita las bibliotecas para resolver las referencias externas de otros archivos objeto o bibliotecas. El compilador y el enlazador no encontrarán las cabeceras/bibliotecas a menos que configure las opciones apropiadas, lo cual no es obvio para el usuario de primera vez.

      Para cada una de las cabeceras utilizadas en su fuente (a través de las directivas #include), el compilador busca las llamadas rutas de inclusión para estas cabeceras. Las rutas de inclusión se especifican mediante la opción -Idir (o la variable de entorno CPATH). Dado que el nombre de archivo de la cabecera es conocido (por ejemplo, iostream.hstdio.h), el compilador sólo necesita los directorios.

      El enlazador busca en las llamadas rutas de biblioteca las bibliotecas necesarias para enlazar el programa en un ejecutable. La ruta de la biblioteca se especifica a través de la opción -Ldir (mayúsculas 'L' seguido de la ruta del directorio) (o variable de entorno LIBRARY_PATH). Además, también hay que especificar el nombre de la biblioteca. En Unix, la biblioteca libxxx.a se especifica mediante la opción -lxxx (letra minúscula 'l', sin el prefijo «lib» y la extensión ".a«). En Windows, proporcione el nombre completo como -lxxx.lib. El enlazador necesita conocer tanto los directorios como los nombres de las bibliotecas. Por lo tanto, es necesario especificar dos opciones.

      Rutas de inclusión, rutas de biblioteca y bibliotecas por defecto

      Trate de listar las rutas de inclusión por defecto en su sistema utilizadas por el «GNU C Preprocessor» a través de «cpp -v«:

      > cpp -v......#include "..." search starts here:#include <...> search starts here: /usr/lib/gcc/x86_64-pc-cygwin/6.4.0/include
      /usr/include
      /usr/lib/gcc/x86_64-pc-cygwin/6.4.0/../../../../lib/../include/w32api

      Prueba a ejecutar la compilación en modo verbose (-v) para estudiar las rutas de librerías (-L) y bibliotecas (-l) utilizadas en tu sistema:

      > gcc -v -o hello.exe hello.c......-L/usr/lib/gcc/x86_64-pc-cygwin/6.4.0-L/usr/x86_64-pc-cygwin/lib-L/usr/lib-L/lib-lgcc_s // libgcc_s.a-lgcc // libgcc.a-lcygwin // libcygwin.a-ladvapi32 // libadvapi32.a-lshell32 // libshell32.a-luser32 // libuser32.a-lkernel32 // libkernel32.a

      En Eclipse CDT: En Eclipse CDT, puede configurar las rutas de inclusión, las rutas de biblioteca y las bibliotecas haciendo clic con el botón derecho en el proyecto ⇒ Propiedades ⇒ C/C++ General ⇒ Rutas y símbolos ⇒ En las pestañas «Includes», «Rutas de biblioteca» y «Bibliotecas». Los ajustes son aplicables sólo al proyecto seleccionado.

      Variables de entorno GCC

      GCC utiliza las siguientes variables de entorno:

  • PATH: Para buscar los ejecutables y las bibliotecas compartidas en tiempo de ejecución (.dll.so).
  • CPATH: Para buscar los include-paths de las cabeceras. Se busca después de las rutas especificadas en las opciones de -I<dir>C_INCLUDE_PATH y CPLUS_INCLUDE_PATH se pueden utilizar para especificar las cabeceras de C y C++ si se indicó el lenguaje concreto en el preprocesamiento.
  • LIBRARY_PATH: Para buscar las rutas de las bibliotecas de enlace. Se busca después de las rutas especificadas en las opciones –L<dir>.

Utilidades para examinar los archivos compilados

Para todas las utilidades GNU, puede utilizar «command --help» para listar el menú de ayuda; o «man command» para mostrar las páginas man.

Utilidad «archivo» – Determina el tipo de archivo

La utilidad «file» puede utilizarse para mostrar el tipo de archivos objeto y archivos ejecutables. Por ejemplo,

$ gcc -c hello.c$ gcc -o hello.exe hello.o $ file hello.c
hello.c: C source, ASCII text, with CRLF line terminators$ file hello.ohello.o: data > file hello.exehello.exe: PE32 executable (console) x86-64, for MS Windows
Utilidad «nm» – Lista la tabla de símbolos de los archivos de objetos

La utilidad «nm» lista la tabla de símbolos de los archivos de objetos. Por ejemplo,

$ nm hello.o0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
U __main
0000000000000000 T main
U puts$ nm hello.exe | grep main
00000001004080cc I __imp___main
0000000100401120 T __main
00000001004010e0 T main
......

«nm» se utiliza habitualmente para comprobar si una función concreta está definida en un fichero de objetos. Un 'T' en la segunda columna indica una función que está definida, mientras que un 'U' indica una función que está indefinida y debe ser resuelta por el enlazador.

Utilidad «ldd» – Lista de bibliotecas de enlace dinámico

La utilidad «ldd» examina un ejecutable y muestra una lista de las bibliotecas compartidas que necesita. Por ejemplo,

> ldd hello.exentdll.dll => /cygdrive/c/WINDOWS/SYSTEM32/ntdll.dll (0x7ff9ba3c0000)
KERNEL32.DLL => /cygdrive/c/WINDOWS/System32/KERNEL32.DLL (0x7ff9b9880000)
KERNELBASE.dll => /cygdrive/c/WINDOWS/System32/KERNELBASE.dll (0x7ff9b6a60000)
SYSFER.DLL => /cygdrive/c/WINDOWS/System32/SYSFER.DLL (0x6ec90000)
ADVAPI32.dll => /cygdrive/c/WINDOWS/System32/ADVAPI32.dll (0x7ff9b79a0000)
msvcrt.dll => /cygdrive/c/WINDOWS/System32/msvcrt.dll (0x7ff9b9100000)
sechost.dll => /cygdrive/c/WINDOWS/System32/sechost.dll (0x7ff9b9000000)
RPCRT4.dll => /cygdrive/c/WINDOWS/System32/RPCRT4.dll (0x7ff9b9700000)
cygwin1.dll => /usr/bin/cygwin1.dll (0x180040000)

GNU Make

La utilidad «make» automatiza los aspectos mundanos de la construcción de ejecutables desde el código fuente. «make» utiliza un llamado makefile, que contiene reglas sobre cómo construir los ejecutables.

Puedes emitir «make --help» para listar las opciones de la línea de comandos; o «man make» para mostrar las páginas man.

Primer Makefile por ejemplo

Comencemos con un ejemplo sencillo para construir el programa Hola-Mundo (hello.c) en ejecutable (hello.exe) mediante la utilidad make.

1234567
// hello.c#include <stdio.h> int main() { printf("Hello, world!\n"); return 0;}

Crea el siguiente archivo llamado «makefile» (sin extensión de archivo), que contiene las reglas para construir el ejecutable, y guárdalo en el mismo directorio que el archivo fuente. Utiliza «tab» para sangrar el comando (NO espacios).

all: hello.exehello.exe: hello.o gcc -o hello.exe hello.ohello.o: hello.c gcc -c hello.c clean: rm hello.o hello.exe

Ejecuta la utilidad «make» como sigue:

> makegcc -c hello.cgcc -o hello.exe hello.o

Ejecutar make sin argumento inicia el objetivo «all» en el makefile. Un makefile consiste en un conjunto de reglas. Una regla consta de 3 partes: un objetivo, una lista de prerrequisitos y un comando, como sigue:

target: pre-req-1 pre-req-2 ...command

El objetivo y los prerrequisitos están separados por dos puntos (:). El comando debe ir precedido de un tabulador (NO de espacios).

Cuando se le pide a make que evalúe una regla, comienza buscando los archivos en los prerrequisitos. Si alguno de los prerrequisitos tiene una regla asociada, hace intentos de actualizar esos primero.

En el ejemplo anterior, la regla «all» tiene un prerrequisito «hello.exemake no encuentra el archivo «hello.exe«, por lo que busca una regla para crearlo. La regla «hello.exe» tiene un requisito previo «hello.o«. De nuevo, no existe, por lo que make busca una regla para crearlo. La regla «hello.o» tiene un requisito previo «hello.cmake comprueba que «hello.c» existe y es más nuevo que el objetivo (que no existe). Ejecuta el comando «gcc -c hello.c«. La regla «hello.exe» ejecuta entonces su comando «gcc -o hello.exe hello.o«. Finalmente, la regla «all» no hace nada.

Más importante, si el prerrequisito no es más nuevo que el objetivo, el comando no se ejecutará. En otras palabras, el comando se ejecutará sólo si el objetivo está desactualizado en comparación con su prerrequisito. Por ejemplo, si volvemos a ejecutar el comando make:

> makemake: Nothing to be done for `all'.

También se puede especificar el objetivo a realizar en el comando make. Por ejemplo, el objetivo «clean» elimina el «hello.o» y «hello.exe«. Entonces puedes ejecutar el make sin objetivo, que es el mismo que «make all«.

> make cleanrm hello.o hello.exe > makegcc -c hello.cgcc -o hello.exe hello.o

Prueba a modificar el «hello.c» y ejecuta make.

NOTA:

  • Si el comando no va precedido de un tabulador, se obtiene un mensaje de error «makefile:4: *** falta el separador. Stop.»
  • Si no hay ningún makefile en el directorio actual, se obtiene un mensaje de error «make: *** No se han especificado objetivos y no se ha encontrado ningún makefile. Stop.»
  • El makefile puede llamarse «makefileMakefile» o «GNUMakefile«, sin extensión de archivo.

Más sobre Makefile

Comentario & Continuación

Un comentario comienza con un # y dura hasta el final de la línea. Una línea larga puede romperse y continuarse en varias líneas mediante una barra invertida (\).

Sintaxis de las reglas

Una sintaxis general para las reglas es:

target1 : 

Las reglas suelen estar organizadas de forma que las más generales van primero. La regla general suele llamarse «all«, que es el objetivo por defecto de make.

Objetivos falsos (o artificiales)

Un objetivo que no representa un archivo se llama objetivo falso. Por ejemplo, el «clean» del ejemplo anterior, que no es más que una etiqueta para un comando. Si el objetivo es un archivo, se comprobará si está desactualizado. El objetivo falso siempre está desactualizado y su comando se ejecutará. Los objetivos falsos estándar son: allcleaninstall.

Variables

Una variable comienza con un $ y se encierra entre paréntesis (...) o llaves {...}. Las variables de un solo carácter no necesitan los paréntesis. Por ejemplo, $(CC)$(CC_FLAGS)$@$^.

Variables automáticas

Las variables automáticas son establecidas por make después de que una regla coincida. Incluyen:

  • $@: el nombre de archivo de destino.
  • $*: el nombre de archivo de destino sin la extensión de archivo.
  • $<: el primer nombre de archivo prerrequisito.
  • $^: los nombres de archivo de todos los prerrequisitos, separados por espacios, descarta los duplicados.
  • $+: similar a $^, pero incluye los duplicados.
  • $?: los nombres de todos los prerrequisitos que son más nuevos que el objetivo, separados por espacios.

Por ejemplo, podemos reescribir el makefile anterior como:

all: hello.exe # $@ matches the target; $< matches the first dependenthello.exe: hello.ogcc -o $@ $<hello.o: hello.cgcc -c $< clean:rm hello.o hello.exe
Ruta virtual – VPATH & vpath

Puedes utilizar VPATH (en mayúsculas) para especificar el directorio en el que buscar las dependencias y los archivos de destino. Por ejemplo,

# Search for dependencies and targets from "src" and "include" directories# The directories are separated by spaceVPATH = src include

También puedes usar vpath (minúsculas) para ser más preciso sobre el tipo de archivo y su directorio de búsqueda. Por ejemplo,

# Search for .c files in "src" directory; .h files in "include" directory# The pattern matching character '%' matches filename without the extensionvpath %.c srcvpath %.h include
Reglas de patrón

Una regla de patrón, que utiliza el carácter de coincidencia de patrón '%' como nombre de archivo, puede aplicarse para crear un objetivo, si no hay una regla explícita. Por ejemplo,

# Applicable for create .o object file.# '%' matches filename.# $< is the first pre-requisite# $(COMPILE.c) consists of compiler name and compiler options# $(OUTPUT_OPTIONS) could be -o $@%.o: %.c$(COMPILE.c) $(OUTPUT_OPTION) $< # Applicable for create executable (without extension) from object .o object file# $^ matches all the pre-requisites (no duplicates)%: %.o$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
Reglas de patrones implícitos

Make viene con un enorme conjunto de reglas de patrones implícitos. Puedes listar todas las reglas a través de la opción --print-data-base.

Un Makefile de ejemplo

Este makefile de ejemplo está extraído de la «C/C++ Development Guide -Makefile» de Eclipse.

# A sample Makefile# This Makefile demonstrates and explains # Make Macros, Macro Expansions,# Rules, Targets, Dependencies, Commands, Goals# Artificial Targets, Pattern Rule, Dependency Rule.# Comments start with a # and go to the end of the line.# Here is a simple Make Macro.LINK_TARGET = test_me.exe# Here is a Make Macro that uses the backslash to extend to multiple lines.OBJS = \ Test1.o \ Test2.o \ Main.o# Here is a Make Macro defined by two Macro Expansions.# A Macro Expansion may be treated as a textual replacement of the Make Macro.# Macro Expansions are introduced with $ and enclosed in (parentheses).REBUILDABLES = $(OBJS) $(LINK_TARGET)# Here is a simple Rule (used for "cleaning" your build environment).# It has a Target named "clean" (left of the colon ":" on the first line),# no Dependencies (right of the colon),# and two Commands (indented by tabs on the lines that follow).# The space before the colon is not required but added here for clarity.clean : rm -f $(REBUILDABLES) echo Clean done# There are two standard Targets your Makefile should probably have:# "all" and "clean", because they are often command-line Goals.# Also, these are both typically Artificial Targets, because they don't typically# correspond to real files named "all" or "clean". # The rule for "all" is used to incrementally build your system.# It does this by expressing a dependency on the results of that system,# which in turn have their own rules and dependencies.all : $(LINK_TARGET) echo All done# There is no required order to the list of rules as they appear in the Makefile.# Make will build its own dependency tree and only execute each rule only once# its dependencies' rules have been executed successfully.# Here is a Rule that uses some built-in Make Macros in its command:# $@ expands to the rule's target, in this case "test_me.exe".# $^ expands to the rule's dependencies, in this case the three files# main.o, test1.o, and test2.o.$(LINK_TARGET) : $(OBJS) g++ -g -o $@ $^# Here is a Pattern Rule, often used for compile-line.# It says how to create a file with a .o suffix, given a file with a .cpp suffix.# The rule's command uses some built-in Make Macros:# $@ for the pattern-matched target# $< for the pattern-matched dependency%.o : %.cpp g++ -g -o $@ -c $<# These are Dependency Rules, which are rules without any command.# Dependency Rules indicate that if any file to the right of the colon changes,# the target to the left of the colon should be considered out-of-date.# The commands for making an out-of-date target up-to-date may be found elsewhere# (in this case, by the Pattern Rule above).# Dependency Rules are often used to capture header file dependencies.Main.o : Main.h Test1.h Test2.hTest1.o : Test1.h Test2.hTest2.o : Test2.h# Alternatively to manually capturing dependencies, several automated# dependency generators exist. Here is one possibility (commented out)...# %.dep : %.cpp# g++ -M $(FLAGS) $< > $@# include $(OBJS:.o=.dep)

Breve resumen

He presentado las características básicas de make aquí para que puedas leer y entender los makefiles simples para construir aplicaciones C/C++. Make es en realidad bastante complejo, y puede ser considerado como un lenguaje de programación por sí mismo!!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *