Como criar um Makefile SIMPLE C ++

303

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?

Befall
fonte
9
.EXE, então é definitivamente o Windows. Pensando bem ... o caminho é no estilo Unix. Provavelmente usando o Mingw-32.
Nathan Osman
2
Suspiro. Suponho que você precise aprender o básico de todas as operações, mesmo que nunca as use. Só tenho que entender como as coisas funcionam. As chances são boas, no entanto, de você sempre desenvolver um IDE, como o Eclipse. Você obterá uma resposta aqui para o seu caso de uma linha simples e há muitos tutoriais da Web, mas se você quiser conhecimento aprofundado, não poderá vencer o livro O'reilly (o mesmo para a maioria dos tópicos em preto e branco). amazon.com/Managing-Projects-Make-Nutshell-Handbooks/dp/… Escolha uma cópia em segunda mão da amazon, half.com, betterworldbooks eBay
Mawg diz que restabelece Monica em
2
O link postado por @Dennis agora está morto, mas o mesmo material pode ser encontrado nesta página archive.org .
Guilherme Salomé
Eu prefiro as idéias dessa pessoa. ( hiltmon.com/blog/2013/07/03/… ) A estrutura do projeto pode ser facilmente modificada para se adequar. E também concordo que o tempo do desenvolvedor deve ser gasto em outras coisas além do automake / autoconf. Essas ferramentas têm seu lugar, mas talvez não para projetos internos. Estou construindo um script que produzirá essa estrutura de projeto.
Daisuke Aramaki
@ GuilhermeSalomé Obrigado, acredito que este é o melhor tutorial simples e completo.
Hareen Laks

Respostas:

560

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.hhe support.oque dependa roote deva ser compilado em um programa chamado tool, e suponha que você tenha invadido os arquivos de origem (o que significa que o existente toolestá desatualizado) e deseja compile o programa.

Para fazer isso sozinho, você poderia

  1. Verifique se é support.ccou support.hhé mais recente que support.o, e se sim, execute um comando como

    g++ -g -c -pthread -I/sw/include/root support.cc
  2. Verifique se é support.hhou tool.ccé mais recente que tool.o, e se sim, execute um comando como

    g++ -g  -c -pthread -I/sw/include/root tool.cc
  3. Verifique se tool.oé mais recente que tool, e se sim, execute um comando como

    g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
    -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
    -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

Ufa! 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 makefileassim:

tool: tool.o support.o
    g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
        -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
        -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

tool.o: tool.cc support.hh
    g++ -g  -c -pthread -I/sw/include/root tool.cc

support.o: support.hh support.cc
    g++ -g -c -pthread -I/sw/include/root support.cc

e apenas digite makena 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.ccalterações, isso significa que support.odeve ser reconstruído, mas tool.opode ser deixado sozinho. Quando as support.oalterações tooldevem 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 (ou VAR := 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:

CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
       -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
       -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
       -lm -ldl

tool: tool.o support.o
    g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)

tool.o: tool.cc support.hh
    g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
    g++ $(CPPFLAGS) -c support.cc

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 de opatpor npatem texto.

Tirar vantagem disso nos dá:

CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

tool: $(OBJS)
    g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh
    g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
    g++ $(CPPFLAGS) -c support.cc

que é mais fácil de digitar e muito mais legível.

Notar que

  1. Ainda estamos declarando explicitamente as dependências para cada arquivo de objeto e o executável final
  2. Tivemos que digitar explicitamente a regra de compilação para os dois arquivos de origem

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:

  1. regras de sufixo (consideradas obsoletas no GNU make, mas mantidas para compatibilidade com versões anteriores)
  2. regras implícitas
  3. regras padrão

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: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c $<

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 usar
  • CXX - o compilador C ++ para usar
  • LD - o vinculador para usar
  • CFLAGS - sinalizador de compilação para arquivos de origem C
  • CXXFLAGS - 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 linker
  • LDLIBS - bibliotecas para vincular

Um Makefile básico

Tirando proveito das regras internas, podemos simplificar nosso makefile para:

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
    $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh

support.o: support.hh support.cc

clean:
    $(RM) $(OBJS)

distclean: clean
    $(RM) tool

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 cleanremover os arquivos de objeto nesse caso.

Ainda temos todas as dependências codificadas.

Algumas melhorias misteriosas

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
    $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

depend: .depend

.depend: $(SRCS)
    $(RM) ./.depend
    $(CXX) $(CPPFLAGS) -MM $^>>./.depend;

clean:
    $(RM) $(OBJS)

distclean: clean
    $(RM) *~ .depend

include .depend

Notar que

  1. Não há mais linhas de dependência para os arquivos de origem!?!
  2. Existe alguma mágica estranha relacionada a .depend e depend
  3. Se você o fizer make, ls -Averá um arquivo chamado .dependque contém itens que parecem criar linhas de dependência

Outras 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.)

dmckee --- ex-moderador gatinho
fonte
9
Este método para gerar dependências é obsoleto e realmente prejudicial. Consulte Geração avançada de dependência automática .
Maxim Egorushkin
5
-pthreadO sinalizador faz gcccom que a definição das macros necessárias -D_REENTRANTseja desnecessária.
Maxim Egorushkin
8
@jcoe Faz um pré-processador extra desnecessário para gerar dependências. Fazer um trabalho desnecessário apenas dissipa o calor derretendo os pólos de gelo e, em uma escala maior, está chegando à morte por calor do nosso universo.
Maxim Egorushkin
2
Provavelmente "prejudicial" é um pouco demais, mas, como as fases ou metas explícitas de geração de dependências estão desatualizadas, já que pelo menos o GCC 3, eu realmente acho que todos devemos passar por eles. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail lamenta os demitidos 27/08/15
2
Realmente, a resposta aceita não deve depender de um software muito específico ( 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.
green diod
56

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):

a3driver: a3driver.o
    g++ -o a3driver a3driver.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

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:

a3driver: a3driver.cpp
    g++ -o a3driver a3driver.cpp

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):

a3driver: a3driver.o secondFile.o
    g++ -o a3driver a3driver.o secondFile.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

secondFile.o: secondFile.cpp secondFile.h
    g++ -c secondFile.cpp

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:

all: a3driver ;

clean:
    # -f so this will succeed even if the files don't exist
    rm -f a3driver a3driver.o

Edição: Eu não percebi que você está no Windows. Eu acho que a única diferença é mudar -o a3driverpara -o a3driver.exe.

Brendan Long
fonte
O código absoluto que estou tentando usar é: p4a.exe: p4driver.cpp g ++ -o p4a p4driver.cpp MAS, ele diz "separador ausente". Estou usando o TAB, mas ainda me diz isso. Qualquer ideia?
Befall 20/03/10
2
Até onde eu sei, essa mensagem de erro só aparece se você tiver espaços. Verifique se você não possui nenhuma linha que comece com espaços (espaço + tab dará esse erro). Essa é a única coisa que consigo pensar ..
Brendan Long
Nota para futuros editores: o StackOverflow não pode renderizar guias, mesmo que você as edite na resposta, portanto, não tente "consertar" minha observação sobre isso.
Brendan Long
35

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 .Carquivos e digite make...

appname := myapp

CXX := clang++
CXXFLAGS := -std=c++11

srcfiles := $(shell find . -name "*.C")
objects  := $(patsubst %.C, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend
friedmud
fonte
2
Um motivo para não encontrar automaticamente os arquivos de origem é que se pode ter diferentes destinos de criação que precisam de arquivos diferentes.
hmijail lamenta os demitidos 27/08/15
Concordou com @hmijail, bem como sub-módulos que contêm uma tonelada de fontes / cabeçalhos que você não deseja que sejam compilados / vinculados ... e, sem dúvida, muitas outras circunstâncias em que a pesquisa / uso exaustivos não é adequado.
Engenheiro de
Por que usar "shell find" e não "curinga"?
Nolan
1
@Nolan para encontrar arquivos de origem em uma árvore diretório de origem
AlejandroVD
13

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:

nmake a3a.exe

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.

a3a.exe: a3driver.obj
    link /out:a3a.exe a3driver.obj
Ninguém
fonte
3
Essa seria uma excelente resposta se não pressupusesse muitas coisas sobre os detalhes do ambiente do OP. Sim, eles estão no Windows, mas isso não significa que eles estão usando nmake. A linklinha de comando também parece muito específica para um compilador específico e deve, no mínimo, documentar qual.
Tripleee 19/05/19
6

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):

main_target : source1 source2 etc
    command to build main_target from sources

source1 : dependents for source1
    command to build source1

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.

a3a.exe : a3driver.obj 
    link /out:a3a.exe a3driver.obj

a3driver.obj : a3driver.cpp
    cc a3driver.cpp
John Knoeller
fonte
6

Sugiro (observe que o travessão é um TAB):

tool: tool.o file1.o file2.o
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

ou

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o

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.ce tool).

Observe que não é necessário declarar fontes. Arquivos de objeto intermediários são gerados usando regra implícita. Consequentemente, este Makefiletrabalho 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 modificamos LINK.oapenas por causa disso. Se você deseja compilar o código C, não precisa forçar o LINK.ovalor.

Claro, você também pode adicionar seus sinalizadores de compilação com variável CFLAGSe adicionar suas bibliotecas LDLIBS. Por exemplo:

CFLAGS = -Wall
LDLIBS = -lm

Uma observação lateral: se você precisar usar bibliotecas externas, sugiro usar o pkg-config para definir corretamente CFLAGSe LDLIBS:

CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)

O leitor atento notará que isso Makefilenão será reconstruído corretamente se um cabeçalho for alterado. Adicione estas linhas para corrigir o problema:

override CPPFLAGS += -MMD
include $(wildcard *.d)

-MMDpermite 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 cleane distcleanregras:

clean:
    $(RM) *.o *.d

distclean: clean
    $(RM) tool

Observe, $(RM)é o equivalente a rm -f, mas é uma boa prática não chamarrm diretamente.

A allregra também é apreciada. Para funcionar, deve ser a primeira regra do seu arquivo:

all: tool

Você também pode adicionar uma installregra:

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIRestá 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 mudar PREFIXpara 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 Makefileno diretório raiz e use caminhos completos para identificar seus arquivos (por exemplo subdir/file.o).

Então, para resumir, seu Makefile completo deve se parecer com:

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)

all: tool
tool: tool.o file1.o file2.o
clean:
    $(RM) *.o *.d
distclean: clean
    $(RM) tool
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin
Jérôme Pouiller
fonte
Perto do fim: não deveria haver linhas vazias entre as regras? A resposta de John Knoeller afirmou isso.
Peter Mortensen
Nenhuma implementação makeque eu conheço (GNU Make e BSD Make) precisa de linhas vazias entre as regras. No entanto, existem toneladas de makeimplementações com seus próprios erros e especificidades.
Jérôme Pouiller 22/10/19
5

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.

appname := myapp

CXX := g++
CXXFLAGS := -Wall -g

srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects  := $(patsubst %.cpp, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend

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.

VectorVortec
fonte