Articles

GCC e Make

GCC (GNU Compiler Collection)

Um Breve Historial e Introdução ao GCC

O Compilador GNU C original (GCC) é desenvolvido por Richard Stallman, o fundador do Projecto GNU. Richard Stallman fundou o Projecto GNU em 1984 para criar um sistema operativo completo do tipo Unix como software livre, para promover a liberdade e cooperação entre utilizadores de computadores e programadores.

GCC, anteriormente para “GNU C Compiler”, tem crescido ao longo dos tempos para suportar muitas línguas tais como C (gcc), C++ (g++), Objective-C, Objective-C++, Java (gcj), Fortran (gfortran), Ada (gnat), Go (gccgo), OpenMP, Cilk Plus, e OpenAcc. É agora referida como “Colecção de Compiladores GNU”. O sítio mãe para GCC é http://gcc.gnu.org/. A versão actual é GCC 7.3, lançada em 2018-01-25.

GCC é um componente chave do chamado “GNU Toolchain”, para o desenvolvimento de aplicações e escrita de sistemas operativos. O GNU Toolchain inclui:

  1. GNU Compiler Collection (GCC): um conjunto de compiladores que suporta muitas linguagens, tais como C/C++ e Objective-C/C+++.
  2. GNU Make: uma ferramenta de automatização para compilar e construir aplicações.
  3. li>GNU Binutils: um conjunto de ferramentas utilitárias binárias, incluindo linker e assembler.

  4. GNU Debugger (GDB).
  5. GNU Autotools: Um sistema de construção incluindo Autoconf, Autoheader, Automake e Libtool.
  6. li>GNU Bison: um gerador parser (semelhante a lex e yacc).

GCC é portátil e executado em muitas plataformas operacionais. GCC (e GNU Toolchain) está actualmente disponível em todos os Unixes. São também portados para Windows (por Cygwin, MinGW e MinGW-W64). GCC é também um compilador cruzado, para produzir executáveis em diferentes plataformas.

Versões GCC

As várias versões GCC são:

  • GCC versão 1 (1987): Versão inicial que suporta C.
  • GCC versão 2 (1992): suporta C++.
  • li>GCC versão 3 (2001): incorporando ECGS (Experimental GNU Compiler System), com melhor optimização.li>GCC versão 4 (2005):li>GCC versão 5 (2015):li>GCC versão 6 (2016):li>GCC versão 7 (2017):

C++ Suporte Padrão

Existem vários padrões C++:

  • C++98
  • C+++11 (também conhecido por C+++0x)
  • C++14 (também conhecido por C+++1y)
  • C++17 (também conhecido por C+++1z)
  • C+++2a (próximo padrão planeado em 2020)

O modo padrão é C++98 para versões GCC anteriores a 6.1, e C+++14 para GCC 6.1 e superiores. Pode usar a bandeira de linha de comando -std para especificar explicitamente o padrão C++. Por exemplo,

  • -std=c++98, ou -std=gnu++98 (C+++98 com extensões GNU)
  • -std=c++11, ou -std=gnu++11 (C++11 com extensões GNU)
  • -std=c++14, ou -std=gnu++14 (C+++14 com extensões GNU), modo por defeito para GCC 6.1 e acima.
  • -std=c++17, ou -std=gnu++17 (C++17 com extensões GNU), experimental.
  • -std=c++2a, ou (C++2a com extensões GNU), experimental.

Instalando GCC em Unixes

GNU Toolchain, incluindo GCC, está incluído em todos os Unixes. É o compilador padrão para a maioria dos sistemas operacionais tipo Unix.

Instalar GCC em Mac OS X

Abrir um Terminal, e introduzir “gcc --version“. Se gcc não estiver instalado, o sistema irá pedir-lhe para instalar gcc.

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

Instalar GCC no Windows

Para Windows, pode instalar Cygwin GCC, MinGW GCC ou MinGW-W64 GCC. Ler “Como instalar Cygwin e MinGW”.

  • Cygwin GCC: Cygwin é um ambiente do tipo Unix e interface de linha de comando para Microsoft Windows. O Cygwin é enorme e inclui a maioria das ferramentas e utilitários Unix. Também inclui o Bash shell.
  • MinGW: MinGW (Minimalist GNU for Windows) é uma porta da Colecção de Compiladores GNU (GCC) e GNU Binutils para uso em Windows. Também incluiu MSYS (Minimal System), que é basicamente uma shell Bourne (bash).
  • MinGW-W64: um garfo de MinGW que suporta tanto janelas de 32-bit como de 64-bit.
Vários GCC sob Cygwin

Existem muitos GCC sob Cygain/MinGW. Para diferenciar estas variações, é necessário compreender o seguinte:

  • Windows/Intel usa estes conjuntos de instruções: x86 é um conjunto de instruções de 32 bits; i868 é uma versão melhorada de 32 bits de x86; x86_64 (ou amd64) é um conjunto de instruções de 64 bits.
  • compiladores/programas de 32-bit podem correr em Windows de 32-bit ou 64-bit (compatível com versões anteriores), mas o compilador de 64-bit só pode correr em Windows.
  • compiladores de 64-bit podem produzir alvo de 32-bit ou 64-bit.
  • Se utilizar o GCC do Cygwin, o alvo pode ser o Windows nativo ou o Cygwin. Se o alvo for o Windows nativo, o código pode ser distribuído e executado sob Windows. No entanto, se o alvo for Cygwin, para distribuir, é necessário distribuir o Cygwin runtime environment (cygwin1.dll). Isto porque o Cygwin é um emulador Unix sob Windows.
MinGW-W64 Target 32/64-bit Native Windows

O MinGW-W64 (um garfo do MinGW, disponível em http://mingw-w64.org/doku.php) suporta tanto o target do Windows nativo de 32-bit como 64-bit. Pode instalar “MinGW-W64” em “Cygwin” seleccionando estes pacotes (na categoria “devel”):

  • mingw64-x86_64-gcc-core: compilador C de 64-bit para alvo de janelas nativas de 64-bit. O executável é “x86_64-w64-mingw32-gcc“.
  • mingw64-x86_64-gcc-g++: compilador C++ de 64 bits para alvo de janelas nativas de 64 bits. O executável é “x86_64-w64-mingw32-g++“.
  • mingw64-i686-gcc-core: compilador C de 64 bits para alvo de Windows nativo de 32 bits. O executável é “i686-w64-mingw32-gcc“.
  • mingw64-i686-gcc-g++: compilador C++ de 64 bits para alvo de janelas nativas de 32 bits. O executável é “i686-w64-mingw32-g++“.

Notas:

  • Sugiro que instale “mingw64-x86_64-gcc-core” e “mingw64-x86_64-gcc-g++” para fornecer códigos nativos do Windows de 64 bits, mas salte “mingw64-i686-gcc-core” e “mingw64-i686-gcc-g++“, a menos que necessite de produzir aplicações Windows de 32 bits.
  • Para JNI (Java Native Interface) em Java de 64 bits, é necessário usar “x86_64-w64-mingw32-gcc” ou “x86_64-w64-mingw32-g++” para produzir código nativo do Windows de 64 bits.

Executar os executáveis e verificar as versões:

// 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
Outros GCCs em Cygwin

Outros pacotes de GCC em Cygwin são:

  • gcc-core, gcc-g++: Compilador C/C++ básico de 64 bits alvo Cygwin de 64 bits. Provavelmente também deveria instalar estes dois pacotes. Contudo, para distribuir o código produzido, é necessário distribuir o Cygwin Runtime Environment (cygwin1.dll). Isto porque o Cygwin é um emulador Unix sob Windows.
  • cygwin32-gcc-core, cygwin32-gcc-g++: Compilador C/C+++ mais antigo para Cygwin alvo de 32 bits (Obsoleto por gcc-code e gcc-g++?).
  • mingw-gcc-core, mingw-gcc-g++: Compilador MinGW 32-bit C/C++ mais antigo para Windows 32-bit (Obsoleto pelos pacotes MinGW-W64?).

Pós Instalação

Versões

Poderia exibir a versão do GCC via --version opção:

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

Mais detalhes podem ser obtidos via -v opção, por exemplo,

$ 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)
Ajuda

p>Pode obter o manual de ajuda através da opção --help. Por exemplo,

$ gcc --help
Man Pages

P>Pode ler as páginas do manual GCC (ou man pages) através do man utilidade:

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

Leitura de páginas man pages sob CMD ou Bash shell pode ser difícil. Pode gerar um ficheiro de texto via:

$ man gcc | col -b > gcc.txt

O col utilitário é necessário para despojar o backspace. (Para Cygwin, está disponível em “Utils”, pacote “util-linux”.)

Alternativamente, poderia procurar uma página de homem online, por exemplo http://linux.die.net/man/1/gcc.

As páginas de homem GCC são mantidas em “usr/share/man/man1“.

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

Arrancar

Os compiladores GNU C e C++ são chamados gcc e g++, respectivamente.

Compilar/Ligar um Programa C Simples – olá.c

Below é o programa Hello-world C hello.c:

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

Para compilar o hello.c:

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

O executável de saída padrão é chamado “a.exe” (Windows) ou “a.out” (Unixes e Mac OS X).

Para executar o programa:

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

Notas para Unixes e Bash Shell:

  • Em Bash shell, o PATH padrão não inclui o directório de trabalho actual. Por conseguinte, é necessário incluir o caminho actual (./) no comando. (Windows inclui o directório actual no PATH automaticamente; enquanto Unixes não – é necessário incluir o directório actual explicitamente no PATH.)
  • É também necessário incluir a extensão do ficheiro, se existir, isto é “./a.out“.
  • Em Unixes, o ficheiro de saída poderia ser “a.out” ou simplesmente “a“. Além disso, é necessário atribuir o modo de ficheiro executável (x) ao ficheiro executável “a.out“, através do comando “chmod a+x filename” (adicionar o modo de ficheiro executável “+x” a todos os utilizadores “a+x“).

Para especificar o nome do ficheiro de saída, utilizar -o option:

// (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

NOTE for Unixes:

  • Em Unixes, omitimos normalmente o .exe extensão de ficheiro (destinado apenas para Windows), e simplesmente nomeamos o executável de saída como hello (via comando “gcc -o hello hello.c“.
  • É preciso atribuir o modo de ficheiro executável através do comando “chmod a+x hello“.
Compile/Link a Simple C++ Program – olá.cpp
12345678
// hello.cpp#include <iostream>using namespace std; int main() { cout << "Hello, world!" << endl; return 0;}

É preciso usar g++ para compilar o programa C+++, como se segue. Utilizamos a opção -o para especificar o nome do ficheiro de saída.

// (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
Mais opções de compilador GCC

Poucas opções de compilador GCC normalmente utilizadas são:

$ g++ -Wall -g -o Hello.exe Hello.cpp
  • -o: especifica o nome do ficheiro executável de saída.
  • -Wall: imprime “all“. Mensagens de aviso.
  • -g: gera informação de depuração simbólica adicional para utilização com gdb debugger.
Compilar e Ligar Separadamente

O comando acima compila o ficheiro fonte em ficheiro objecto e link com outros ficheiros objecto e bibliotecas do sistema em executável num só passo. Pode separar compilar e ligar em dois passos como se segue:

// 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

As opções são:

  • -c: Compilar em ficheiro objecto “Hello.o“. Por defeito, o ficheiro objecto tem o mesmo nome que o ficheiro fonte com extensão “.o“. (não há necessidade de especificar -o opção). Nenhuma ligação com outros ficheiros objecto ou bibliotecas.
  • Link é executada quando o ficheiro de entrada são ficheiros objecto “.o” (em vez do ficheiro fonte “.cpp” ou “.c“). GCC usa um programa de ligação separado (chamado ld.exe) para executar a ligação.
Compilar e Ligar Ficheiros de Fontes Múltiplas

Compor que o seu programa tem dois ficheiros de fontes: file1.cppfile2.cpp. Poderia compilá-los todos num único comando:

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

No entanto, normalmente compilamos cada um dos ficheiros-fonte separadamente em ficheiro-objecto, e ligamo-los em conjunto na fase posterior. Neste caso, as alterações de um ficheiro não requerem a recompilação dos outros ficheiros.

> g++ -c file1.cpp> g++ -c file2.cpp> g++ -o myprog.exe file1.o file2.o
Compilar numa Biblioteca Partilhada

Compilar e ligar o programa C/C++ numa biblioteca partilhada (".dll" no Windows, ".so" em Unixes), usar -shared opção. Ler “Java Native Interface” por exemplo.

GCC Compilation Process

GCC compila um programa C/C++ em executável em 4 passos, como mostrado no diagrama acima. Por exemplo, um “gcc -o hello.exe hello.c” é realizado da seguinte forma:

  1. Pré-processamento: através do Pré-processador GNU C (cpp.exe), que inclui os cabeçalhos (#include) e expande as macros (#define
    > cpp hello.c > hello.i

    O ficheiro intermédio resultante “hello.i” contém o código fonte expandido.

  2. Compilação: O compilador compila o código-fonte pré-processado em código de montagem para um processador específico.
    > gcc -S hello.i

    A opção -S especifica para produzir código de montagem, em vez de código objecto. O ficheiro de montagem resultante é “hello.s“.

  3. Assembly: O assembler (as.exe) converte o código de montagem em código de máquina no ficheiro objecto “hello.o
    > as -o hello.o hello.s
  4. Linker: Finalmente, o linker (ld.exe) liga o código objecto com o código da biblioteca para produzir um ficheiro executável “hello.exe
    > ld -o hello.exe hello.o ...libraries...

Verbose Mode (-v)

p>p>pode ver o processo de compilação detalhada ao activar a opção -v (verbose). Por exemplo,

> gcc -v -o hello.exe hello.c
Definindo Macro (-D)

P>Pode usar a opção -Dname para definir uma macro, ou -Dname=value para definir uma macro com um valor. O value deve ser incluído entre aspas duplas se contiver espaços.

Líderes (.h), Bibliotecas Estáticas (.lib, .a) e Biblioteca Partilhada (.dll, .so)

Biblioteca Estática vs. Biblioteca Partilhada

Uma biblioteca é uma colecção de ficheiros objecto pré-compilados que podem ser ligados aos seus programas através do linker. Exemplos são as funções do sistema tais como printf() e sqrt().

Existem dois tipos de bibliotecas externas: biblioteca estática e biblioteca partilhada.

  1. Uma biblioteca estática tem extensão de ficheiro “.a“. (ficheiro de arquivo) em Unixes ou “.lib” (biblioteca) em Windows. Quando o seu programa está ligado contra uma biblioteca estática, o código da máquina das funções externas utilizadas no seu programa é copiado para o executável. Uma biblioteca estática pode ser criada através do programa de arquivo “ar.exe“.
  2. Uma biblioteca partilhada tem extensão de ficheiro “.so“. (objectos partilhados) em Unixes ou “.dll” (biblioteca de links dinâmicos) em Windows. Quando o seu programa está ligado contra uma biblioteca partilhada, apenas uma pequena tabela é criada no executável. Antes do executável começar a funcionar, o sistema operativo carrega o código da máquina necessário para as funções externas – um processo conhecido como ligação dinâmica. A ligação dinâmica torna os ficheiros executáveis mais pequenos e poupa espaço em disco, porque uma cópia de uma biblioteca pode ser partilhada entre vários programas. Além disso, a maioria dos sistemas operativos permite que uma cópia de uma biblioteca partilhada em memória seja utilizada por todos os programas em execução, poupando, assim, memória. Os códigos da biblioteca partilhada podem ser actualizados sem a necessidade de recompilar o seu programa.

Devido à vantagem da ligação dinâmica, o GCC, por defeito, liga à biblioteca partilhada se esta estiver disponível.

Pode listar o conteúdo de uma biblioteca através de “nm filename“.

Procura de ficheiros de cabeçalho e bibliotecas (-I, -L e -l)

Ao compilar o programa, o compilador precisa dos ficheiros de cabeçalho para compilar os códigos-fonte; o ligador precisa das bibliotecas para resolver referências externas de outros ficheiros objecto ou bibliotecas. O compilador e o linker não encontrarão os cabeçalhos/libraries a menos que se definam as opções apropriadas, o que não é óbvio para o primeiro utilizador.

Para cada um dos cabeçalhos utilizados na sua fonte (via #include directivas), o compilador procura os chamados caminhos de inclusão para estes cabeçalhos. Os percursos de inclusão são especificados através de -Idir opção (ou variável de ambiente CPATH). Uma vez que o nome do ficheiro do cabeçalho é conhecido (por exemplo, iostream.hstdio.h), o compilador só precisa dos directórios.

O linker pesquisa os chamados percursos de biblioteca para bibliotecas necessárias para ligar o programa a um executável. O percurso da biblioteca é especificado através de -Ldir opção (maiúsculas 'L' seguido pelo caminho do directório) (ou variável de ambiente LIBRARY_PATH). Além disso, também tem de especificar o nome da biblioteca. Em Unixes, a biblioteca libxxx.a é especificada via -lxxx opção (letra minúscula 'l', sem o prefixo “lib” e ".a” extensão). No Windows, fornecer o nome completo tal como -lxxx.lib“. O linker precisa de conhecer tanto os directórios como os nomes das bibliotecas. Assim, duas opções precisam de ser especificadas.

Padrão Incluir percursos, Percursos da biblioteca e Bibliotecas

Lista de percursos de inclusão padrão no seu sistema utilizado pelo “GNU C Preprocessor” via “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

Try a executar a compilação em modo verboso (-v) para estudar os percursos da biblioteca (-L) e bibliotecas (-l) utilizados no seu 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

br>Eclipse CDT: No Eclipse CDT, pode definir os caminhos de inclusão, caminhos da biblioteca e bibliotecas clicando com o botão direito do rato no projecto ⇒ Propriedades ⇒ C/C++ Geral ⇒ Caminhos e Símbolos ⇒ Em separadores “Includes”, “Library Paths” e “Libraries”. As definições são aplicáveis apenas ao projecto seleccionado.

GCC Environment Variables

GCC utiliza as seguintes variáveis de ambiente:

  • PATH: Para pesquisar os executáveis e bibliotecas partilhadas de tempo de execução (.dll.so).
  • CPATH: Para pesquisar os percursos de inclusão de cabeçalhos. É pesquisado após os caminhos especificados em -I<dir> opções. C_INCLUDE_PATH e CPLUS_INCLUDE_PATH pode ser usado para especificar cabeçalhos C e C++ se a língua em particular foi indicada no pré-processamento.
  • LIBRARY_PATH: Para pesquisar percursos de biblioteca para bibliotecas de links. É pesquisado após os caminhos especificados em –L<dir> opções.

Utilitários para examinar os ficheiros compilados

Para todos os utilitários GNU, pode usar “command --help” para listar o menu de ajuda; ou “man command” para exibir as páginas man.

“file” Utilitário – Determinar tipo de ficheiro

O utilitário “file” pode ser usado para exibir o tipo de ficheiros objecto e ficheiros executáveis. Por exemplo,

$ 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
“nm” Utilitário – Lista Tabela de Símbolos de Ficheiros de Objectos

O utilitário “nm” lista a tabela de símbolos de ficheiros de objectos. Por exemplo,

$ 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” é normalmente utilizado para verificar se uma determinada função está definida num ficheiro objecto. Um 'T' na segunda coluna indica uma função que está definida, enquanto um 'U' indica uma função que não está definida e que deve ser resolvida pelo linker.

Utilitário “ldd” – Listar bibliotecas de ligação dinâmica

O utilitário “ldd” examina um executável e exibe uma lista das bibliotecas partilhadas de que necessita. Por exemplo,

> 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

O utilitário “make” automatiza os aspectos mundanos da construção do executável a partir do código fonte. “make” utiliza o chamado makefile, que contém regras sobre como construir os executáveis.

Pode emitir “make --help” para listar as opções de linha de comando; ou “man make” para exibir as páginas man.

Primeiro Makefile Por Exemplo

Div> Comecemos com um exemplo simples para construir o programa Hello-world (hello.c) em executável (hello.exe) via make utility.

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

Criar o seguinte ficheiro chamado “makefile” (sem qualquer extensão de ficheiro), que contém regras para construir o executável, e guardar no mesmo directório que o ficheiro fonte. Use “tab” para indentar o comando (NÃO espaços).

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

Executar o utilitário “make” como se segue:

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

Executar make sem argumentos inicia o alvo “all” no makefile“. Um makefile consiste num conjunto de regras. Uma regra consiste em 3 partes: um alvo, uma lista de pré-requisitos e um comando, como se segue:

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

O alvo e pré-requisitos são separados por dois pontos (:). O comando deve ser precedido por um separador (NÃO espaços).

Quando make é pedido para avaliar uma regra, esta começa por encontrar os ficheiros nos pré-requisitos. Se algum dos pré-requisitos tiver uma regra associada, faça tentativas para actualizar esses primeiro.

No exemplo acima, a regra “all” tem um pré-requisito “hello.exemake não consegue encontrar o ficheiro “hello.exe“, pelo que procura uma regra para a criar. A regra “hello.exe” tem um pré-requisito “hello.o“. Mais uma vez, não existe, portanto make” procura uma regra para a criar. A regra “hello.o” tem um pré-requisito “hello.cmake” verifica que “hello.c” existe e é mais recente do que o alvo (que não existe). Executa o comando “gcc -c hello.c“. A regra “hello.exe” corre depois o seu comando “gcc -o hello.exe hello.o“. Finalmente, a regra “all” não faz nada.

Mais importante, se o pré-requisito não for mais recente do que o alvo, o comando não será executado. Por outras palavras, o comando será executado apenas se o alvo estiver desactualizado em comparação com o seu pré-requisito. Por exemplo, se voltarmos a executar o comando make:

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

Também se pode especificar o alvo a ser feito no comando make. Por exemplo, o alvo “clean” remove o “hello.o” e “hello.exe“. Pode então executar o “make sem alvo, que é o mesmo que “make all“.

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

Try modificando o “hello.c” e executar make.

NOTES:

  • Se o comando não for precedido por uma tabulação, recebe uma mensagem de erro “makefile:4: *** separador em falta. Stop”
  • Se não houver makefile no directório actual, recebe uma mensagem de erro “make”: *** Nenhum alvo especificado e nenhum makefile encontrado. Stop”
  • O makefile pode ser chamado “makefileMakefile” ou “GNUMakefile“, sem extensão de ficheiro.

Mais no Makefile

Comentário & Continuação

Um comentário começa com um # e dura até ao fim da linha. A linha longa pode ser quebrada e continuada em várias linhas através de uma barra invertida (\).

Sintaxe das regras

Uma sintaxe geral para as regras é:

target1 : 

As regras são normalmente organizadas de modo a que as regras mais gerais fiquem em primeiro lugar. A regra geral é frequentemente denominada “all“, que é o alvo por defeito para make.

Metas de Fonia (ou Alvos Artificiais)

Um alvo que não representa um ficheiro é chamado alvo falso. Por exemplo, o “clean” no exemplo acima, que é apenas uma etiqueta para um comando. Se o alvo for um ficheiro, este será verificado em relação ao seu pré-requisito de desactualização. O alvo falso está sempre desactualizado e o seu comando será executado. Os alvos falsificados padrão são: allcleaninstall.

Variáveis

Uma variável começa com um $ e está entre parênteses (...) ou aparelho {...}. As variáveis de carácter único não necessitam dos parênteses. Por exemplo, $(CC)$(CC_FLAGS)$@$^.

Automatic Variables

Automatic variables are set by make after a rule is matched. Lá incluem:

  • $@: o nome do ficheiro alvo.
  • $*: o nome do ficheiro alvo sem a extensão do ficheiro.
  • $<: o primeiro pré-requisito do nome do ficheiro.
  • $^: os nomes de ficheiro de todos os pré-requisitos, separados por espaços, descartar duplicados.
  • $+: semelhante a $^, mas inclui duplicados.
  • $?: os nomes de todos os pré-requisitos que são mais recentes do que o alvo, separados por espaços.

Por exemplo, podemos reescrever o 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
Virtual Path – VPATH & vpath

p> Pode usar VPATH (maiúsculas) para especificar o directório de pesquisa de dependências e ficheiros alvo. Por exemplo,

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

Tambem se pode usar vpath (minúsculas) para ser mais preciso sobre o tipo de ficheiro e o seu directório de pesquisa. Por exemplo,

# 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
Regras Padrão

Uma regra padrão, que usa o caracter de correspondência padrão '%' como nome do ficheiro, pode ser aplicada para criar um alvo, se não houver uma regra explícita. Por exemplo,

# 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 $@
Regras de padrão implícitas

Make vem com um enorme conjunto de regras de padrão implícitas. Pode listar todas as regras através de --print-data-base option.

A Sample Makefile

Esta amostra makefile é extraída do “C/C++ Guia de Desenvolvimento -Makefile” do 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)

Síntese do Criefile

Apresentei aqui as características básicas do make para que possa ler e compreender os makefiles simples para construir aplicações C/C++. Make é na realidade bastante complexo, e pode ser considerado como uma linguagem de programação por si só!!

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *