InglêsFrancêsEspanhol

favicon do OnWorks

makepp_tutorial - Online na nuvem

Execute makepp_tutorial no provedor de hospedagem gratuita OnWorks no Ubuntu Online, Fedora Online, emulador online do Windows ou emulador online do MAC OS

Este é o comando makepp_tutorial que pode ser executado no provedor de hospedagem gratuita OnWorks usando uma de nossas várias estações de trabalho online gratuitas, como Ubuntu Online, Fedora Online, emulador online do Windows ou emulador online do MAC OS

PROGRAMA:

NOME


makepp_tutorial - Tutorial sobre como escrever makefiles

DESCRIÇÃO


A makefile é o conjunto de instruções que você usa para dizer ao makepp como construir seu
programa. O makepp pode aceitar a maioria dos makefiles escritos para o make padrão do Unix, mas se
você está começando do zero, muitas vezes é muito mais simples usar alguns dos recursos avançados do makepp
recursos. Esta é uma introdução para escrever makefiles que são específicos para makepp.

Se você já sabe muito sobre como escrever makefiles, você pode querer pelo menos ler o
seções posteriores deste arquivo porque mostram a maneira preferida de fazer as coisas com makepp,
o que geralmente é diferente da maneira tradicional de fazer isso com o make. Outra fonte de
exemplos e conselhos sobre como escrever makefiles para makepp é makepp_cookbook.

Construir um programa a partir de seus arquivos de origem pode ser uma tarefa complicada e demorada
Operação. Os comandos são muito longos para serem digitados manualmente todas as vezes. No entanto, um
shell script simples raramente é usado para compilar um programa, porque é muito
demorado para recompilar todos os módulos quando apenas um deles foi alterado.

No entanto, é muito sujeito a erros permitir que uma pessoa diga ao computador quais arquivos precisam ser
recompilado. Esquecer de recompilar um arquivo pode significar horas de depuração frustrante. UMA
ferramenta automática confiável é necessária para determinar exatamente quais módulos precisam
recompilação.

Makepp (abreviação de Make-plus-plus, ou make ++) é uma ferramenta para resolver exatamente esse problema.
É uma melhoria no fazer programa, uma ferramenta padrão que existe por muitos
anos. Ele se baseia em seu próprio conhecimento embutido (em casos muito simples) ou em um arquivo
chamado de Makefile que contém uma receita detalhada para construir o programa.

Normalmente, os arquivos de entrada são o código-fonte do programa e os arquivos de saída são executáveis,
mas o makepp não se importa com o que sejam. Você pode usar um makefile para controlar qualquer tipo de
procedimento onde você precisa executar seletivamente certos comandos, dependendo de quais arquivos
mudou. Você poderia, por exemplo, usar makepp para fazer análise de dados, onde sua entrada
arquivos são dados brutos e programas de análise, e seus arquivos de saída são dados processados ​​ou
gráficos ou qualquer coisa. Makepp descobrirá quais dos arquivos de dados processados ​​precisam ser
atualizado sempre que algum dos arquivos de dados ou programas de análise mudam. Os exemplos neste
introdução irá assumir que você está construindo um programa executável a partir do código-fonte, mas você
pode fazer muito mais com makepp do que apenas se você usar sua imaginação.

Se o seu programa consiste em um único módulo, você provavelmente não precisa do makepp, porque você
saiba que qualquer mudança que você fizer requer a recompilação desse módulo. No entanto, se o seu
programa consiste em apenas dois módulos, então você definitivamente vai querer usar um programa
como makepp.

Do I necessidade a arquivo make?
Se o seu programa é relativamente simples e não requer nada de especial,
O makepp pode já saber como construí-lo sem que você dê instruções explícitas. Para
Por exemplo, suponha que você tenha um programa em um único arquivo de origem, denominado "test.c". Você pode
apenas digite "makepp test" e seu programa será construído assim:

% teste makepp
makepp: Entrando no diretório `/ algum lugar / ou / outro '
gcc -g -Wall -c teste.c -o teste.o
gcc -g -Wall teste.o -o teste
Aviso: no Unix, para executar um programa chamado 'teste', você deve digitar
./teste
em vez de apenas 'testar'.

Estes são os comandos básicos necessários para compilar um programa no Unix. Se esses comandos não
faça algum sentido para você, consulte makepp_tutorial_compilation.

Makepp contém regras integradas para C, C ++ e Fortran.

Makepp pode às vezes descobrir como compilar programas que estão contidos em mais de
um arquivo de origem ou programas que devem ser vinculados a várias bibliotecas do sistema. Faz
isso adivinhando quais arquivos de origem e bibliotecas você precisa com base nos arquivos que você
incluir. O algoritmo real é muito complicado para discutir aqui em um tutorial (mas veja
makepp_builtin); você pode tentar, e se não funcionar automaticamente para você, você precisa
escreva seu próprio makefile.

Por padrão, para C e C ++, makepp compila o programa com informações de depuração e sem
otimização. Se você deseja ativar a otimização para que seu programa seja executado mais rapidamente,
mude a linha de comando para:

makepp CFLAGS = -O2 test

Se você estiver compilando C ++ em vez de C, use "CXXFLAGS = -O2" em vez de "CFLAGS = -O2". Para
lista completa de outras opções que você pode configurar sem escrever um makefile, veja
makepp_builtin.

As regras embutidas do Makepp são um pouco mais poderosas do que o make Unix padrão, mas se você
escrever programas de qualquer complexidade, é provável que você precise de um makefile eventualmente para
diga ao makepp o que fazer.

Se você não está familiarizado com os comandos de compilação do Unix, pode ser útil neste momento para
leia makepp_tutorial_compilation para uma descrição de quais são essas compilações Unix enigmáticas
comandos fazem.

A simples makefile
Suponha que você esteja escrevendo um programa C ++ que tem dois módulos de origem, "processing.cxx" e
"gui.cxx", junto com vários arquivos de inclusão. Se você fosse construir seu programa a partir de
scratch, você precisaria executar algo como estes comandos:

c ++ -c processing.cxx -o processing.o
c ++ -c gui.cxx -o gui.o
c ++ processing.o gui.o -o my_program

Os primeiros dois comandos são comandos de compilação e o terceiro invoca o vinculador para
combine os dois arquivos de objeto em um único executável. Se você fizer alterações em "gui.cxx"
mas não para "processing.cxx", então você não precisa executar novamente o primeiro comando, mas você
precisa executar os dois últimos comandos. makepp pode descobrir isso para você
automaticamente.

(Se você nunca trabalhou com make antes, pode estar pensando que pode combinar o
acima de três comandos em um único comando, como este:

c ++ processing.cxx gui.cxx -o my_program

Quando você omite a opção "-c" do compilador, ele combina a compilação e a vinculação
Passo. Isso geralmente é bastante conveniente quando você não está escrevendo um makefile. No entanto, é
não é uma boa ideia fazer isso em um makefile, porque ele sempre recompila ambos os módulos, mesmo
se um deles não mudou, e isso pode levar uma quantidade significativa de tempo extra.)

Para usar o makepp para controlar o processo de construção, você precisará escrever um makefile. O
makefile é um arquivo de texto que contém a receita para construir seu programa. Geralmente
reside no mesmo diretório que os fontes, e é normalmente chamado de "Makeppfile",
"Makefile" ou, apenas na raiz da sua árvore de construção "RootMakeppfile".

Cada um desses comandos deve ser um separado governar em um makefile. Uma regra é um
instrução para construir um ou mais arquivos de saída de um ou mais arquivos de entrada. Makepp
determina quais regras precisam ser executadas novamente, determinando se algum dos arquivos de entrada
para uma regra foram alterados desde a última vez que a regra foi executada.

Uma regra tem uma sintaxe como esta:

output_filenames: input_filenames
Ações

A primeira linha da regra contém uma lista separada por espaços de arquivos de saída, seguida por um
dois pontos, seguidos por uma lista de arquivos de entrada separados por espaço. Os arquivos de saída também são
chamado tem como alvo, e os arquivos de entrada também são chamados dependências; dizemos que o alvo
arquivo depende das dependências, porque se qualquer uma das dependências mudar, o destino
deve ser reconstruído.

As linhas restantes da regra (o Ações) são comandos do shell a serem executados. Cada
a ação deve ser indentada com pelo menos um espaço (o make tradicional requer uma guia
personagem). Normalmente, há apenas uma linha de ação, mas pode haver quantas você desejar;
cada linha é executada sequencialmente, e se qualquer uma delas falhar, o restante não é
executado. A regra termina na primeira linha que não é indentada.

Alguns ou todos os Ações também podem ser instruções Perl. Eles podem estar em uma única linha e
rodeado por "perl {...}". Ou eles podem abranger várias linhas, caso em que as chaves
deve ser duplicado, "perl {{...}}".

Você pode colocar as regras em qualquer ordem no makefile, mas é tradicional escrever o
regra que vincula o programa primeiro, seguida pelas regras de compilação. Uma razão para isso
é que se você simplesmente digitar "" makepp "", o makepp tenta construir o primeiro alvo em
o arquivo, o que significa que ele construirá todo o seu programa e não apenas uma parte dele.
(Se você quiser construir algo diferente do primeiro alvo, você deve especificar o nome
do destino na linha de comando, por exemplo, "" makepp processing.o "".)

Os comandos de compilação acima devem ser escritos como três regras separadas. Um makefile para
construir este programa pode ter a seguinte aparência:

# Comando Link:
meu_programa: processamento.o gui.o
c ++ processing.o gui.o -o my_program
perl {{
imprimir "meu_programa é construído \ n";
}}

# Comandos de compilação:
processamento.o: processamento.cxx
c ++ -c processing.cxx -o processing.o

gui.o: gui.cxx
c ++ -c gui.cxx -o gui.o

(Os caracteres em uma linha após um "#" são ignorados; são apenas comentários. Você não
precisa do "" # comando Link: "" comentário no makefile.)

Para usar este makefile, simplesmente faça cd para o diretório e digite "" makepp "". Makepp irá tentar
para construir o primeiro destino no makefile, que é "my_program". (Se você não quiser
para construir o primeiro alvo, então você tem que fornecer o nome do alvo que você realmente
deseja construir na linha de comando.)

Quando o makepp tenta construir "meu_programa", ele percebe que primeiro deve construir
"processing.o" e "gui.o" antes de executar o comando de link. Então, olha para o
outras regras no makefile para determinar como construí-los.

Para construir "processing.o", makepp usa a segunda regra. Desde "processing.o"
depende de "processing.cxx", o makepp também tentará fazer "processing.cxx". Não há
regra para fazer "processing.cxx"; ele já deve existir.

O Makepp verifica se "processing.cxx" mudou desde a última vez que "processing.o" foi
construído. Por padrão, ele determina isso observando as datas no arquivo. Makepp
lembra qual foi a data de "processing.cxx" da última vez que "processing.o" foi feito por
armazená-lo em um arquivo separado (em um subdiretório chamado ".makepp"). Makepp irá executar
as ações para construir o alvo se alguma das seguintes opções for verdadeira:

· O alvo não existe.

· O destino existe, mas makepp não possui nenhuma informação sobre a última compilação.

· A data em qualquer arquivo de entrada mudou desde a última compilação, e há um
diferença significativa no arquivo de entrada. Para arquivos C ou C ++, isso significa uma mudança
além de espaços em branco ou dentro dos comentários.

· A data em qualquer destino mudou desde a última construção, e há uma diferença em
o arquivo de destino.

· As ações mudaram desde a última compilação.

· A última construção ocorreu em uma arquitetura diferente (tipo de CPU ou operação diferente
tipo de sistema).

· Qualquer variável de ambiente listada na regra mudou de valor.

Pode parecer um pouco engraçado que o makepp execute a ação se o arquivo de saída ou
os arquivos de entrada mudaram desde a última compilação. Makepp é projetado para garantir que
sua construção está correta, de acordo com os comandos do makefile. Se você for e modificar
o arquivo você mesmo, então o makepp não pode garantir que o arquivo modificado está realmente correto,
por isso, insiste na reconstrução. (Para obter mais informações sobre como o makepp decide se
reconstruir, e como você pode controlar isso, consulte makepp_signatures e makepp_command.)

Agora, "processing.o" pode não depender apenas de "processing.cxx"; se "processing.cxx" inclui
quaisquer arquivos ".h", então ele precisa ser recompilado se algum desses arquivos ".h" foi alterado,
mesmo que o próprio "processing.cxx" não tenha mudado. Você pode modificar a regra assim:

# Listagem desnecessária de arquivos .h
processamento.o: processamento.cxx processamento.h vetor_simples.h lista.h
c ++ -c processing.cxx -o processing.o

No entanto, é um verdadeiro incômodo modificar o makefile toda vez que você altera a lista de
arquivos incluídos e também é extremamente sujeito a erros. Você não só teria que
liste os arquivos que "processing.cxx" inclui, mas também todos os arquivos que esses arquivos
incluir, etc. Vocês não para do esta. Makepp é inteligente o suficiente para verificar se há inclusão
arquivos automaticamente. Sempre que ele vê um comando que se parece com uma compilação C ou C ++
(olhando para a primeira palavra da ação), ele lê os arquivos de origem procurando por
diretivas "#include". Ele sabe onde procurar por arquivos de inclusão, analisando seu compilador
linha de comando para opções "-I". Todos os arquivos incluídos são automaticamente adicionados ao
a lista de dependências e quaisquer arquivos que eles incluam. Se algum deles mudou, o
o arquivo será recompilado.

Depois que o makepp sabe que "processing.o" está atualizado, ele determina se "gui.o"
precisa ser reconstruída aplicando o mesmo procedimento à terceira regra. Quando ambos
"processing.o" e "gui.o" são conhecidos por serem construídos corretamente, então makepp aplica o mesmo
procedimento para ver se o comando link precisa ser executado novamente.

O makefile acima funcionará, mas mesmo para este problema simples, um usuário experiente não é
provavelmente escreverá seu makefile desta forma. Várias melhorias são discutidas no próximo
.

utilização variáveis

Até agora, nosso makefile para compilar nosso programa de dois módulos se parece com isto:

# Comando Link:
meu_programa: processamento.o gui.o
c ++ processing.o gui.o -o my_program

# Comandos de compilação:
processamento.o: processamento.cxx
c ++ -c processing.cxx -o processing.o

gui.o: gui.cxx
c ++ -c gui.cxx -o gui.o

Isso funciona maravilhosamente bem, mas suponha que agora queiramos alterar algumas opções de compilação. Ou
talvez queiramos usar um compilador diferente. Teríamos que mudar todas as três compilações
Linhas.

Da mesma forma, suponha que desejamos alterar a lista de módulos a compilar. Teríamos que mudar
em dois lugares.

A duplicação de informações como essa é uma receita para o desastre. Se você for e mudar o seu
makefile, é praticamente garantido que em algum momento, você ou outra pessoa vai esquecer
para mudar um dos lugares. Dependendo de qual é a mudança (especialmente se ela afeta
definições de pré-processador), isso pode levar a problemas sutis e difíceis de depurar em seu
.

A maneira de evitar a duplicação de informações é especificar as informações apenas uma vez e
armazene-o em uma variável, que pode ser acessada sempre que a informação for necessária.

# Defina os símbolos que podemos querer alterar:
CXX: = c ++
CXXFLAGS: = -g

OBJETOS: = processing.o gui.o

meu_programa: $ (OBJETOS)
$ (CXX) $ (OBJETOS) -o meu_programa

processamento.o: processamento.cxx
$ (CXX) $ (INCLUI) $ (CXXFLAGS) -c processing.cxx -o processing.o

gui.o: gui.cxx
$ (CXX) $ (CXXFLAGS) -c gui.cxx -o gui.o

Aqui, "$ (CXX)" se expande para ser o valor da variável "CXX", e da mesma forma para
"$ (CXXFLAGS)" e "$ (OBJETOS)". Agora podemos apenas mudar uma linha no makefile, e todos
comandos de compilação relevantes são afetados.

Na verdade, nem mesmo precisamos alterar o makefile para alterar as opções de compilação.
As atribuições especificadas na linha de comando substituem as atribuições no makefile. Para
exemplo, poderíamos digitar isso no shell:

makepp CXXFLAGS = "- g -O2"

que substitui a configuração de "CXXFLAGS" no makefile. É como se o makefile
continha a linha

CXXFLAGS: = -g -O2

em vez da definição que contém.

Pode não ser útil ser capaz de substituir essas coisas para você
desenvolvimento, mas se você distribuir suas fontes para outras pessoas, elas podem apreciá-lo.

Os nomes das variáveis ​​diferenciam maiúsculas de minúsculas (por exemplo, "OBJETOS" é diferente de "objetos"). Usualmente
as pessoas escrevem a maioria das variáveis ​​apenas em maiúsculas, mas você não precisa.

Se você precisar colocar um cifrão literal em uma ação de regra, escreva-o com um cifrão duplo
sinal, assim:

teste:
para testfile em * .test; execute run_test $$ testfile; feito

Convencionalmente, existem algumas variáveis ​​que você pode querer definir. Estes são apenas
convenções, mas você os verá em muitos makefiles.

CC: = cc # O compilador C.
CFLAGS: = -g # opções de compilação C que se relacionam com
# otimização ou depuração (geralmente
# apenas -g ou -O). Normalmente isso não
# incluem opções -I para especificar o
# inclui diretórios, porque então você
# não foi possível substituí-lo na linha de comando
# facilmente como no exemplo acima.
CXX: = c ++ # O compilador C ++. (Às vezes, "CPP" em vez
# de CXX.)
CXXFLAGS: = -g # Opções de compilação C ++ relacionadas a
# otimização ou depuração (-O ou -g).
F77: = f77 # O compilador Fortran.
FFLAGS: = # Sinalizadores de otimização para Fortran.

O Makepp irá adivinhar os valores apropriados para algumas dessas variáveis ​​se você não as especificar
(veja makepp_builtin), mas geralmente é melhor defini-los explicitamente - torna mais fácil
qualquer um lendo seu makefile.

Existem muitas coisas mais poderosas que você pode fazer com variáveis, mas primeiro nós
precisa explicar mais algumas coisas sobre makefiles.

padrão regras

Ter uma regra para cada comando de compilação é bom quando há apenas alguns arquivos, mas
e se o seu programa consistir em dezenas de arquivos de origem? A maioria deles deve ser compilada
com comandos muito semelhantes. É tedioso digitar uma regra separada para cada fonte
arquivo, e então se você decidir mudar as regras, você tem que mudar o makefile em um
dezenas de lugares. A melhor solução para este problema é usar um de cinto de segurança governar.

Uma regra padrão é uma maneira concisa de especificar uma regra para vários arquivos de uma vez. A regra
dependerá dos nomes dos arquivos, mas geralmente depende deles de uma forma simples. Vocês
especifique um padrão usando o curinga "%". Quando presente na lista de dependências, "%"
corresponde a qualquer string de qualquer comprimento; quando presente na lista de alvos, "%" representa o
string que corresponde a "%" na lista de dependências.

A seguinte regra padrão pegará qualquer arquivo ".c" e o compilará em um arquivo ".o":

% .o:% .c
$ (CC) $ (CFLAGS) $ (INCLUI) -c $ (entrada) -o $ (saída)

(Isso pressupõe que você tem as variáveis ​​"CC", "CFLAGS" e "INCLUDES" definidas para serem
algo adequado. O Makepp adivinhará um valor para "CC" e "CFLAGS".)

A primeira linha da regra diz que se aplica a todos os arquivos de entrada possíveis que correspondem
o padrão "% .c". Esses arquivos ".c" podem ser transformados no arquivo ".o" correspondente
usando as ações especificadas.

A ação da regra é bastante semelhante às outras ações que vimos anteriormente, exceto
que ele usa automático variáveis. Uma variável automática é uma variável cujo valor é
definido automaticamente pelo makepp dependendo da regra em que aparece. O mais útil
variáveis ​​automáticas são:

"$ (entrada)"
O nome do primeiro arquivo de entrada. Nesta regra, este seria o arquivo que corresponde
o padrão "% .c". "$ (dependência)" é um sinônimo para "$ (entrada)". Em makefiles mais antigos,
você também verá o símbolo críptico $ <usado também.

"$ (saída)"
O nome do primeiro arquivo de saída. Nesta regra, este seria o arquivo que corresponde
o padrão "% .o". "$ (target)" e $ @ são sinônimos.

"$ (entradas)"
O nome de todos os arquivos de entrada listados explicitamente. Neste caso, uma vez que existe apenas um,
"$ (entradas)" é equivalente a "$ (entrada)". "$ (dependências)" e $ ^ são sinônimos.

"$ (saídas)"
O nome de todos os destinos listados explicitamente. Neste caso, uma vez que existe apenas um,
"$ (saídas)" é equivalente a "$ (saídas)". "$ (targets)" é um sinônimo para
"$ (saídas)".

Observe que essas variáveis ​​são minúsculas.

Você pode usar essas variáveis ​​automáticas mesmo para regras não padronizadas. Isso evita repetir
nomes de arquivo de destino.

Na verdade, você pode fazer coisas consideravelmente mais complicadas com regras de padrão. Por exemplo,

# Coloque os arquivos objeto em um diretório separado:
objetos /%. o:% .cpp
$ (CXX) $ (CXXFLAGS) -c $ (entrada) -o $ (saída)

# Execute um pré-processador para criar arquivos de origem:
moc _%. cxx:% .h
$ (MOC) $ (entrada) -o $ (saída)

Usando regras de padrão e variáveis ​​automáticas, provavelmente reescreveríamos nosso makefile para nosso
programa simples como este:

CXX: = c ++
CXXFLAGS: = -g
INCLUI: = -I. # Isso conteria quaisquer opções -I para o
# compilador, se houver.
LIBS: = -L / usr / X11R6 / lib -lX11 # Contém as bibliotecas que precisamos vincular.
OBJETOS: = processing.o gui.o

meu_programa: $ (OBJETOS)
$ (CXX) $ (entradas) -o $ (saída) $ (LIBS)

% .o:% .cxx
$ (CXX) $ (INCLUI) $ (CXXFLAGS) -c $ (entrada) -o $ (saída)

Agora não precisamos ter uma regra explícita para cada arquivo de objeto que precisamos produzir. Se nós
queremos adicionar outro módulo ao nosso programa, só temos que mudar a linha que
define a variável "OBJETOS". Observe que este makefile agora é muito mais conciso do que nosso
makefile original. Cada informação ocorre apenas uma vez, então não há possibilidade
de cometer um erro ao alterar as informações em um lugar e esquecer de alterá-las em
outras.

Quando você usa regras de padrão, não é incomum haver duas regras diferentes que podem
produza o mesmo arquivo. Se ambas as regras são regras de padrão, então aquela que ocorre mais tarde em
o makefile é realmente usado. Se uma regra é uma regra padrão e a outra é uma
regra explícita (uma que realmente nomeia o arquivo de destino explicitamente), então a regra explícita
é usado. Isso geralmente é útil se você deseja compilar a maioria dos módulos com o mesmo comando,
mas há um módulo que precisa de opções de compilação ligeiramente diferentes, conforme mostrado em
este fragmento makefile:

CXXFLAGS: = -g -O2
FAST_CXXFLAGS: = -DNO_DEBUG -O6 -malign-double -funroll-all-loops

% .o:% .cpp
$ (CXX) $ (CXXFLAGS) -c $ (entrada) -o $ (saída)

time_critical_subs.o: time_critical_subs.cpp
$ (CXX) $ (FAST_CXXFLAGS) -c $ (entrada) -o $ (saída)

Há também outra sintaxe que pode ser mais conveniente para afetar as opções de compilação
para apenas um ou alguns alvos. É possível dizer ao makepp que uma variável deve ter
um valor diferente para certos alvos específicos. Neste exemplo, seria assim:

CXXFLAGS: = -g -O2
FAST_CXXFLAGS: = -DNO_DEBUG -O6 -malign-double -funroll-all-loops

% .o:% .cpp
$ (CXX) $ (CXXFLAGS) -c $ (entrada) -o $ (saída)

time_critical_subs.o: CXXFLAGS: = $ (FAST_CXXFLAGS)

Em geral, se você especificar um nome de variável após uma lista de destinos, é necessário um
valor diferente quando o comando de construção para esses destinos está sendo determinado.

Se você quiser fazer algo com padrões que não são expressos facilmente
usando o caractere curinga "%", o makepp tem outra sintaxe que é um pouco mais difícil de ler, mas
consideravelmente mais poderoso. Consulte a cláusula foreach nas regras para obter mais detalhes.

Makepp, na verdade, tem regras embutidas para compilar código C ou C ++ ou Fortran, que são
disponíveis se você não substituí-los por suas próprias regras. As regras embutidas são quase
idênticos aos exemplos acima. A maioria dos makefiles contém regras de padrão para compilação,
mas você pode depender das regras embutidas, se desejar.

Falso tem como alvo
Freqüentemente, é conveniente colocar comandos no makefile que não criam um arquivo,
mas estão de alguma forma logicamente associados ao processo de construção. Por exemplo, um muito comum
procedimento em makefiles é algo assim:

prefixo =/ usr / local

instalar: nosso_programa
& instale -m 0755 nosso_programa $ (prefixo)/ bin
& instalar -m 0644 $ (wildcard * .png) $ (prefixo) / share / our_program / icons

.PHONY: instalar

Quando alguém digita "makepp install", o makepp primeiro constrói "our_program" e, em seguida, executa o
comandos associados ao destino de instalação. O comando "instalar" simplesmente copia seu
argumentos para o diretório especificado e define a proteção do arquivo para o indicado
valor. Então, copia nosso_programa para dentro / usr / local / bin, e alguns arquivos de dados associados em
/ usr / local / share / our_program / icons. Mas isso não cria um arquivo chamado "instalar" no
o diretório atual.

O próprio comando, & install, é precedido por um E comercial. Isso significa que não é um
Comando shell, mas um comando interno semelhante. (Você vê a diferença em que você precisa
a função "$ (curinga)", porque o Shell não está expandindo para você.) Makepp tem
alguns deles, por razões de portabilidade - "instalar", quando é
presente, difere enormemente e até mesmo comandos triviais como "echo" do - e performance. Ver
os comandos embutidos para mais detalhes.

O alvo "instalar" aqui é chamado de falso alvo porque o makepp o trata como se fosse
um arquivo real, mas não é realmente um arquivo, é apenas um truque para forçar o makepp a construir
suas dependências e, em seguida, execute alguns comandos.

Essa é a linha

.PHONY: instalar

é para. Diz ao makepp que realmente não deve esperar que o arquivo "./install" exista
após a execução dos comandos. Se você esquecer a declaração falsa, o makepp irá
espere que o arquivo "install" exista após a execução dos comandos, e ele irá reclamar
bem alto se isso não acontecer.

Você também pode escrever a declaração falsa como esta:
$ (instalação falsa): nosso_programa
...

e, em seguida, omita a linha ".PHONY: install". Isso significa que você pode declarar o alvo como
falso na mesma linha em que você o define, o que pode tornar seus makefiles mais legíveis.

Alvos falsos são extremamente comuns em makefiles. Em quase todos os makefiles, o primeiro
target é o alvo falso "todos", assim:

$ (todos falsos): programa1 programa2 programa3

Se nenhum alvo for especificado na linha de comando, makepp tenta construir o primeiro alvo
no arquivo. Se o seu makefile fizer mais do que apenas um programa, você provavelmente desejará
compilar todos os programas por padrão. Neste exemplo, se o programador apenas digitar
"makepp" sem nenhum argumento, o makepp tenta construir "todos", o que o força a construir
todos os três programas deste diretório.

Aqui está um fragmento de makefile de amostra que ilustra alguns alvos falsos comumente usados:

PROGRAMAS: = discombobulator combobulator

$ (tudo falso): $ (PROGRAMAS) # Tudo é o primeiro destino, então é o padrão.

combobulator: $ (COMBOBULATOR_OBJS)
$ (CXX) $ (entradas) -o $ (saída)

discombobulator: $ (DISCOMBOBULATOR_OBJS)
$ (CXX) $ (entradas) -o $ (saída)

#
# Este destino garante que tudo seja compilado e, em seguida, coloca o
# programas em um lugar onde todos possam acessá-los. Nós fazemos o
# diretórios se eles ainda não existirem. Não usamos o & mkdir, porque
# & install mantém registro de tudo o que faz, para que o & desinstalação possa mais tarde
# desfazer isso.
#
prefixo: = / usr / local

$ (instalação falsa): tudo
& install -d $ (prefixo)/ bin $ (prefixo) / compartilhar / combobulate
& instalar -m 0755 $ (PROGRAMAS) $ (prefixo)/ bin
& instalar -m 0644 $ (curinga * .xbm) $ (prefixo) / compartilhar / combobulate

#
# Este destino faz uma distribuição de origem para envio a alguém.
#
VERSÃO: = 3.14

$ (distribuição falsa):
rm -rf combobulate - $ (VERSION) # Livre-se do lixo anterior, se houver.
& mkdir combobulate - $ (VERSION)
& cp -l $ (wildcard * .c * .h) Makefile README INSTALAR combobulate - $ (VERSION)
tar cf - combobulate - $ (VERSION) | gzip -9c> combobulate - $ (VERSION) .tar.gz
rm -rf combobulate - $ (VERSION)

#
# Este alvo executa testes de regressão para certificar-se de que o (s) programa (s) são
# fazendo o que deve fazer.
#
$ (teste falso): $ (PROGRAMAS)
noecho para testfile em * .test; Faz \
./combobulate $$ testfile | ./discombobulate -> junk_output; \
if cmp -s junk_output $$ testfile; então \
echo passou $$ testfile; \
outro \
echo falhou $$ testfile; \
fi; \
feito
#
# Se "noecho" é a primeira palavra da ação, a ação em si não é
# impresso antes de ser executado. Neste caso, imprimir a ação
# iria apenas bagunçar a tela, então é muito comum suprimir
# imprimindo para tais comandos longos.
#
# Observe o uso do cifrão duplo para passar um único cifrão para
# A concha. Observe também as barras invertidas no final de uma linha para indicar
# que um comando shell continua para a próxima linha.
#

Trabalho com vários diretórios
Se o seu programa atingir um tamanho substancial ou se usar bibliotecas que precisam ser construídas
mas devem ser mantidos separados, é bastante provável que você tenha dividido suas fontes em
vários diretórios. Uma das principais motivações para escrever makepp era fazer lidar
com vários diretórios muito mais fácil do que com o utilitário make padrão. Se vocês são
familiarizado com o make Unix padrão, você notará que com o makepp, você não precisa
mexa com complexidades feias, como invocações recursivas de make.

Com makepp, você simplesmente coloca um makefile separado em cada diretório que constrói o relevante
arquivos nesse diretório. Quando um makefile se refere a arquivos cujos comandos de compilação estão em
diferentes makefiles, makepp automaticamente encontra as regras de compilação apropriadas em outro
makefiles. Todas as ações em cada makefile são executadas com o diretório atual definido para ser
o diretório que contém o makefile, então cada makefile pode ser escrito independentemente de
Todos os outros. Nenhum makefile precisa saber nada sobre os outros makefiles; isso não
até tem que dizer ao makepp para carregar as regras daqueles outros makefiles.

Quando você tiver escrito seus makefiles, faça cd para o diretório que contém seu programa principal,
e digite "makepp" como faria normalmente. O makepp irá carregar no makefile desse
diretório. Irá notar que este makefile se refere a arquivos em outros diretórios, e
examinará esses outros diretórios para ver se há um makefile neles. Desta maneira,
todos os makefiles relevantes serão carregados.

Como um exemplo simples, suponha que seu diretório de nível superior contenha o seguinte makefile
(o nome sugerido é "RootMakeppfile", mas "Makeppfile" também funcionará):

# Makefile de nível superior:

CXX: = c ++
CXXFLAGS: = -O2
my_program: main.o goodies / libgoodies.so
$ (CXX) $ (entradas) -o $ (saída)

% .o:% .cxx
$ (CXX) $ (CXXFLAGS) -c $ (entrada) -o $ (saída)

Você precisaria escrever um makefile no diretório "goodies" que constrói
"libgoodies.so", assim:

# goodies / Makefile

CXX: = c ++
CXXFLAGS: = -O2

MÓDULOS = candy.o chips.o licorice.o cookies.o pipoca.o espinafre.o

libgoodies.so: $ (MODULES)
$ (CXX) -shared $ (entradas) -o $ (saída)
# Observe que o comando é escrito assumindo que
# o diretório atual é o subdiretório
# "goodies", não o subdiretório de nível superior.
# Makepp cds neste diretório antes de executar
# quaisquer comandos deste makefile.

% .o:% .cxx
$ (CXX) $ (CXXFLAGS) -fpic -c $ (entrada) -o $ (saída)

E isso é tudo que você precisa fazer.

Quaisquer variáveis ​​que você especificar na linha de comando substituem a definição do
variável em todos os makefiles. Assim, por exemplo, se você digitar "makepp CXXFLAGS =" - g "", todos
os módulos serão recompilados para depuração porque a definição de "CXXFLAGS" em ambos
makefiles é sobrescrito.

Os diretórios contendo outras fontes não precisam ser subdiretórios de nível superior
diretório (como estão neste exemplo). Eles podem estar em qualquer lugar no sistema de arquivos; makepp
irá carregar automaticamente um makefile de qualquer diretório que contenha um arquivo que é um
dependência de algum destino que está tentando construir. Ele também irá carregar um makefile de qualquer
diretório que é varrido por um curinga.

O carregamento automático funciona se os arquivos construídos pelo seu makefile residirem no mesmo diretório
como o próprio makefile. Se você escrever seu makefile de forma que suas regras produzam arquivos em um
diretório diferente do próprio makefile, então você pode ter que dizer ao makepp onde
procure os makefiles, pois não há como adivinhar. Você pode fazer isso usando
a instrução "load_makefile" em seu makefile. Para mais informações sobre este e outros
problemas relacionados a compilações de vários diretórios, consulte "Dicas para vários diretórios" em
makepp_cookbook.

Uma advertência: se você referenciar a variável "$ (MAKE)" em seu makefile, makepp automaticamente
entra no modo de compatibilidade com versões anteriores e desativa o carregamento automático.

Modelo or clichê arquivos

O Makepp tem vários outros recursos que tornam a vida um pouco mais fácil para os programadores que têm
para manter um programa abrangendo vários diretórios. Nos exemplos acima, você notará
que as definições das variáveis ​​"CXX" e "CXXFLAGS" devem ser repetidas em cada
makefile. Pode ser um incômodo inserir novamente as mesmas informações em cada makefile, e
pode ser um problema se você decidir alterá-lo - pode ser necessário modificar dezenas de
diferentes makefiles.

O que você pode fazer é colocar todas as informações que são comuns a cada makefile
em um arquivo separado, localizado talvez no topo da árvore de diretórios. Comum
as informações geralmente incluem definições de variáveis ​​e, às vezes, também regras de padrões. (No
no exemplo acima, no entanto, as regras de padrão não são as mesmas em ambos os makefiles.)
suponha que você tenha chamado esse arquivo de "standard_defs.mk". Então, cada makefile simplesmente precisa
contém uma declaração como esta:

incluir standard_defs.mk

Quando makepp vê esta declaração, ele insere o conteúdo do arquivo no makefile em
esse ponto. A instrução "incluir" primeiro procura o arquivo no diretório atual,
em seguida, no pai do diretório atual e assim por diante até o nível superior do arquivo
sistema, então você realmente não precisa especificar "../standard_defs.mk" ou
"../../../../standard_defs.mk".

Assim, poderíamos reescrever os makefiles acima para ficarem assim. "standard_defs.mk" existiria
no diretório de nível superior e pode conter as seguintes definições:

#standard_defs.mk
CXX: = c ++
CXXFLAGS: = -O2

#
# Também incluímos uma regra de padrão que pode ser útil em um ou mais
# subdiretórios. Esta regra padrão é para compilação C para colocar
# coisas em uma biblioteca compartilhada (é para isso que serve a opção -fpic).
#
% .o:% .cxx
$ (CXX) $ (CXXFLAGS) -fpic -c $ (entrada) -o $ (saída)

Observe que, uma vez que o arquivo incluído é realmente inserido em cada makefile, as regras no
arquivo incluído são aplicados com o diretório padrão definido para o diretório que contém o
makefile que incluiu o arquivo, não o diretório que contém o arquivo de inclusão.

O "Makefile" de nível superior pode ter a seguinte aparência:

# Makefile de nível superior
incluir standard_defs.mk

my_program: main.o goodies / libgoodies.so
$ (CXX) $ (entradas) -o $ (saída)

#
# Observe que esta regra padrão substitui a encontrada em standard_defs.mk,
# porque makepp vê isso mais tarde. Esta regra padrão é para compilação para
# um módulo que não pertence a uma biblioteca compartilhada.
#
% .o:% .cxx
$ (CXX) $ (CXXFLAGS) $ (entrada) -o $ (saída)

E o makefile do subdiretório pode ter a seguinte aparência:

# goodies / Makefile
incluir standard_defs.mk

MÓDULOS = candy.o chips.o licorice.o cookies.o pipoca.o espinafre.o

libgoodies.so: $ (MODULES)
$ (CXX) -shared $ (entradas) -o $ (saída)

# Não precisamos da regra padrão para a compilação de arquivos .cxx para .o,
# porque está contido em standard_defs.mk.

A -F compilação opção

Se você executar o makepp de dentro de um editor como o emacs, e você estiver editando fontes de
vários diretórios diferentes, você pode descobrir que o diretório padrão para makepp difere
dependendo de qual arquivo você editou recentemente. Como resultado, o makepp pode não carregar
o makefile correto.

O que você pode fazer para garantir que o makepp sempre carregue o (s) makefile (s) correto (s), não importa o que
diretório passa a ser o seu diretório atual, é usar a opção de linha de comando "-F",
como isso:

makepp -F ~ / src / meu_programa

O makepp irá primeiro fazer o cd para o diretório "~ / src / meu_programa"antes de tentar carregar um
arquivo make.

utilização Curingas
Até este ponto, tivemos que listar explicitamente todos os módulos que entram em um
programa ou uma biblioteca. O makefile anterior, por exemplo, continha esta linha:

MÓDULOS = candy.o chips.o licorice.o cookies.o pipoca.o espinafre.o

libgoodies.so: $ (MODULES)
$ (CXX) -shared $ (entradas) -o $ (saída)

Neste caso, listar todos os módulos que vão para "libgoodies.so" não é tão grande
lidar uma vez que não há muitos deles. Mas às vezes pode ser um verdadeiro incômodo para
lista todos os arquivos de objeto, especialmente se esta lista estiver mudando rapidamente durante
desenvolvimento. Freqüentemente, você deseja que cada módulo em todo o diretório seja
compilado em seu programa ou biblioteca. Seria muito mais fácil se você pudesse apenas dizer
makepp para fazer isso sem listar todos eles.

Bem, você pode. As linhas acima podem ser reescritas como:

libgoodies.so: * .o
$ (CXX) -shared $ (entradas) -o $ (saída)

O curinga "* .o" corresponde a qualquer arquivo ".o" existente ou a qualquer arquivo ".o" que ainda não
existem, mas podem ser feitas por qualquer uma das regras que o makepp conhece a partir de qualquer makefiles que
leu. Portanto, o curinga retornará a mesma lista de arquivos, não importa se você
não compilou nada ainda, ou se todos os módulos foram compilados antes.

Claro, se você contaminar seus diretórios com arquivos extras que não deveriam ser compilados
diretamente em sua biblioteca, (por exemplo, se você escrever pequenos programas de teste e deixá-los em
mesmo diretório dos arquivos de origem da biblioteca), esses módulos estarão incorretamente
incluído em sua biblioteca. Se você optar por usar curingas, cabe a você manter o
diretório limpo o suficiente.

O Makepp suporta os curingas usuais do Unix e um adicional:

"*"
Corresponde a qualquer string de 0 ou mais caracteres. Não corresponderá ao caractere "/". Para
exemplo, "a * c" corresponde a "ac", "abc" e "aaaaabc", mas não a "aa / bc".

"?" Corresponde exatamente a um caractere (sem incluir "/"). Por exemplo, "???. O" corresponde a todos
nomes de arquivos que possuem 3 caracteres antes da extensão ".o".

[caracteres]
Corresponde a qualquer um de uma lista de caracteres nessa posição. Por exemplo, "[abc] .o" corresponde
"ao", "bo", "co", mas não "abc.o" ou "do". Você também pode especificar um intervalo de
caracteres, por exemplo, "dados_ [0-9]" corresponderá a "dados_0", "dados_1", etc.

"**"
Este é um caractere curinga especial, encontrado apenas no makepp (e no shell zsh, do qual eu
roubou a ideia, como o bash fez desde então). Corresponde a qualquer número de intervenientes
diretórios. Por exemplo, "** / *. O" corresponde a "xyz.o", "test_programs / abc.o" e
"um subdiretório / profundamente / aninhado / / def.o".

Se suas fontes estão contidas em vários subdiretórios, e você deseja vincular todos os
módulos de objeto juntos, você pode escrever assim:

liboodles.so: ** / *. o
$ (CXX) -shared $ (entradas) -o $ (saída)

Funções e Avançado Variável Uso
Makepp possui uma série de maneiras extremamente poderosas de manipular texto. Este tutorial mostra um
algumas das maneiras mais úteis, mas você pode querer dar uma olhada em makepp_variables e
makepp_functions para uma lista mais completa.

listas of correspondente arquivos

Um problema comum em makefiles é a manutenção de duas listas de arquivos correspondentes.
Considere as duas variáveis ​​a seguir:

FONTES: = a.cpp bc.cpp def.cpp
OBJS: = ao bc.o def.o

Podemos querer ter uma lista de fontes se o makefile puder construir distribuições de fontes,
e podemos precisar de uma lista de objetos para o comando link. É entediante mudar os dois
linhas sempre que um novo módulo é adicionado, e não é improvável que um programador mude
uma linha e esqueça de mudar a outra. Aqui, mostraremos quatro maneiras diferentes de evitar
a duplicação.

A função patsubst
A primeira é usar as funções do makepp para converter uma lista em outra. Uma função
a invocação se parece um pouco com uma variável, exceto que uma função pode receber argumentos:

$ (função arg1 arg2 arg3 ...)

O Makepp fornece muitas funções poderosas, mas provavelmente a mais útil delas é a
função "patsubst". Você poderia escrever as linhas acima assim:

FONTES = a.cpp bc.cpp def.cpp
OBJS = $ (patsubst% .cpp,% .o, $ (FONTES))

A função "patsubst" aplica um padrão a cada palavra em uma lista de palavras, e
realiza uma substituição textual simples. Quaisquer palavras da lista que correspondam ao padrão
no primeiro argumento são colocados na saída após fazer a substituição indicada
pelo segundo argumento. O curinga "%" corresponde a qualquer string de 0 ou mais caracteres.
Neste exemplo, o padrão "% .cpp" é aplicado a todas as palavras em "$ (SOURCES)". o
primeira palavra, "a.cpp" corresponde ao padrão e o curinga "%" corresponde à string "a".
O "%" no segundo argumento é então substituído por "a" e o resultado é "ao". Para
o segundo argumento, "%" corresponde a "bc", então o resultado é "bc.o".

As funções do Makepp podem remover nomes de diretório, remover extensões, filtrar correspondências
palavras, retorne a saída de comandos do shell e outros truques úteis. Além disso,
você também pode escrever suas próprias funções em perl que podem ser chamadas de outras partes do
o makefile. Veja makepp_extending para detalhes.

Referências de substituição
Uma vez que a função "patsubst" é tão comum, há uma sintaxe abreviada para ela
chamado de substituição referência. Poderíamos ter escrito as linhas acima assim:

FONTES = a.cpp bc.cpp def.cpp
OBJS = $ (FONTES:%. Cpp =%. O)
OBJS = $ (FONTES: .cpp = .o) # Abreviação, quando ambos começam com%.

substituição de estilo rc
Às vezes, as invocações de "patsubst" ou as referências de substituição equivalentes podem ser
um tanto enigmático. O Makepp oferece outra opção que às vezes é mais conveniente:
estilo rc substituição (assim chamado porque foi criado pelo shell rc).

MÓDULOS: = a bc def
FONTES: = $ (MÓDULOS) .cpp
OBJS: = $ (MÓDULOS) .o

O que aconteceu aqui é que quando avaliou "$ (MODULES) .cpp", makepp anexou ".cpp"
para cada palavra em "$ (MODULES)", e da mesma forma para "$ (MODULES) .o". Em geral, qualquer
caracteres anteriores a "$ (variável)" (até um delimitador de palavra) são colocados antes de cada
palavra em "$ (variável)", e quaisquer caracteres após "$ (variável)" são colocados após
cada palavra em "$ (variável)". Assim, o resultado da avaliação de "x $ (MODULES) y" seria
"xay xbcy xdefy".

Código Perl Inline
Se você conhece Perl, pode inserir código Perl para realizar manipulações arbitrárias em
variáveis ​​em seu makefile. Isso é melhor ilustrado por um exemplo:

FONTES: = a.cpp bc.cpp def.cpp
perl_begin
($ OBJS = $ SOURCES) = ~ s / \. Cpp / .o / g;
perl_end

Qualquer texto entre a instrução "perl_begin" e a instrução "perl_end" é transmitido
para o intérprete perl. Você também pode usar uma sintaxe mais perlish equivalente de "perl
{...} ". Todas as variáveis ​​no makefile (exceto variáveis ​​automáticas) são acessíveis
como escalares Perl. Quaisquer variáveis ​​que você definir com o código Perl estarão acessíveis no
arquivo make.

Então o que o exemplo acima faz é copiar o texto de $ SOURCES para $ OBJS, então
substitua cada ocorrência de ".cpp" por ".o".

Neste exemplo, usar código Perl embutido é provavelmente desnecessário, pois é mais fácil
e maneiras mais claras de fazer a mesma manipulação. Mas todo o poder do perl
um intérprete está disponível se você precisar.

Fonte / Objeto Separação e Variante constrói

Até este ponto, todos os makefiles que vimos colocam os arquivos objeto no mesmo
diretório como os arquivos de origem. Esta é geralmente a forma como os makefiles são escritos, e é
certamente a maneira mais simples de fazer as coisas. No entanto, suponha que você precise compilar seu
programa em uma máquina Linux e uma máquina Solaris. Os binários das duas máquinas
são incompatíveis, é claro. Ao contrário do make tradicional, makepp é inteligente o suficiente para saber
que se a última compilação foi no Linux, e a compilação atual está no Solaris, um
a recompilação de tudo é necessária.

Mas isso ainda deixa um problema: quando você recompila no Solaris, você apaga o seu Linux
binários. Então, quando você voltar para o Linux, terá que recompilar tudo novamente,
mesmo que os arquivos de origem não tenham mudado.

Um problema relacionado é se você construir seu programa com várias opções diferentes. Suponha
por exemplo, você geralmente compila seu programa com otimização:

CFLAGS: = -O2

% .o:% .c
$ (CC) $ (CFLAGS) -c $ (entrada) -o $ (saída)

meu_programa: * .o
$ (CC) $ (entradas) -o $ (saída)

No entanto, você descobre um bug e deseja habilitar a depuração em todos os arquivos, então
alterar "CFLAGS":

CFLAGS: = -g -DMALLOC_DEBUG

Makepp percebe que os comandos de construção mudaram e precisa recompilar
tudo. Mas, novamente, a recompilação com depuração habilitada apaga seus binários antigos, então
se você quiser reativar a otimização, tudo deve ser recompilado novamente, até mesmo o
arquivos que não foram alterados.

A solução óbvia para esses problemas é colocar o dependente de arquitetura ou
arquivos dependentes de variantes em um subdiretório separado. Existem duas técnicas básicas para
fazendo isso: especificando explicitamente um diretório alternativo ou usando repositórios.

Explícito especificações of alternado diretórios

Você poderia reescrever as regras em seu makefile para despejar os objetos em um diferente
diretório, como este:

ARCH; = $ (shell uname -s) - $ (shell uname -m) - $ (shell uname -r)
# ARCH torna-se a saída dos comandos uname.
# Às vezes as pessoas usam apenas $ (shell uname -m), mas
# será o mesmo para FreeBSD e Linux em um
# x86. O -r não é realmente útil no Linux, mas é
# importante para outros sistemas operacionais: binários para SunOS 5.8
# normalmente não funciona no SunOS 5.7. Observe o; = qual
# significa avaliar isso no máximo uma vez, quando pela primeira vez
# precisava.
CFLAGS: = -O2
OBJDIR; = $ (ARCH) -optim

$ (OBJDIR) /%. O:% .c
$ (CC) $ (CFLAGS) -c $ (entrada) -o $ (saída)

$ (OBJDIR) / meu_programa: $ (OBJDIR) / *. O
$ (CC) $ (entradas) -o $ (saída)

Agora, quando você executa o makepp, "ARCH" é automaticamente definido para algo diferente para cada
arquitetura, e todos os objetos são colocados em um diretório diferente para cada
arquitetura, para que não se sobrescrevam. Se você quiser recompilar ligando
depuração, então você teria que alterar "CFLAGS" e "OBJDIR".

Um problema com essa abordagem é que o carregamento implícito não funcionará mais. O único
lugar que o makepp sabe procurar por um makefile quando precisa construir algo está no
diretório do arquivo que está tentando construir. Se isso é um problema para você, então você pode
diga explicitamente ao makepp onde procurar usando a instrução "load_makefile".

Repositórios

Repositórios são uma maneira mágica de usar um makefile que é escrito para colocar objetos no
mesmo diretório, mas tendo makepp automaticamente colocado os objetos em um diretório diferente.
Suponha que comecemos com o makefile original acima (antes de modificá-lo para colocar o
objetos em um diretório diferente), e temos trabalhado no Linux, então nosso diretório de origem
é preenchido com binários do Linux. Quando queremos recompilar nosso código no Solaris em vez de
Linux, usamos o seguinte comando em vez de apenas digitar "makepp":

% mkdir solaris
% cd solaris
% makepp -R ..

O que a opção "-R" para makepp faz neste caso é declarar o diretório ".." (que
é o diretório de origem original) como um repositório. Um repositório é apenas uma maneira de obter
makepp para enganar todas as ações fazendo-as acreditar que todos os arquivos em uma árvore de diretório são
realmente localizado em uma árvore de diretório diferente no sistema de arquivos. No exemplo acima,
makepp finge que todos os arquivos em ".." (e todos os subdiretórios de "..") são na verdade
no diretório atual (e subdiretórios correspondentes).

Mais precisamente, um repositório é um lugar onde o makepp procura se precisa encontrar um arquivo que
não existe na árvore de diretórios atual. Se o arquivo existir no diretório atual
árvore, é usado; se não existe, mas existe um arquivo no repositório, makepp faz um
link simbólico temporário do arquivo no repositório para o diretório atual. (UMA
link simbólico é um apelido para o arquivo original. É como uma cópia, exceto que tentar
acessar o link, na verdade, acessa o arquivo original.) As ações da regra então atuam no
arquivo no diretório atual, mas realmente referencia os arquivos no repositório.

Neste exemplo, inicialmente começamos com um novo diretório em branco Solaris. (Não faz
tem que estar em branco, é claro, e não será a segunda vez que você executa o makepp.) Makepp é
executado neste diretório, e vê que não há makefile lá. No entanto, existe um
makefile no repositório, para que ele se vincule ao do repositório e o leia. o
regra de padrão no makefile que converte arquivos ".c" em arquivos ".o" faz com que o makepp
vincule todos os arquivos ".c" que ele precisa do repositório e execute o comando de compilação
do Solaris subdiretório. Portanto, os arquivos ".o" agora são colocados no Solaris
subdiretório, não no diretório de nível superior. Quando o comando de construção é concluído, qualquer
arquivos vinculados ao repositório são excluídos, portanto, o Solaris subdiretório conterá
apenas os arquivos binários para Solaris. Todos os arquivos ".o" que existem no repositório são
inalterado, então quando você volta para sua máquina Linux e executa novamente o makepp, a maior parte do seu
o programa não terá que ser recompilado.

Às vezes, pode ser mais conveniente usar uma forma diferente do comando do repositório.
Os três comandos de shell acima podem ser totalmente substituídos pelo seguinte comando:

% makepp -R solaris =. -F solaris

O que isto significa é que os arquivos no diretório atual devem ser vinculados ao
Solaris subdiretório conforme necessário. (O Solaris subdiretório será criado
automaticamente se não existir.) Então a opção "-F" faz com que o makepp faça o cd para o
Solaris diretório e execute o makefile lá (que será vinculado a partir do
repositório).

Usar um repositório não tem as mesmas desvantagens que especificar explicitamente um objeto
diretório; makefiles serão carregados implicitamente como esperado, desde que makepp seja
em questão, o makefile na verdade está no mesmo diretório que os arquivos de destino. Contudo,
se sua construção envolve não apenas uma, mas várias árvores de diretório, o uso de repositórios pode
torna-se bastante complicado.

Repositórios são apenas uma forma de fingir que as coisas localizadas em um lugar no arquivo
sistema estão, na verdade, em um lugar diferente durante a construção. Isso é muito
técnica poderosa que pode ser usada para mais do que apenas separar suas fontes e
binários. Para obter mais detalhes, consulte makepp_repositories.

depuração Makefiles
Folhas para Envie o

Se você tem um procedimento de construção complicado, você descobre que o makepp está reconstruindo as coisas mais
frequentemente do que você pensa que eles precisam ser reconstruídos. Ou você pode descobrir que não está reconstruindo
coisas quando deveria. Você não tem que ficar olhando para seus makefiles até ver o
problema. Em cada construção, makepp produz um arquivo de log que explica qual regra ele pensou
deveria ser usado para construir cada destino, quais arquivos ele achava que cada destino dependia
e (se decidiu reconstruir) por que achou que uma reconstrução era necessária. Este binário
arquivo é visto makepplog, utilitário mppl.

O formato de saída é mais ou menos autoexplicativo. Indentação transmite profundidade em makepp's
árvore de inferência. Suponha que o destino seja "todos" e "todos" dependa de "meu_programa" e
"my_program" depende de "* .o", que depende dos arquivos ".c" correspondentes. Mensagens de log
relacionados a "todos" não serão indentados, as mensagens de log relacionadas à construção do destino
"my_program" terá dois espaços recuados, mensagens de log relacionadas à construção de qualquer um dos
os arquivos de objetos terão recuo de 4 espaços e mensagens de log relacionadas à construção de qualquer um dos
os arquivos de origem terão 6 espaços recuados.

Se você estiver fazendo um make paralelo (usando a opção de linha de comando "-j"), a ordem do
mensagens no arquivo de log não farão tanto sentido, já que mensagens de diferentes
os alvos serão intercalados. Você pode tentar depurar um make serial primeiro.

comum erros in arquivos criados

Não especificando todas as dependências
Makepp é projetado para ser extremamente inteligente em encontrar dependências, e se você apenas
usar um comando de compilador Unix C ou C ++ padrão, é realmente um pouco difícil
faça o makepp perder algo. (Por favor, envie-me exemplos se você achar que falhou
algo, para que eu possa tornar o makepp mais inteligente.) No entanto, se você estiver executando outros comandos
do que a compilação, ou lidar com outras linguagens que não C ou C ++, é muito mais fácil
ter problemas.

Se você não informar ao makepp todas as dependências de um arquivo, e ele não pode inferi-las por
analisar a linha de comando ou verificar os arquivos em busca de inclusões, então não é possível reconstruir um
arquivo quando deveria. Você pode tornar esse tipo de erro menos provável usando apenas
variáveis ​​automáticas em suas ações, em vez de repetir as listas de dependências. Para
exemplo,

arquivo_combinado: abc
do_something abcd> combinado_file

tem um erro porque d é mencionado na ação, mas não na lista de dependências. Se
o comando foi escrito usando variáveis ​​automáticas como esta:

arquivo_combinado: abcd
do_something $ (inputs)> mixed_file

então, teria sido impossível cometer esse erro.

Outra maneira de ocorrer uma dependência ausente é se um programa realmente usar um arquivo
mas não leva o nome do arquivo na linha de comando. Por exemplo, se você é
compilando código Fortran, makepp no ​​momento não sabe como verificar
arquivos. Portanto, você deve listar explicitamente todos os arquivos incluídos.

Uma coisa que às vezes é útil para o teste é começar com um sistema completamente limpo
diretório - apenas o mínimo que você acha que deve ser necessário - e reconstruir
tudo do zero. Isso pode ser feito de forma mais conveniente usando repositórios,
como isso:

% rm -rf test-build-dir
% makepp -R test-build-dir =. -F test-build-dir

Se a compilação falhar porque algum arquivo não está presente, significa que o makepp não
perceber que algum arquivo era uma dependência, porque ele apenas vincula arquivos do repositório
que pensava que eram necessários. A realização deste teste ocasionalmente pode economizar horas de
depuração mais tarde. Eu trabalhei em projetos onde isso nunca foi feito por meses
porque a recompilação demorou muito. Como resultado, muitos pequenos problemas surgiram.
Havia alguns arquivos de objeto que não tinham mais arquivos de origem, alguns arquivos de origem
que nunca foram reconstruídos adequadamente por um comando de pré-processamento, etc.

Obviamente, isso não pegará todas as dependências ausentes, mas pegará algumas delas.

Não especificando todos os alvos
Você deve especificar todos os arquivos que um determinado comando modifica como alvos, ou então makepp
pode não ter percebido que eles mudaram. Você pode especificar mais de um destino. Para
exemplo,

y.tab.h y.tab.c: analisar.y
yacc -d parse.y

Se você tivesse esquecido de especificar y.tab.h como um alvo, então o makepp não saberia
reconstruir y.tab.h usando este comando, e arquivos que dependem de y.tab.h pode não ser
recompilado após a execução do comando yacc.

Por favor, sugira coisas que você achou confusas ou perigosas, e eu irei observar
ou tente consertar o makepp para que não sejam mais um perigo.

Use makepp_tutorial online usando serviços onworks.net


Servidores e estações de trabalho gratuitos

Baixar aplicativos Windows e Linux

Comandos Linux

Ad