ルール実行時に変数の作成を定義する


208

私のGNUmakefileで、一時ディレクトリーを使用するルールが欲しいのですが。例えば:

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

前述のとおり、上記のルールは、ルールが解析されるときに一時ディレクトリを作成します。つまり、常にout.tarを作成しなくても、多くの一時ディレクトリが作成されます。/ tmpが未使用の一時ディレクトリで散らかされるのを避けたいのですが。

変数が定義されるたびにではなく、ルールが実行されたときにのみ変数が定義されるようにする方法はありますか?

私の主な考えは、mktempとtarをシェルスクリプトにダンプすることですが、それは少し見苦しいようです。

回答:


321

あなたの例では、TMPいつでも変数が設定されている(と、一時ディレクトリが作成された)ルールがためにout.tar評価されます。out.tarが実際に起動されたときにのみディレクトリを作成するには、ディレクトリの作成を次の手順に移動する必要があります。

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

evalのそれは手動でメイクファイルに入力されたかのように関数は、文字列を評価します。この場合、TMP変数をshell関数呼び出しの結果に設定します。

編集(コメントに応じて):

一意の変数を作成するには、次のようにします。

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

これにより、ターゲットの名前(この場合はout.tar)が変数の前に追加され、という名前の変数が生成されますout.tar_TMP。うまくいけば、それは衝突を防ぐのに十分です。


2
いくつかの説明を冷却してください...これはTMPをこのターゲットにスコープしません、でしょうか?したがって、独自の$(TMP)使用法(おそらく-jと並行して)を持つ他のルールがある場合、競合が発生する可能性がありますか?また、@ echoも必要ですか?あなたはそれを省くことができるようです。
Emil Sit

3
トリックを行うようです(Make以外の平均的なグルに対しては少し不透明ですが:-)ありがとうございます。
Emil Sit

29
この解決策に注意してください! $(eval $@_TMP := $(shell mktemp -d))Makefileが最初に評価されたときに、ルール手順の順序ではなく発生します。つまり、$(eval ...)あなたが思っているよりも早く起こります。この例では問題ないかもしれませんが、この方法では、いくつかの順次操作で問題が発生します。
JamesThomasMoon1979 2015年

1
@ JamesThomasMoon1979これらの制限を克服する方法を知っていますか。たとえば、ルールの前半で実行されたステップの結果であるはずのいくつかの変数を評価しますか?
Vadim Kotov 2017年

2
私自身の質問への回答:私の回避策は、1番目のルールの実行中にファイルを作成し、2番目のルールの最初のステップでそれらを評価しようとすることでした。
Vadim Kotov 2017年

63

これを行う比較的簡単な方法は、シーケンス全体をシェルスクリプトとして書き込むことです。

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

ここにいくつかの関連するヒントをまとめました:https : //stackoverflow.com/a/29085684/86967


3
これは間違いなく、最も簡単なので、最高の答え(回避である@eval、同じ仕事をして)。値は正しくコマンドに渡されますが、出力には$TMP(例tar -C $TMP ...:)が表示されることに注意してください。
カールリヒター

これが通常行われる方法です。実際には受け入れられたものの努力を誰も経験しないので、人々がこの答えを見てくれることを願っています。
pmos

シェル固有のコマンドを使用している場合はどうなりますか?その場合、シェルコマンドはmakeを呼び出すものと同じシェル言語にする必要がありますよね?私はシバンのあるmakefileを見ました。
ptitpion

@ptitpion:SHELL := /bin/bashmakefileでBASH固有の機能を有効にすることもできます。
nobar 2018

31

もう1つの可能性は、ルールが実行されたときに個別の行を使用してMake変数を設定することです。

たとえば、以下は2つのルールを持つメイクファイルです。ルールが起動すると、一時ディレクトリが作成され、TMPが一時ディレクトリ名に設定されます。

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}

コードを実行すると、予期した結果が生成されます。

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow

念のため、GNU makeには、このアプローチを補完するために必要な場合がある順序のみの前提条件の構文があります。
アルコール:2014年

11
この解決策に注意してください! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)そして、ruleB: TMP = $(shell mktemp -d testruleB_XXXX)のMakefileが最初に評価されたときに発生します。つまり、ruleA: TMP = $(shell ...あなたが思っているよりも早く起こります。これはこの特定のケースではうまくいくかもしれませんが、この方法はいくつかの順次操作で問題を引き起こします。
JamesThomasMoon1979 2015年

1

「しない」の答えは嫌いですが...しないでください。

makeの変数はグローバルであり、実行段階ではなく、メイクファイルの「解析」段階で評価されることになっています。

この場合、変数が単一のターゲットに対してローカルである限り、@ nobarの回答に従い、それをシェル変数にします。

ターゲット固有の変数も、他のmake実装(katiMozilla pymake)によって有害で​​あると見なされます。そのため、スタンドアロンでビルドされているか、ターゲット固有の変数を持つ親ターゲットの依存関係としてビルドされているかによって、ターゲットのビルド方法が異なります。そして、何が既に構築されているのかわからないので、あなたはそれがどのようであったかを知りません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.