出力リダイレクトによってサイズがゼロのファイルが生成されるため、Makeはルールを成功と誤って見なします。

出力リダイレクトによってサイズがゼロのファイルが生成されるため、Makeはルールを成功と誤って見なします。

makefile には次のような規則がいくつかあります。

out.txt: foo.sh input.txt
  ./foo.sh -i input.txt > out.txt

foo.sh失敗すると、out.txtサイズ0のファイルが生成されます。走れば作るout.txt今回もファイルが正常に作成されたと誤って判断し、ルールを再実行しません。

この状況を処理する正しい方法は何ですか?

答え1

ルールが失敗した場合は、以下を定義してターゲットファイルの削除を要求できます。特別な目標名前付き.DELETE_ON_ERROR。何もする必要はなく、依存関係は必要ないので、makefileに追加するだけです。

.DELETE_ON_ERROR:

これにより、次のようになります。

$ cat Makefile
.DELETE_ON_ERROR:
foo:
    false > foo

$ make
false > foo
make: *** [foo] Error 1
make: *** Deleting file `foo'
zsh: exit 2     make

$ stat foo
stat: cannot stat `foo': No such file or directory

~からレシピのエラー:

通常、レシピ行が失敗したときにターゲットファイルがまったく変更されると、ファイルが破損して使用できないか、少なくとも完全に更新されません。ただし、ファイルのタイムスタンプは現在最新の状態であることを示すため、次回makeファイルを実行するときにファイルを更新しようとしません。この状況は、信号によってシェルが終了する場合と同じです。邪魔する。したがって、ファイルの変更を開始した後にレシピが失敗した場合、通常は正しい方法はターゲットファイルを削除することです。これはターゲットとして存在する場合にmake行われます。.DELETE_ON_ERRORこれはほとんど常にやりたいことですが、make歴史的な慣行ではないため、互換性のために明示的に要求する必要があります。

答え2

可能な場合はいつでも、makefileルールは最初に一時的な名前でターゲットを作成し、それを所定の位置に移動する必要があります。これにより、何らかの理由でビルドプロセスが中断されても、半分だけ作成されたターゲットファイルと完全に作成されたファイルを区別できない状況が発生しません。

out.txt: foo.sh input.txt
        ./foo.sh -i input.txt >[email protected]
        mv -f [email protected] $@

mv -f [email protected] $@これは一般的なメイクファイル慣用語です。

ジュリアーノの答え動的に生成された一時ファイル名のバリエーションが表示されます。同じターゲットを作成するプロセスが複数ある場合、または別のユーザーがディレクトリに書き込むことができる場合は、動的名前の生成が必要です。ビルドツリーの場合、これはほとんど発生しないため(この問題が発生した場合は、一般的なmakefileで発生する多くの操作が破損する可能性があるため)、通常、追加の複雑さは必要ありません。

答え3

これはあなたが書いたもの(またはあなたが書いたものによって生成されたもの)のように見えるので、出力ファイル名を使用するフラグを追加することを考えてみfoo.shましょう。-oこれにより、デフォルトのI / Oリダイレクト動作に頼る必要はありません。スクリプトは出力ファイルの一部をクリーンアップし、エラーを早期に検出できる場合は、最初からファイルの生成を避けることができます。

答え4

foo.shエラーが発生した場合は、ゼロ以外の値を正しく返すと思います。その後、一時的な操作を実行し、成功した場合にのみ出力を上書きする必要があります。

tmp=$$(mktemp) && ./foo.sh input.txt > $$tmp && mv $$tmp $@ || rm -f $$tmp && false

関連情報