Makefile
.ONESHELL
.ONESHELLはGNU Makeの特別なターゲットで、Makefile内のレシピに記述された複数行のコマンドを1つのシェルプロセス内で実行するようにする設定です。これにより、複数行のコマンド間でシェルの状態(例えば変数や現在のディレクトリ)が共有されます。
通常、Makefile内のレシピは1行ごとに新しいシェルプロセスで実行されます。しかし、.ONESHELLを使うと、1つのシェルプロセスで全てのコマンドが実行されます。
動作の違い
.ONESHELLなし(デフォルト動作)
Makefile内で複数行のコマンドを記述すると、各行ごとに新しいシェルプロセスが生成されます。
target:
cd /tmp
echo "Current Directory: $(pwd)"
実行結果:
/bin/sh: 1: cd: can't cd to /tmp
Current Directory: /current/working/directory
理由:
- cd /tmpは1つのシェルプロセスで実行されます。
- echo "Current Directory: $(pwd)"は別のシェルプロセスで実行されるため、cdの効果が反映されません。
.ONESHELLを使用した場合
.ONESHELLを使うと、複数行のコマンドが1つのシェルプロセス内で実行されます。
.ONESHELL:
target:
cd /tmp
echo "Current Directory: $(pwd)"
実行結果:
Current Directory: /tmp
理由:
- 全てのコマンドが1つのシェルプロセス内で実行されるため、cd /tmpの効果が後続のコマンドにも反映されます。
使用方法
.ONESHELLは特殊ターゲットとして、Makefileの任意の場所で指定できます。
グローバルに有効化
Makefile全体で.ONESHELLを有効にする場合、最初に記述します。
.ONESHELL:
target:
echo "This is a test"
cd /tmp
echo "Now in $(pwd)"
個別ターゲットで有効化
特定のターゲットにのみ.ONESHELLを適用する場合は、そのターゲットのレシピ部分に記述します。
target1:
echo "Each line runs in a separate shell"
.ONESHELL: target2
target2:
echo "All lines in one shell"
cd /tmp
echo "Current Directory: $(pwd)"
注意点
シェルの選択: .ONESHELLを使用する場合でも、SHELL変数で明示的にシェルを指定できます。例えば、Bashを使う場合:
SHELL := /bin/bash
.ONESHELL:
target:
echo "Using Bash"
for i in {1..5}; do echo $$i; done
スクリプト風に記述: .ONESHELLを使用する際、複数行のコマンドがスクリプトのように実行されるため、if文やループもスムーズに記述できます。
.ONESHELL:
SHELL := /bin/bash
target:
if [ -d /tmp ]; then
echo "/tmp exists";
else
echo "/tmp does not exist";
fi
一部のMakefileでは非互換: .ONESHELLは比較的新しいGNU Makeの機能です。古いバージョンのGNU Makeでは動作しない可能性があります(バージョン4.0以降でサポート)。
Include
Makefile
におけるinclude
ディレクティブは、複数のMakefileを組み合わせて管理するための便利な方法です。プロジェクトが大きくなると、Makefileの記述が複雑になることがあります。このとき、Makefileを複数のファイルに分割し、それらを効率的に統合するためにinclude
が使われます。
基本構文
include ファイル名
または
-include ファイル名
ファイル名
には、相対パスまたは絶対パスを指定します。複数のファイルを指定することも可能で、スペースで区切ります。
include file1.mk file2.mk
動作
-
include
を使うと、指定したファイルの内容がその場所に挿入されたかのように動作します。 - 通常のMakefileと同じ形式で記述されたファイルをインクルードできます。
- 典型的には、ビルド設定や共通ルールを別ファイルに分割する場合に利用されます。
-include
との違い
-include
(またはinclude
の前にハイフンをつける形式)は、指定したファイルが存在しなくてもエラーにしません。これは、オプショナルな設定ファイルを読み込む場合に便利です。
例:
-include optional.mk
optional.mk
が存在しなくても、Makeはエラーを出さずに処理を続行します。
使用例
基本的な使い方
メインのMakefile
:
include settings.mk
all:
echo $(PROJECT_NAME)
settings.mk
:
PROJECT_NAME = MyProject
結果:
$ make
echo MyProject
MyProject
複数の設定ファイル
Makefile
:
include config/*.mk
all:
echo $(CONFIG_1) $(CONFIG_2)
config/setting1.mk
:
CONFIG_1 = Value1
config/setting2.mk
:
CONFIG_2 = Value2
結果:
$ make
echo Value1 Value2
Value1 Value2
オプショナルなファイルの読み込み
Makefile
:
-include local.mk
all:
echo $(OPTIONAL_VAR)
local.mk
が存在する場合はその内容が適用されますが、存在しない場合はエラーになりません。
注意点
-
ファイルのパス:
- 指定するファイルが存在しない場合、
include
はエラーを出します(-include
を使わない場合)。 - パスが正しいかを確認してください。
- 指定するファイルが存在しない場合、
-
依存関係の再評価:
-
include
されたファイルの中で変数やルールを定義する場合、それらが正しくMakeの再評価に影響を与えるように記述する必要があります。
-
-
循環参照:
- ファイル間で相互に
include
する構造を作らないように注意してください。これにより無限ループが発生する可能性があります。
- ファイル間で相互に
まとめ
include
ディレクティブを使うことで、Makefileをモジュール化して管理しやすくできます。大規模プロジェクトや設定ファイルの共有が必要な場合に非常に有用です。一方で、適切なパス管理や依存関係の確認が必要です。