Somos obrigados a usar um Makefile para reunir tudo para o nosso projeto, mas nosso professor nunca nos mostrou como fazê-lo.
Eu só tenho um arquivo a3driver.cpp
. O driver importa uma classe de um local "/user/cse232/Examples/example32.sequence.cpp"
,.
É isso aí. Tudo o resto está contido no .cpp
.
Como eu faria um Makefile simples que cria um executável chamado a3a.exe
?
Respostas:
Como é para o Unix, os executáveis não têm extensões.
Uma coisa a notar é que
root-config
é um utilitário que fornece a compilação correta e sinalizadores de vinculação; e as bibliotecas corretas para criar aplicativos contra raiz. Esse é apenas um detalhe relacionado ao público original deste documento.Make Me Baby
ou você nunca esquece a primeira vez que é feito
Uma discussão introdutória sobre make e como escrever um makefile simples
O que é o Make? E por que devo me importar?
A ferramenta chamada Make é um gerenciador de dependências de construção. Ou seja, ele cuida de saber quais comandos precisam ser executados em que ordem para levar seu projeto de software a partir de uma coleção de arquivos de origem, arquivos de objetos, bibliotecas, cabeçalhos, etc., etc. - alguns dos quais podem ter sido alterados recentemente --- e transformá-los em uma versão correta e atualizada do programa.
Na verdade, você pode usar o Make for também, mas não vou falar sobre isso.
Um Makefile trivial
Suponha que você tenha um diretório contendo:,
tool
tool.cc
tool.o
support.cc
support.hh
esupport.o
que dependaroot
e deva ser compilado em um programa chamadotool
, e suponha que você tenha invadido os arquivos de origem (o que significa que o existentetool
está desatualizado) e deseja compile o programa.Para fazer isso sozinho, você poderia
Verifique se é
support.cc
ousupport.hh
é mais recente quesupport.o
, e se sim, execute um comando comoVerifique se é
support.hh
outool.cc
é mais recente quetool.o
, e se sim, execute um comando comoVerifique se
tool.o
é mais recente quetool
, e se sim, execute um comando comoUfa! Que aborrecimento! Há muito a lembrar e várias chances de cometer erros. (BTW - os detalhes das linhas de comando exibidas aqui dependem do nosso ambiente de software. Esses funcionam no meu computador.)
Claro, você pode executar todos os três comandos todas as vezes. Isso funcionaria, mas não se adapta bem a um software substancial (como o DOGS, que leva mais de 15 minutos para compilar desde o início no meu MacBook).
Em vez disso, você pode escrever um arquivo chamado
makefile
assim:e apenas digite
make
na linha de comando. O qual executará as três etapas mostradas acima automaticamente.As linhas não indentadas aqui têm o formato "target: dependencies" e informam Make que os comandos associados (linhas indentadas) devem ser executados se alguma das dependências for mais recente que o destino. Ou seja, as linhas de dependência descrevem a lógica do que precisa ser reconstruído para acomodar alterações em vários arquivos. Se
support.cc
alterações, isso significa quesupport.o
deve ser reconstruído, mastool.o
pode ser deixado sozinho. Quando assupport.o
alteraçõestool
devem ser reconstruídas.Os comandos associados a cada linha de dependência são configurados com uma guia (veja abaixo), que deve modificar o destino (ou pelo menos tocá-lo para atualizar o horário da modificação).
Variáveis, Regras Integradas e Outros Artigos
Nesse ponto, nosso makefile está simplesmente lembrando o trabalho que precisa ser feito, mas ainda tivemos que descobrir e digitar todos os comandos necessários em sua totalidade. Não precisa ser assim: Make é uma linguagem poderosa com variáveis, funções de manipulação de texto e uma série de regras internas que podem tornar isso muito mais fácil para nós.
Criar variáveis
A sintaxe para acessar uma variável make é
$(VAR)
.A sintaxe para atribuir a uma variável Make é:
VAR = A text value of some kind
(ouVAR := A different text value but ignore this for the moment
).Você pode usar variáveis em regras como esta versão aprimorada do nosso makefile:
que é um pouco mais legível, mas ainda exige muita digitação
Criar funções
O GNU make suporta uma variedade de funções para acessar informações do sistema de arquivos ou de outros comandos no sistema. Nesse caso, estamos interessados em
$(shell ...)
qual expande para a saída do (s) argumento (s) e$(subst opat,npat,text)
que substitui todas as instâncias deopat
pornpat
em texto.Tirar vantagem disso nos dá:
que é mais fácil de digitar e muito mais legível.
Notar que
Regras implícitas e padrão
Geralmente, esperamos que todos os arquivos de origem C ++ sejam tratados da mesma maneira, e o Make fornece três maneiras de declarar isso:
Regras implícitas estão embutidas e algumas serão discutidas abaixo. As regras de padrão são especificadas em um formulário como
o que significa que os arquivos de objeto são gerados a partir dos arquivos de origem C executando o comando mostrado, em que a variável "automática" se
$<
expande para o nome da primeira dependência.Regras internas
O Make possui uma série de regras internas que significam que, muitas vezes, um projeto pode ser compilado por um makefile muito simples.
A regra de criação do GNU para arquivos de origem C é a exibida acima. Da mesma forma, criamos arquivos de objetos a partir de arquivos de origem C ++ com uma regra como
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
.Arquivos de objeto único são vinculados usando
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, mas isso não funcionará no nosso caso, porque queremos vincular vários arquivos de objeto.Variáveis usadas por regras internas
As regras internas usam um conjunto de variáveis padrão que permitem especificar informações do ambiente local (como onde encontrar os arquivos de inclusão ROOT) sem reescrever todas as regras. Os mais prováveis de serem interessantes para nós são:
CC
- o compilador C para usarCXX
- o compilador C ++ para usarLD
- o vinculador para usarCFLAGS
- sinalizador de compilação para arquivos de origem CCXXFLAGS
- sinalizadores de compilação para arquivos de origem C ++CPPFLAGS
- sinalizadores para o pré-processador c (normalmente incluem caminhos de arquivo e símbolos definidos na linha de comando), usados por C e C ++LDFLAGS
- sinalizadores de linkerLDLIBS
- bibliotecas para vincularUm Makefile básico
Tirando proveito das regras internas, podemos simplificar nosso makefile para:
Também adicionamos vários destinos padrão que executam ações especiais (como limpar o diretório de origem).
Observe que, quando make é chamado sem um argumento, ele usa o primeiro destino encontrado no arquivo (neste caso, todos), mas você também pode nomear o destino para obter qual é o que faz
make clean
remover os arquivos de objeto nesse caso.Ainda temos todas as dependências codificadas.
Algumas melhorias misteriosas
Notar que
make
,ls -A
verá um arquivo chamado.depend
que contém itens que parecem criar linhas de dependênciaOutras leituras
Conheça Bugs e Notas Históricas
O idioma de entrada do Make é sensível a espaço em branco. Em particular, as linhas de ação que seguem as dependências devem começar com uma guia . Mas uma série de espaços pode ter a mesma aparência (e, de fato, existem editores que convertem abas silenciosamente em espaços ou vice-versa), o que resulta em um arquivo Make que parece correto e ainda não funciona. Isso foi identificado como um bug desde o início, mas (conta a história ) não foi corrigido, porque já havia 10 usuários.
(Isso foi copiado de um post da wiki que escrevi para estudantes de graduação em física.)
fonte
-pthread
O sinalizador fazgcc
com que a definição das macros necessárias-D_REENTRANT
seja desnecessária.root-config
). Uma alternativa mais geral com a mesma capacidade deve ser proposta, se houver, ou deve ser deixada de fora. Não reduzi o voto por causa da lista e explicação das macros de criação mais usadas.Eu sempre achei que isso era mais fácil de aprender com um exemplo detalhado, então veja como penso em makefiles. Para cada seção, você tem uma linha que não é recuada e mostra o nome da seção seguido por dependências. As dependências podem ser outras seções (que serão executadas antes da seção atual) ou arquivos (que, se atualizados, farão com que a seção atual seja executada novamente na próxima vez em que você executar
make
).Aqui está um exemplo rápido (lembre-se de que estou usando 4 espaços em que devo usar uma guia, o Stack Overflow não me permite usar guias):
Quando você digita
make
, ele escolhe a primeira seção (a3driver). o a3driver depende do a3driver.o, então ele irá para essa seção. O a3driver.o depende do a3driver.cpp, portanto, ele será executado apenas se o a3driver.cpp tiver sido alterado desde a última execução. Supondo que ele tenha (ou nunca tenha sido executado), ele irá compilar a3driver.cpp em um arquivo .o, depois voltar para a3driver e compilar o executável final.Como existe apenas um arquivo, ele pode até ser reduzido para:
A razão pela qual eu mostrei o primeiro exemplo é que ele mostra o poder dos makefiles. Se você precisar compilar outro arquivo, basta adicionar outra seção. Aqui está um exemplo com um secondFile.cpp (que é carregado em um cabeçalho chamado secondFile.h):
Dessa forma, se você alterar algo em secondFile.cpp ou secondFile.he recompilar, ele só recompilará secondFile.cpp (não a3driver.cpp). Ou, alternativamente, se você alterar algo no a3driver.cpp, ele não recompilará o secondFile.cpp.
Deixe-me saber se você tiver alguma dúvida sobre isso.
Também é tradicional incluir uma seção chamada "todos" e uma seção chamada "limpo". "all" geralmente compila todos os executáveis e "clean" remove "artefatos de compilação", como arquivos .o e executáveis:
Edição: Eu não percebi que você está no Windows. Eu acho que a única diferença é mudar
-o a3driver
para-o a3driver.exe
.fonte
Por que todo mundo gosta de listar arquivos de origem? Um simples comando find pode cuidar disso facilmente.
Aqui está um exemplo de um simples C ++ Makefile. Basta soltá-lo em um diretório que contém
.C
arquivos e digitemake
...fonte
Você tinha duas opções.
Opção 1: makefile mais simples = NO MAKEFILE.
Renomeie "a3driver.cpp" para "a3a.cpp" e, na linha de comando, escreva:
E é isso. Se você estiver usando o GNU Make, use "make" ou "gmake" ou qualquer outra coisa.
Opção 2: um makefile de 2 linhas.
fonte
nmake
. Alink
linha de comando também parece muito específica para um compilador específico e deve, no mínimo, documentar qual.Seu arquivo Make terá uma ou duas regras de dependência, dependendo se você compila e vincula com um único comando ou com um comando para a compilação e outro para o link.
Dependência é uma árvore de regras que se parece com isso (observe que o recuo deve ser uma TAB):
Não deve ser uma linha em branco após os comandos para um alvo, e deve não ser uma linha em branco antes dos comandos. O primeiro destino no makefile é o objetivo geral, e outros destinos são criados apenas se o primeiro destino depender deles.
Portanto, seu makefile será mais ou menos assim.
fonte
Sugiro (observe que o travessão é um TAB):
ou
A última sugestão é um pouco melhor, pois reutiliza as regras implícitas do GNU Make. No entanto, para funcionar, um arquivo de origem deve ter o mesmo nome que o executável final (ou seja:
tool.c
etool
).Observe que não é necessário declarar fontes. Arquivos de objeto intermediários são gerados usando regra implícita. Consequentemente, este
Makefile
trabalho para C e C ++ (e também para Fortran, etc ...).Observe também que, por padrão, o Makefile é usado
$(CC)
como vinculador.$(CC)
não funciona para vincular arquivos de objeto C ++. Nós modificamosLINK.o
apenas por causa disso. Se você deseja compilar o código C, não precisa forçar oLINK.o
valor.Claro, você também pode adicionar seus sinalizadores de compilação com variável
CFLAGS
e adicionar suas bibliotecasLDLIBS
. Por exemplo:Uma observação lateral: se você precisar usar bibliotecas externas, sugiro usar o pkg-config para definir corretamente
CFLAGS
eLDLIBS
:O leitor atento notará que isso
Makefile
não será reconstruído corretamente se um cabeçalho for alterado. Adicione estas linhas para corrigir o problema:-MMD
permite criar arquivos .d que contêm fragmentos Makefile sobre dependências de cabeçalhos. A segunda linha apenas os usa.Com certeza, um Makefile bem escrito também deve incluir
clean
edistclean
regras:Observe,
$(RM)
é o equivalente arm -f
, mas é uma boa prática não chamarrm
diretamente.A
all
regra também é apreciada. Para funcionar, deve ser a primeira regra do seu arquivo:Você também pode adicionar uma
install
regra:DESTDIR
está vazio por padrão. O usuário pode configurá-lo para instalar seu programa em um sistema alternativo (obrigatório para o processo de compilação cruzada). Os mantenedores de pacotes para distribuição múltipla também podem mudarPREFIX
para instalar seu pacote no/usr
.Uma palavra final: não coloque arquivos de origem em subdiretórios. Se você realmente deseja fazer isso, mantenha-o
Makefile
no diretório raiz e use caminhos completos para identificar seus arquivos (por exemplosubdir/file.o
).Então, para resumir, seu Makefile completo deve se parecer com:
fonte
make
que eu conheço (GNU Make e BSD Make) precisa de linhas vazias entre as regras. No entanto, existem toneladas demake
implementações com seus próprios erros e especificidades.Eu usei a resposta de friedmud . Eu olhei para isso por um tempo, e parece ser uma boa maneira de começar. Essa solução também possui um método bem definido de adicionar sinalizadores de compilador. Eu respondi novamente, porque fiz alterações para fazê-lo funcionar no meu ambiente, Ubuntu e g ++. Mais exemplos de trabalho são o melhor professor, às vezes.
Makefiles parecem ser muito complexos. Eu estava usando um, mas estava gerando um erro relacionado à não vinculação nas bibliotecas do g ++. Essa configuração resolveu esse problema.
fonte