本連載では、複数回に渡ってGNU makeで使うMakefileの基本的な機能の説明をしてきた。今回は、Makefileを複数のファイルに分割した場合のテクニックをもう少し取り上げておこう。どのやり方を採用するかはケース・バイ・ケースだが、さまざまなやり方があることを知っておくのは悪いことではないはずだ。
includeディレクティブでMakefileを分ける
前回は、1つのMakefileを複数のMakefileに分ける方法を取り上げた。分割したMakefileは同じディレクトリにある必要はなく、includeディレクティブでパスを指定すれば読み込むことができる。例えば、次のように大本となるMakefileの一部を「inc01.mk」と「inc02.mk」に分割したとする。
% tree
.
├── Makefile
└── mk
├── inc01.mk
└── inc02.mk
1 directory, 3 files
%
Makefieに次のようにincludeディレクティブを記述し、ほかのMakefileを読み込むようにする。
test: target1 target2
@echo "----"
@echo test:
@echo -n " pwd: "
@pwd
include mk/inc01.mk
include mk/inc02.mk
読み込まれる方のMakefile(mk/inc01.mk、mk/inc02.mk)の中身は次のようになっている。
◆mk/inc01.mk
target1:
@echo "--------"
@echo target1: mk/inc01.mk
@echo -n " pwd: "
@pwd
◆mk/inc02.mk
target2:
@echo "----"
@echo target2: mk/inc02.mk
@echo -n " pwd: "
@pwd
実行すると次のようになる。
%
make
--------
target1: mk/inc01.mk
pwd: /home/daichi/Documents
----
target2: mk/inc02.mk
pwd: /home/daichi/Documents
----
test:
pwd: /home/daichi/Documents
%
Makefileで利用する変数やターゲットなどは、行う処理が似ている場合はほぼ同じ記述を使うことがある。このように重複したコードが複数のファイルに存在する状態だと、場合によっては後で関連する全てのMakefileを書き換える必要な事態を生むことがある。
複数のMakefileを使っていて、どのMakefileにも似たような記述を行っている場合、それらを共通部分として取り出してまとめたいことがある。そんなときに使うのが、このincludeディレクティブであり、実用上は必須とも言える機能になっている。
問題はパスが変わる可能性があること
例えば、10個あるMakefileに共通する“似たような部分”を別のMakefileとして分割したとする。これをincludeディレクティブで読み込むようにすれば、共通部分の書き換えは1箇所で済むことになる(共通にしたことで、書き換えにより気を使うことになる面もあるのだが、ここではそちらの問題は置いておく)。
しかし、includeするところに書いてあるパスについてはハードコーディング状態だ。共有しているMakefileの配置場所を変えれば、そのMakefileをincludeしているほかの全てのMakefileの中に書いてあるパスを書き換える必要がある。逆に、呼び出しているMakefileのパスを変えても、絶対パスで記述してある場合を除いてincludeのところに書いてあるパスを書き換える必要がある。
完全に相対パスが変わらないという状況にある場合を除いて、includeに書いてあるパスは書き換えが必要な可能性がある。これは忘れたことに起きる事態なので、問題になりやすい。
includeにディレクトリを指定しない
GNU makeにはこのパスの指定を引数で行うための方法がある。引数でディレクトリパスを指定する方法だ。この方法であればファイル名を変更しない限り、ディレクトリの変更に関してはmakeに与えるオプションで変更することができる。実行するコマンドはシェルの関数やエイリアスを使えば、設定ファイルで一括管理することができる。ここまですると先ほどのパス変更の問題も1箇所で管理できることになる。
挙動をわかりやすくするために、次のようにMakefileを配置することにする。
% tree
.
├── Makefile
├── mk1
│ └── inc01.mk
└── mk2
└── inc02.mk
2 directories, 3 files
%
それぞれ中身を次のようにしておく。
◆Makefile
test: target1 target2
@echo "----"
@echo test:
@echo -n " pwd: "
@pwd
include inc01.mk
include inc02.mk
◆mk1/inc01.mk
target1:
@echo "--------"
@echo target1: mk1/inc01.mk
@echo -n " pwd: "
@pwd
◆mk/inc02.mk
target2:
@echo "----"
@echo target2: mk2/inc02.mk
@echo -n " pwd: "
@pwd
特に注目しておきたいのはMakefileに書いてあるincludeディレクティブの内容だ。パスが書いておらず、ファイル名だけになっている。
この状態でmakeを実行すると次のようになる。
% make
Makefile:8: inc02.mk: No such file or directory
make: *** ターゲット 'inc02.mk' を make するルールがありません. 中止.
%
当然だが、makeの実行は失敗する。includeディレクティブに指定したパスはカレントディレクトリのファイルになっており、カレントディレクトリにそのファイルはないので読み込めないとエラーになる。
includeのパスをオプションを指定する
そこでオプションにパスを指定する方法だ。実行方法を見るのわかりやすい。次のように実行する。
% make --include-dir mk1 --include-dir mk2
--------
target1: mk1/inc01.mk
pwd: /home/daichi/Documents
----
target2: mk2/inc02.mk
pwd: /home/daichi/Documents
----
test:
pwd: /home/daichi/Documents
%
想定通りに実行されていることがわかる。
includeディレクティブにはファイル名しか記載していない。しかし、「--include-dir mk1」と「--include-dir mk2」というオプションを指定したことで、./mk1/と./mk2/にデプロイされているファイルも読み込みの対象となった。この結果includeディレクティブが機能するようになった。
GNU makeの--include-dirオプションは「-I」と書いても同じなので、次のように実行することもできる。
% make -I mk1 -I mk2
--------
target1: mk1/inc01.mk
pwd: /home/daichi/Documents
----
target2: mk2/inc02.mk
pwd: /home/daichi/Documents
----
test:
pwd: /home/daichi/Documents
%
includeディレクティブで指定するパスをファイル名だけにして、ディレクトリはオプションで指定する。――こういうやり方もあるということも覚えておいてもらえれば幸いだ。
どっちの方法を使うかはケースバイケース
どちらの方法を使うかは状況による。別のサーバに持っていって使うとか、ホスティングサービス経由で配布するとか、それ単体での運用が想定されている場合には相対パスで直接includeディレクティブに書いておく方が扱いやすいことが多いだろう。
一方、再配布を想定しておらず、ローカルの環境で使っているようなケースだと、場合によってはGNU makeのコマンドオプションで指定した方が便利なこともあるかと思う。状況に応じて扱いやすい方を使っていただきたい。