😀

Makefileでシェルスクリプトを便利にする.ONESHELL

2024/12/08に公開

はじめに

Makefileは、主にビルドプロセスの管理に使用されるツールですが、シェルスクリプトを組み込むことで、タスクの自動化や簡単なスクリプト実行もできます。しかし、Makefile内で複数行のシェルコマンドを書く場合、シェルの挙動やMakefileの構文に関するいくつかの気をつけるポイントがあります。そんな中、.ONESHELLを使うことで、シェルスクリプトをよりシンプルで効率的に書くことができます。

.ONESHELLとは

.ONESHELLは、Makefile内で複数行のシェルコマンドを1プロセスで実行するためのオプションです。通常、Makefileでは各コマンドは個別のプロセスで実行されますが、.ONESHELLを使うと、複数のコマンドが1つのシェルセッション内で実行されるようになります。これにより、シェルの環境を共有でき、例えば変数の値を保持したまま次のコマンドに渡すことが可能になります。

.ONESHELLの基本的な使い方

.ONESHELLはMakefileの一番上に書くことで、Makefile内のすべてのターゲットに対して適用されます。

.ONESHELL:

以下のように書くと、指定したターゲットだけで、.ONESHELLを使うことができます。

.ONESHELL: target

SAMPLE1: シンプルな使い方

次に、シンプルな例を見てみましょう。.ONESHELLを使わない場合と使った場合の違いを理解するために、以下の2つのMakefileを比較します。

.ONESHELLを使わない場合

target:
    echo "Step 1"
    VAR=10
    echo "Step 2: $${VAR}"

このMakefileは、echoコマンドを2回実行していますが、変数VARは最初のechoコマンドの後に設定され、2番目のechoコマンドでは変数の値が表示されません。なぜなら、Makefileではデフォルトで各コマンドが個別のシェルセッションで実行されるためです。

.ONESHELLを使った場合

.ONESHELL:

target:
    echo "Step 1"
    VAR=10
    echo "Step 2: $${VAR}"

.ONESHELLを使うと、すべてのコマンドが1つのシェルセッション内で実行されるため、VAR=10で設定した変数を次のコマンドで利用できるようになります。この場合、echo "Step 2: $${VAR}"は正しくStep 2: 10と変数の値を表示します。

SAMPLE2: 複数行のシェルスクリプトを簡潔に

次に、少し複雑なシェルスクリプトをMakefile内で記述する例を見てみましょう。

.ONESHELLを使わない場合

SHELL := /bin/bash

target:
    @echo "start"
    @source .env
    @./test.sh "$${TEST_VAL1}"
    @echo "end"

このMakefileでは、sourcetest.shechoのコマンドが個別に実行されます。.envに定義された環境変数のTEST_VAL1をtest.shに渡すことはできません。
ちなみに、test.shがエラーになると、最後のechoコマンドは実行されません。

$ make target
start
arg1=
end

.ONESHELLを使った場合

.ONESHELL:
SHELL := /bin/bash

target:
    @echo "start"
    source .env
    ./test.sh "$${TEST_VAL1}"
    echo "end"

.ONESHELLを使うことで、sourcetest.shechoのコマンドが1つのシェルセッション内で実行されます。.envに定義された環境変数をtest.shに渡すことができます。注意が必要なのは、もしtest.shがエラーになった場合、最後のechoは実行されます。

$ make target
start
arg1=1
end

test.shがエラーになったとき、スクリプトを中止したい場合は、以下のように.SHELLFLAGSを使用します。

.ONESHELL:
SHELL := /bin/bash
.SHELLFLAGS := -e

target:
    @echo "start"
    source .env
    ./test.sh "$${TEST_VAL1}"
    echo "end"

SAMPLE3: 条件分岐を読みやすく

次に、if文を使用したシェルスクリプトの例を考えてみます。.ONESHELLを使うことで、以下のように書けるので読みやすいコードになります。

.ONESHELL:

target:
	@VAR1=5
	VAR2=10
	if [ $${VAR1} -gt $${VAR2} ]; then
		echo "VAR1 is greater"
	else
		echo "VAR2 is greater"
	fi

まとめ

このように、.ONESHELLを活用することで、Makefile内でのシェルスクリプト作成がよりスムーズになります。シンプルな使い方から、少し複雑な処理に至るまで、Makefileの中でシェルスクリプトをうまく使いこなしましょう。

Discussion