Comportamento estranho automatizando com “make -f”

7

Eu tenho um conjunto de diretórios, alguns dos quais contêm makefiles, e alguns dos makefiles têm clean targets. No diretório pai, eu tenho um script simples:

#!/bin/bash

for f in *; do
        if [[ -d $f && -f $f/makefile ]]; then
                echo "Making clean in $f..."
                make -f $f/makefile clean
        fi
done

Isso faz uma coisa estranha quando atinge um diretório com um makefile sem um alvo "limpo" (definido). Por exemplo, considerando dois diretórios, one e two , contendo;

um / makefile

clean:
        -rm *.x

two / makefile

clean:

No segundo caso, "clean" está presente sem diretivas, por isso, se você executou "make clean" em two , receberá:

make: Nothing to be done for 'clean'.

Versus se não houver "clean":

make: *** No rule to make target 'clean'.  Stop.

No entanto, para o problema que estou prestes a descrever, o resultado é o mesmo se o destino está presente, mas não está definido ou não está presente. Executando clean.sh do diretório pai;

Making clean in one...
rm *.x
rm: cannot remove '*.x': No such file or directory
make: [clean] Error 1 (ignored)

Portanto, one não precisou de limpeza. Não é grande coisa, isso é como esperado. Mas então:

Making clean in two...
cat clean.sh >clean 
chmod a+x clean

Por que o cat clean.sh>clean etc.? Note que criei um exemplo mínimo exatamente como mostrado acima - não há outros arquivos ou diretórios ao redor (apenas clean.sh, diretórios um e dois e os makefiles mínimos). Mas depois que o clean.sh for executado, make copiou clean.sh > clean e tornou-o executável. Se eu executar o clean.sh novamente:

Making clean in one...
make: 'clean' is up to date.
Making clean in two...
make: 'clean' is up to date.
Press any key to continue...

Algo ainda mais estranho, porque agora ele não está usando os arquivos de criação especificados - está usando um alvo misterioso "atualizado".

Eu notei um fenômeno talvez relacionado: se remover uma das cláusulas de teste em clean.sh assim:

#       if [[ -d $f && -f $f/makefile ]]; then
        if [[ -d $f ]]; then

E crie um diretório three sem makefile, parte da saída inclui:

Making clean in three...
make: three/makefile: No such file or directory
make: *** No rule to make target 'three/makefile'.  Stop.

Não existe tal arquivo ou diretório que eu entenda, mas por que make então procura por um alvo com esse nome? A página man parece bem direta:

-f file, --file=file, --makefile=FILE

  Use file as a makefile.
    
por goldilocks 09.08.2013 / 22:04

1 resposta

9

Esse comportamento não é um erro. É uma característica. Um recurso e um possível erro do usuário para ser preciso.

O recurso em questão é uma das regras implícitas do Make. No seu caso, a regra implícita para "construir" arquivos *.sh . O erro do usuário, seu erro, não está alterando o diretório de trabalho antes de invocar o makefile nos subdiretórios.

TL; DR: para corrigir isso, você pode fazer um ou mais dos seguintes procedimentos:

  1. Corrija o script de shell para alterar o diretório de trabalho:

    #!/bin/bash
    
    for f in *; do
            if [[ -d $f && -f $f/makefile ]]; then
                    echo "Making clean in $f..."
                    (cd $f; make clean)
            fi
    done
    
  2. Torne as regras vazias explícitas:

    clean: ;
    
  3. Torne os alvos clean falsos:

    .PHONY: clean
    

Explicação detalhada:

Make tem várias regras implícitas. Isso permite invocar make em projetos simples sem escrever um makefile.

Tente isso para uma demonstração:

  1. crie um diretório vazio e mude para o diretório
  2. crie um arquivo chamado clean.sh .
  3. executar make clean

Saída:

$ make clean
cat clean.sh >clean 
chmod a+x clean

BAM! Esse é o poder das regras implícitas de make. Consulte o manual sobre as regras implícitas para obter mais informações.

Tentarei responder à pergunta aberta restante:

Por que ele não invoca a regra implícita para o primeiro makefile? Como você substituiu a regra implícita pela regra clean explícita.

Por que a regra clean no segundo makefile não sobrescreve a regra implícita? Porque não tinha receita. As regras sem receita não sobrescrevem as regras implícitas, em vez disso, apenas acrescentam pré-requisitos. Consulte o manual sobre várias regras para obter mais informações. Veja também o manual sobre regras com receitas vazias explícitas .

Por que é um erro não alterar o diretório de trabalho antes de invocar um makefile em um subdiretório? Porque make não altera o diretório de trabalho. Make funcionará no diretório de trabalho herdado. Bem, tecnicamente, isso não é necessariamente um erro, mas na maioria das vezes é. Você deseja que os makefiles nos subdiretórios funcionem nos subdiretórios? Ou você quer que eles trabalhem no diretório pai?

Por que ignorar a regra clean explícita do primeiro makefile na segunda invocação de clean.sh ? Porque agora o arquivo de destino clean já existe. Como a regra clean não tem pré-requisitos, não é necessário reconstruir o destino. Consulte o manual sobre os destinos falsos , que descreve exatamente esse problema.

Por que a busca pelo alvo three/makefile na terceira invocação? Porque o make sempre tenta refazer os makefiles antes de fazer qualquer outra coisa. Isso é especialmente verdadeiro se o makefile for solicitado explicitamente usando -f , mas não existe. Consulte o manual sobre como refazer os makefiles para obter mais informações.

    
por 10.08.2013 / 00:06

Tags