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
e support.o
que dependa root
e deva ser compilado em um programa chamado tool
, e suponha que você tenha invadido os arquivos de origem (o que significa que o existente tool
está desatualizado) e deseja compile o programa.
Para fazer isso sozinho, você poderia
Verifique se é support.cc
ou support.hh
é mais recente que support.o
, e se sim, execute um comando como
g++ -g -c -pthread -I/sw/include/root support.cc
Verifique se é support.hh
ou tool.cc
é mais recente que tool.o
, e se sim, execute um comando como
g++ -g -c -pthread -I/sw/include/root tool.cc
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 makefile
assim:
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 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 que support.o
deve ser reconstruído, mas tool.o
pode ser deixado sozinho. Quando as support.o
alterações tool
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
(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 opat
por npat
em 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
- Ainda estamos declarando explicitamente as dependências para cada arquivo de objeto e o executável final
- 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:
- regras de sufixo (consideradas obsoletas no GNU make, mas mantidas para compatibilidade com versões anteriores)
- regras implícitas
- 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 clean
remover 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
- Não há mais linhas de dependência para os arquivos de origem!?!
- Existe alguma mágica estranha relacionada a .depend e depend
- Se você o fizer
make
, ls -A
verá um arquivo chamado .depend
que 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.)