Como você força um makefile a reconstruir um destino


184

Eu tenho um makefile que cria e depois chama outro makefile. Como esse makefile chama mais makefiles que fazem o trabalho, ele realmente não muda. Assim, continua pensando que o projeto foi construído e atualizado.

dnetdev11 ~ # make
make: `release' is up to date.

Como forço o makefile a reconstruir o destino?

clean = $(MAKE) -f ~/xxx/xxx_compile.workspace.mak clean


build = svn up ~/xxx                                                       \
        $(clean)                                                                \
        ~/cbp2mak/cbp2mak -C ~/xxx ~/xxx/xxx_compile.workspace        \
        $(MAKE) -f ~/xxx/xxx_compile.workspace.mak $(1)                    \


release:
        $(build )

debug:
        $(build DEBUG=1)

clean:
        $(clean)

install:
        cp ~/xxx/source/xxx_utility/release/xxx_util /usr/local/bin
        cp ~/xxx/source/xxx_utility/release/xxxcore.so /usr/local/lib

Nota: Nomes removidos para proteger os inocentes

Editar: Versão final fixa:

clean = $(MAKE) -f xxx_compile.workspace.mak clean;


build = svn up;                                         \
        $(clean)                                        \
        ./cbp2mak/cbp2mak -C . xxx_compile.workspace;   \
        $(MAKE) -f xxx_compile.workspace.mak    $(1);   \


.PHONY: release debug clean install

release:
        $(call build,)

debug:
        $(call build,DEBUG=1)

clean:
        $(clean)

install:
        cp ./source/xxx_utillity/release/xxx_util /usr/bin
        cp ./dlls/Release/xxxcore.so /usr/lib

Lodle, como essa é uma pergunta frequentemente visitada, você gostaria de editar a pergunta para ser mais moderna? (Parece que .PHONYnão era o seu único problema, e você não está realmente deveria editar a solução para a questão, ou pelo menos não mais.)
Keith M

Respostas:


23

Você pode declarar que um ou mais de seus alvos são falsos .

Um alvo falso é aquele que não é realmente o nome de um arquivo; é apenas um nome para que uma receita seja executada quando você faz uma solicitação explícita. Há duas razões para usar um alvo falso: evitar um conflito com um arquivo com o mesmo nome e melhorar o desempenho.

...

Um alvo falso não deve ser um pré-requisito para um arquivo de destino real; se for, sua receita será executada toda vez que o make atualizar o arquivo. Desde que um alvo falso nunca seja um pré-requisito de um alvo real, a receita do alvo falso será executada apenas quando o alvo falso for um objetivo especificado


68
Esta resposta, apesar de ser "aceita" e altamente "votada", é realmente errada. Primeiro, ele diz "declarar alvos falsos", mas depois diz "destino falso não é realmente o nome de um arquivo". Bem, se seu alvo é um arquivo, isso é uma contradição na resposta. Segundo, ele diz que "alvo falso não deve ser um pré-requisito de um real" - bem, e se for? A pergunta original, não especificou se é ou não é. A resposta correta é: não declarar que seus alvos são falsos, mas sim, declarar um alvo falso adicional e, então, depender dos alvos que você deseja reconstruir.
precisa saber é o seguinte

2
@MarkGaleck. Quando a resposta afirma que "Um alvo falso é aquele que não é realmente o nome de um arquivo", ele é citado diretamente no manual do gcc make. Está perfeitamente correto.
Drlolly 14/05

"Destino" é um termo Make que se refere ao texto à esquerda de dois pontos :, não apenas ao resultado final que você deseja criar (por exemplo, seu arquivo binário). Na pergunta, release, debug, clean, e installsão os alvos marca, não xxx_utilou xxxcore.soou qualquer outra coisa.
Keith M

728

A -Bopção de fazer, cuja forma longa é --always-make, indica makepara desconsiderar os carimbos de data e hora e fazer os destinos especificados. Isso pode anular o objetivo de usar o make, mas pode ser o que você precisa.


4
@MarkKCowan Concordo plenamente! Essas opções são exatamente o que eu estava procurando, não uma solução alternativa, como sugere Dave.
Maarten Bamelis

8
A ressalva dessa abordagem é que ela apenas cria muitas coisas. Especificamente com as ferramentas automáticas, eu o vi executando novamente a configuração. Desejo que uma solução baseada em LD_PRELOAD possa ser criada!
vrdhn

sim, e pode até reescrever arquivos que você não queria! tais como bibliotecas globais do sistema que aparecem nas dependências e são reconstruídas e substituído ...
Julio Guerra

18

Um truque que costumava ser documentado em um manual da Sun makeé usar um destino (inexistente) '.FORCE'. Você pode fazer isso criando um arquivo, force.mk, que contém:

.FORCE:
$(FORCE_DEPS): .FORCE

Supondo que seu makefile existente seja chamado makefile, você pode executar:

make FORCE_DEPS=release -f force.mk -f makefile release

Como .FORCEnão existe, tudo o que depende será desatualizado e reconstruído.

Tudo isso funcionará com qualquer versão do make; no Linux, você possui o GNU Make e, portanto, pode usar o destino .PHONY conforme discutido.

Também vale a pena considerar por que o makerelease está atualizado. Isso pode ocorrer porque você possui um touch releasecomando entre os comandos executados; pode ser porque existe um arquivo ou diretório chamado 'release' que existe e não tem dependências e, portanto, está atualizado. Depois, há a verdadeira razão ...


14

Alguém sugeriu .PHONY, o que é definitivamente correto. .PHONY deve ser usado para qualquer regra para a qual uma comparação de datas entre a entrada e a saída é inválida. Como você não possui nenhum alvo no formulário output: input, use .PHONY para TODOS eles!

Tudo isso dito, você provavelmente deve definir algumas variáveis ​​na parte superior do seu makefile para os vários nomes de arquivos e definir regras reais de make que possuem seções de entrada e saída para que você possa usar os benefícios do make, ou seja, que você só irá compilar coisas que são necessárias para copmile!

Editar: exemplo adicionado. Não testado, mas é assim que você faz .PHONY

.PHONY: clean    
clean:
    $(clean)

1
Bem, se você pudesse me mostrar um exemplo, seria bom. Atm im apenas cortando-lo de tentar obter a coisa barragem para o trabalho: P
Lodle

1
A localização do .PHONYalvo não importa. Pode estar em qualquer lugar do Makefile.
Adrian W

5

Se bem me lembro, 'make' usa carimbos de data e hora (hora da modificação do arquivo) para determinar se um destino está ou não atualizado. Uma maneira comum de forçar uma recriação é atualizar esse carimbo de data / hora, usando o comando 'touch'. Você pode tentar chamar 'touch' no seu makefile para atualizar o registro de data e hora de um dos destinos (talvez um desses sub-makefiles), o que pode forçar o Make a executar esse comando.


5

Essa técnica simples permitirá que o makefile funcione normalmente quando forçar não é desejado. Crie um novo alvo chamado force no final do seu makefile . O alvo de força tocará em um arquivo do qual seu alvo padrão depende. No exemplo abaixo, adicionei touch myprogram.cpp . Também adicionei uma ligação recursiva a fazer . Isso fará com que o alvo padrão seja criado toda vez que você digita make force .

yourProgram: yourProgram.cpp
       g++ -o yourProgram yourProgram.cpp 

force:
       touch yourProgram.cpp
       make

Você nunca deve usar makedentro de um Makefile. Use em $(MAKE)vez disso.
Benjamin Crawford Ctrl-Alt-Tut

3

Eu tentei isso e funcionou para mim

adicione estas linhas ao Makefile

clean:
    rm *.o output

new: clean
    $(MAKE)     #use variable $(MAKE) instead of make to get recursive make calls

salve e ligue agora

make new 

e recompilará tudo novamente

O que aconteceu?

1) 'novas' chamadas limpas. 'clean' do 'rm', que remove todos os arquivos de objeto com a extensão '.o'.

2) 'novas' chamadas 'fazem'. 'make' veja que não há arquivos '.o', então ele cria todo o '.o' novamente. o vinculador vincula todo o arquivo .o em uma saída executável

Boa sorte


1
Em receita para newmelhor uso do $(MAKE)quemake
Basile Starynkevitch 29/11

1

De acordo com a Recursiva de Miller Tornar Considerável Prejudicial, você deve evitar ligar $(MAKE)! No caso de você mostrar, é inofensivo, porque esse não é realmente um makefile, apenas um script de wrapper, que poderia muito bem ter sido escrito no Shell. Mas você diz que continua assim em níveis de recursão mais profundos, e provavelmente encontrou os problemas mostrados nesse ensaio revelador.

Obviamente, com o GNU, é difícil evitar isso. E mesmo estando cientes desse problema, é a maneira documentada de fazer as coisas.

OTOH, makepp foi criado como uma solução para esse problema. Você pode escrever seus makefiles em um nível por diretório, mas todos eles são atraídos para uma visualização completa do seu projeto.

Mas makefiles legados são escritos recursivamente. Portanto, há uma solução alternativa em $(MAKE)que nada faz além de canalizar as sub-solicitações de volta ao processo principal do makepp. Somente se você fizer coisas redundantes ou, pior, contraditórias entre seus submakes, deverá solicitar --traditional-recursive-make(o que obviamente quebra essa vantagem do makepp). Eu não conheço seus outros makefiles, mas se eles estiverem escritos de maneira limpa, com o makepp, as reconstruções necessárias deverão ocorrer automaticamente, sem a necessidade de hacks sugeridos aqui por outras pessoas.


Não respondeu à pergunta: tangente ao ponto principal e deve ser um comentário, não uma resposta.
flungo

Talvez eu não tenha sido claro o suficiente. Com o makepp, esse arquivo de invólucro inteiro não é necessário. Ao conhecer as dependências exatas (todas elas, não apenas as listadas após a :), ela sempre será reconstruída quando necessário.
Daniel

1

Se você não precisar preservar nenhuma das saídas que você já compilou com sucesso

nmake /A 

reconstrói tudo


0

Na verdade, depende de qual é o alvo. Se for um alvo falso (ou seja, o alvo NÃO está relacionado a um arquivo), você deve declará-lo como .PHONY.

Se, no entanto, o destino não for um destino falso, mas você apenas deseja reconstruí-lo por algum motivo (um exemplo é quando você usa a macro de pré-processamento __TIME__), você deve usar o esquema FORCE descrito nas respostas aqui.



0

Já foi mencionado, mas achei que poderia adicionar ao uso touch

Se touchtodos os arquivos de origem a serem compilados, o touchcomando alterará os carimbos de data e hora de um arquivo para a hora em que o touchcomando foi executado.

O registro de data e hora do arquivo de origem é o que é makeusado para "saber" que um arquivo foi alterado e precisa ser recompilado

Por exemplo: Se o projeto era um projeto c ++, execute touch *.cpp, execute makenovamente e make deve recompilar o projeto inteiro.


0

Como Abernier apontou, há uma solução recomendada no manual de criação do GNU, que usa um alvo 'falso' para forçar a reconstrução de um destino:

clean: FORCE
        rm $(objects)
FORCE: ; 

Isso funcionará limpo, independentemente de outras dependências.

Eu adicionei o ponto e vírgula à solução do manual, caso contrário, uma linha vazia é necessária.


-1

No meu sistema Linux (Centos 6.2), há uma diferença significativa entre declarar o destino .PHONY e criar uma dependência falsa do FORCE, quando a regra realmente cria um arquivo correspondente ao destino. Quando o arquivo deve ser regenerado todas as vezes, é necessária a dependência falsa FORCE no arquivo e .PHONY para a dependência falsa.

errado:

date > $@

certo:

FORCE
    date > $@
FORCE:
    .PHONY: FORCE

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.