Definir tornar variável no tempo de execução da regra

209

No meu GNUmakefile, eu gostaria de ter uma regra que use um diretório temporário. Por exemplo:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

Conforme escrito, a regra acima cria o diretório temporário no momento em que a regra é analisada . Isso significa que, mesmo eu não entendo.tar o tempo todo, muitos diretórios temporários são criados. Eu gostaria de evitar que meu / tmp esteja repleto de diretórios temporários não utilizados.

Existe uma maneira de fazer com que a variável seja definida apenas quando a regra é acionada, em vez de sempre que é definida?

Meu pensamento principal é despejar o mktemp e o tar em um script de shell, mas isso parece um pouco desagradável.

Emil Sit
fonte

Respostas:

322

No seu exemplo, a TMPvariável é configurada (e o diretório temporário criado) sempre que as regras para out.tarsão avaliadas. Para criar o diretório apenas quando ele out.tarrealmente é acionado, é necessário mover a criação do diretório para as etapas:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

A função eval avalia uma string como se tivesse sido digitada no makefile manualmente. Nesse caso, define a TMPvariável para o resultado da shellchamada de função.

editar (em resposta a comentários):

Para criar uma variável exclusiva, você pode fazer o seguinte:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

Isso incluiria o nome do destino (out.tar, neste caso) na variável, produzindo uma variável com o nome out.tar_TMP. Felizmente, isso é suficiente para evitar conflitos.

e.James
fonte
2
Esclareça alguns esclarecimentos ... isso não fará com que o TMP alcance esse objetivo, certo? Portanto, se houver outras regras que tenham seu próprio uso de $ (TMP) (possivelmente em paralelo com -j), poderá haver conflitos? Além disso, o @echo é mesmo necessário? Parece que você pode simplesmente deixar de fora.
Emil Sit
3
Parece fazer o truque (embora um pouco opaco para o guru médio não-Make :-) Obrigado!
Emil Sit
29
Tenha cuidado com esta solução! $(eval $@_TMP := $(shell mktemp -d))acontecerá quando o Makefile for avaliado pela primeira vez, não na ordem dos procedimentos da regra. Em outras palavras, $(eval ...)acontece mais cedo do que você pensa. Embora isso possa ser bom para este exemplo, esse método causará problemas para algumas operações seqüenciais.
JamesThomasMoon1979
1
@ JamesThomasMoon1979 você sabe como superar essas limitações, por exemplo, avaliar algumas variáveis ​​que devem resultar de etapas executadas anteriormente na regra?
Vadim Kotov
2
Respondendo à minha própria pergunta: a solução alternativa para mim foi criar arquivos durante a execução da 1ª regra e tentar avaliá-los na primeira etapa da 2ª regra.
Vadim Kotov
63

Uma maneira relativamente fácil de fazer isso é escrever a sequência inteira como um script de shell.

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\

Consolidei algumas dicas relacionadas aqui: https://stackoverflow.com/a/29085684/86967

nobar
fonte
3
Esta é definitivamente a resposta mais simples e, portanto, melhor (evitando @e evale fazendo o mesmo trabalho). Observe que em sua saída você vê $TMP(por exemplo tar -C $TMP ...), embora o valor seja passado corretamente para o comando.
Karl Richter
É assim que é feito normalmente; Espero que as pessoas vejam essa resposta, porque, na prática, ninguém passa por todo o esforço da pessoa aceita.
pmos
E se você estiver usando comandos específicos do shell? Nesse caso, os comandos do shell devem estar na mesma linguagem do shell que a chamada make, certo? Eu já tinha visto alguns makefile com shebang.
Ptitpion
@ptitpion: Você também pode querer SHELL := /bin/bashem seu makefile ativar recursos específicos do BASH.
Nobar
31

Outra possibilidade é usar linhas separadas para configurar as variáveis ​​Make quando uma regra é acionada.

Por exemplo, aqui está um makefile com duas regras. Se uma regra for acionada, ela criará um diretório dir e definirá TMP para o nome do diretório temp.

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

A execução do código produz o resultado esperado:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow
user3124434
fonte
Apenas como lembrete, o GNU make possui uma sintaxe para pré-requisitos somente para pedidos, que podem ser necessários para complementar essa abordagem.
anol
11
Tenha cuidado com esta solução! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)e ruleB: TMP = $(shell mktemp -d testruleB_XXXX)acontecerá quando o Makefile for avaliado pela primeira vez. Em outras palavras, ruleA: TMP = $(shell ...acontece mais cedo do que você pensa. Embora isso possa funcionar para esse caso específico, esse método causará problemas para algumas operações seqüenciais.
JamesThomasMoon1979
1

Não gosto das respostas "Não", mas ... não.

makeAs variáveis ​​de são globais e devem ser avaliadas durante o estágio de "análise" do makefile, não durante o estágio de execução.

Nesse caso, desde que a variável local para um único destino, siga a resposta do @ nobar e torne-a uma variável shell.

Variáveis ​​específicas de destino também são consideradas prejudiciais por outras implementações do make: kati , Mozilla pymake . Por causa deles, um destino pode ser criado de maneira diferente, dependendo de ser construído de forma independente ou como uma dependência de um destino pai com uma variável específica do destino. E você não saberá como foi , porque não sabe o que já foi construído.

Victor Sergienko
fonte