Qual é o objetivo de .PHONY em um makefile?

1739

O que .PHONYsignifica em um Makefile? Eu já passei por isso , mas é muito complicado.

Alguém pode me explicar em termos simples?

lazer
fonte

Respostas:

1947

Por padrão, os destinos do Makefile são "destinos de arquivos" - são usados ​​para criar arquivos de outros arquivos. Make assume que seu destino é um arquivo, e isso facilita a escrita de Makefiles:

foo: bar
  create_one_from_the_other foo bar

No entanto, às vezes você deseja que o Makefile execute comandos que não representam arquivos físicos no sistema de arquivos. Bons exemplos para isso são os alvos comuns "limpo" e "todos". Provavelmente, esse não é o caso, mas você pode ter um arquivo com o nome cleanno diretório principal. Nesse caso, o Make ficará confuso porque, por padrão, o cleandestino estaria associado a esse arquivo e o Make só o executará quando o arquivo não parecer atualizado com relação às dependências.

Esses alvos especiais são chamados de falsos e você pode dizer explicitamente que eles não estão associados aos arquivos, por exemplo:

.PHONY: clean
clean:
  rm -rf *.o

Agora make cleanserá executado conforme o esperado, mesmo se você tiver um arquivo chamado clean.

Em termos de Make, um destino falso é simplesmente um destino sempre desatualizado; portanto, sempre que você solicitar make <phony_target>, ele será executado independentemente do estado do sistema de arquivos. Alguns comuns makealvos que estão muitas vezes falsa são: all, install, clean, distclean, TAGS, info, check.

Eli Bendersky
fonte
49
@eSKay: 'por que é chamado de' falso '?' - porque não é um alvo real. Ou seja, o nome do destino não é um arquivo produzido pelos comandos desse destino.
Bernard
96
@Lazer: Não sei se você é um falante nativo de inglês. Eu não estou. a palavra falso não significa o que parece. en.wiktionary.org/wiki/phony diz: Fraudulento; falso; tendo uma aparência enganosa.
Bahbar 26/08/10
57
Esta resposta não está exatamente completa - embora possa ser abordada no tutorial vinculado. .PHONY força a criação de um rótulo / arquivo em um Makefile, se fizer parte do tipo topológico de qualquer que seja o seu destino. Ou seja, se você tiver um rótulo 'cleanup:' definido como falso, e o rótulo de instalação estiver definido com cleanup como um pré-requisito - ou seja, 'install: cleanup', a limpeza sempre será executada quando o Makefile tentar criar 'instalar'. Isso é útil para as etapas que você sempre deseja que sejam executadas, independentemente de serem bem-sucedidas - ele ignora os carimbos de data e hora e apenas o força.
Sintetizadorpatel
9
Observe que você não precisa usar .PHONY desde que não tenha um arquivo com o mesmo nome da tarefa. A tarefa sempre será executada de qualquer maneira, e o Makefile ficará mais legível.
22716 Bernard
15
"um alvo falso é simplesmente um alvo sempre desatualizado" - ótima explicação!
Danijel
730

Vamos supor que você tenha um installalvo, o que é muito comum em makefiles. Se você não usar .PHONY, e um arquivo nomeado installexistir no mesmo diretório que o Makefile, make installnão fará nada . Isso ocorre porque Make interpreta a regra como "execute tal e qual receita para criar o arquivo chamado install". Como o arquivo já está lá e suas dependências não foram alteradas, nada será feito.

No entanto, se você criar o installalvo PHONY, ele informará à ferramenta make que o alvo é fictício, e esse make não deve esperar que ele crie o arquivo real. Portanto, ele não verifica se o installarquivo existe, o que significa: a) seu comportamento não será alterado se o arquivo existir eb) extra stat()não será chamado.

Geralmente todos os alvos em seu Makefile que não produzem um arquivo de saída com o mesmo nome que o nome do alvo devem ser PHONY. Isso normalmente inclui all, install, clean, distclean, e assim por diante.

George Y.
fonte
6
@PineappleUndertheSea A resposta aceita foi aprimorada significativamente a partir do seu nível inicial de inutilidade, e agora é tão boa quanto esta. Eu tive que examinar seu histórico de revisões para entender seu comentário.
Mark Amery
2
Isso parece meio inútil, pois nunca terei arquivos chamados 'install' ou similares na minha base de código. A maioria dos arquivos terá uma extensão de arquivo, e os arquivos sem extensão geralmente estão em maiúsculas, como 'README'. Por outro lado, se você tiver um script bash chamado 'install' em vez de 'install.sh', terá um mau momento.
Nucleartide
6
@ JasonTu Isso não é necessariamente verdade. As convenções de script do Bash solicitam que você omita a extensão .shou .bashdos "programas" executados como se tivessem uma função principal e reserve adicionar uma extensão para as bibliotecas que você inclui ( source mylib.sh). Na verdade, eu comecei a esta pergunta SO porque eu tinha um script no mesmo diretório como o meu Makefile chamadoinstall
Kyle
10
@ Kyle Sim, eu não tenho certeza do que meu eu passado significava. Estes dias eu uso .PHONYo tempo todo ...
nucleartide
2
@JasonTu A solução aqui é simples: construir uma máquina do tempo e "substituir" o seu eu passado. Eu recomendo levar uma pá junto com você para que ninguém perceba que você é a .PHONYversão.
Mateen Ulhaq
120

NOTA : A ferramenta make lê o makefile e verifica os carimbos de data / hora da modificação dos arquivos ao lado do símbolo ':' em uma regra.

Exemplo

Em um diretório 'test', os seguintes arquivos estão presentes:

prerit@vvdn105:~/test$ ls
hello  hello.c  makefile

No makefile, uma regra é definida da seguinte maneira:

hello:hello.c
    cc hello.c -o hello

Agora, suponha que o arquivo 'hello' seja um arquivo de texto que contenha alguns dados, que foram criados após o arquivo 'hello.c'. Portanto, o carimbo de data / hora de modificação (ou criação) de 'hello' será mais novo que o de 'hello.c'. Portanto, quando chamaremos 'make hello' na linha de comando, ela será impressa como:

make: `hello' is up to date.

Agora acesse o arquivo 'hello.c' e coloque alguns espaços em branco, o que não afeta a sintaxe ou a lógica do código, e salve e saia. Agora, o carimbo de data / hora da modificação do hello.c é mais recente que o do 'hello'. Agora, se você chamar 'make hello', ele executará os comandos como:

cc hello.c -o hello

E o arquivo 'olá' (arquivo de texto) será substituído por um novo arquivo binário 'olá' (resultado do comando de compilação acima).

Se usarmos .PHONY no makefile da seguinte maneira:

.PHONY:hello

hello:hello.c
    cc hello.c -o hello

e depois invoque 'make hello', ele ignorará qualquer arquivo presente no pwd 'test' e executará o comando sempre.

Agora, suponha que o destino 'hello' não tenha dependências declaradas:

hello:
    cc hello.c -o hello

e o arquivo 'hello' já estiver presente no pwd 'test', então 'make hello' sempre será exibido como:

make: `hello' is up to date.
Prerit Jain
fonte
3
Isso não apenas faz sentido os comandos que eu corro, makecomo finalmente faz com que o todo faça sentido, é tudo sobre os arquivos! Obrigado por esta resposta.
Kzqai
80
.PHONY: install
  • significa que a palavra "instalar" não representa um nome de arquivo neste Makefile;
  • significa que o Makefile não tem nada a ver com um arquivo chamado "install" no mesmo diretório.
YourBestBet
fonte
45

É um destino de construção que não é um nome de arquivo.

JohnMcG
fonte
33

A melhor explicação é o próprio manual do GNU make: 4.6 Seção de alvos falsos .

.PHONYé um dos nomes de destino internos especiais do make . Você pode estar interessado em outros alvos, por isso vale a pena examinar essas referências.

Quando chegar a hora de considerar um destino .PHONY, o make executará sua receita incondicionalmente, independentemente de existir um arquivo com esse nome ou qual seja a hora da última modificação.

Você também pode estar interessado nos destinos padrão da marca , como alle clean.

James Wald
fonte
11

Há também um tratamento complicado e importante de ".PHONY" - quando um alvo físico depende de um alvo falso que depende de outro alvo físico:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Você simplesmente esperaria que, se atualizasse o TARGET2, o TARGET1 fosse considerado obsoleto contra o TARGET1, portanto, o TARGET1 deve ser reconstruído. E realmente funciona dessa maneira .

A parte complicada é quando o TARGET2 não está obsoleto contra o TARGET1 - nesse caso, você deve esperar que o TARGET1 não deva ser reconstruído.

Surpreendentemente, isso não funciona porque: o alvo falso foi executado de qualquer maneira (como os destinos falsos normalmente fazem) , o que significa que o alvo falso foi considerado atualizado . E por causa disso, o TARGET1 é considerado obsoleto contra o alvo falso .

Considerar:

all: fileall

fileall: file2 filefwd
    echo file2 file1 >fileall


file2: file2.src
    echo file2.src >file2

file1: file1.src
    echo file1.src >file1
    echo file1.src >>file1

.PHONY: filefwd
.PHONY: filefwd2

filefwd: filefwd2

filefwd2: file1
    @echo "Produced target file1"


prepare:
    echo "Some text 1" >> file1.src
    echo "Some text 2" >> file2.src

Você pode brincar com isso:

  • primeiro faça 'make prepare' para preparar os "arquivos de origem"
  • brinque com isso tocando em arquivos específicos para vê-los atualizados

Você pode ver que o arquivo filiall depende indiretamente do arquivo1 através de um destino falso - mas ele sempre é reconstruído devido a essa dependência. Se você alterar a dependência fileallde filefwdpara file, agora fileallnão será reconstruído todas as vezes, mas apenas quando algum dos destinos dependentes estiver obsoleto como um arquivo.

Ethouris
fonte
5

O alvo especial .PHONY:permite declarar alvos falsos, para que makenão os verifique como nomes de arquivo reais: funcionará o tempo todo, mesmo que esses arquivos ainda existam.

Você pode colocar vários .PHONY:em seu Makefile:

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

Existe outra maneira de declarar alvos falsos: basta colocar '::'

all :: prog1 prog2

...

clean ::
    ...
distclean ::
    ...

O '::' tem um significado especial: os alvos são falsos e podem aparecer várias vezes:

clean ::
    rm file1

...


clean ::
    rm file2

Os blocos de comando serão chamados um após o outro.

Edouard Thiel
fonte
3

Costumo usá-los para dizer ao alvo padrão para não disparar.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Sem FALSO, make supercleaniria disparar clean, andsomethingelsee catcher superclean; mas com PHONY, make supercleannão vai disparar o catcher superclean.

Não precisamos nos preocupar em dizer que o cleanalvo é PHONY, porque não é completamente falso. Embora nunca produza o arquivo limpo, ele possui comandos para acionar, portanto, o make pensará que é um alvo final.

No entanto, o supercleanalvo é realmente falsa, então make vai tentar empilhar-lo com qualquer outra coisa que fornece deps para o supercleanalvo - o que inclui outras supercleanmetas e o% alvo.

Note que não dizemos nada sobre andsomethingelseou blah, então eles claramente vão para o coletor.

A saída é mais ou menos assim:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
jettero
fonte