はじめに
今までmakeについては「なんかインストールのときに実行させられるやつ」くらいの認識しかありませんでした。
最近勉強を始めたC言語でコンパイルをする必要があり、コンパイルについて調べていたら『C言語を使うならMakeを使おうよ』なる記事があったので、これを機にmakeについて調べてみました。
ちなみにコンパイルの仕方は色々まとめられてるので、それ以前の「makeとはなんぞや」という基本的な部分をまとめました。
環境
$ make -v
でバージョン確認が可能。
$ make -v
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redist
makeについて
makeって何?
Googleで「make とは」と検索して出てくる説明はだいたいこんな感じです。
プログラムのビルドを簡単にしてくれるすげーやつ
こいつのおかげで依存関係とか気にしなくても自動でビルドできるよ
でも後述するmakeの簡単な使い方を知るうちに「少し違うのでは」と思ったので本家の説明を読んでみた。
GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program's source files.
「makeはプログラムのソースファイルから、実行可能なファイルや他のプログラムファイルの生成を管理するツールだよ」みたいなことが書かれていて、ざっくりいうとファイルの生成管理ツールなんだと理解しました。
makeを使ってみる(not コンパイル)
makeの実行手順
こちらのブログを参考に実際に使ってみます。
makeは、以下のような手順で実行されます。
- コマンドライン上でmakeを実行する。
- makeファイルが読み込まれ、そこにあるコマンドが実行される。
make実行時に特に指定がなければ、同じディレクトリのMakefileという名前のmakeファイルが読み込まれます。
Makefileを作成、実行してみる
makeファイルは2つの要素から構成されているそうです。
- ファイル名:生成したいファイルの名前
- コマンド:そのファイルを生成するためのコマンド
この2つを以下のように記載する。
test.txt:
echo Hello World! > text.txt
1行目に生成したいファイル名、2行目にコマンドを入力するのですが、2行目の頭をインテンド下げしないとコマンドとして認識してくれないそうなので注意。
これをMakefileとして保存してコマンドライン上でmakeを実行するとtext.txtが生成されます。
このとき、実行するコマンドもシェル上に表示されますが、-n
オプションで非表示にできます。
$ make
echo Hello World! > hello.txt
$ cat hello.txt
Hello World!
また、Makefile内のコマンドの先頭に@をつけることで非表示にすることもできます。
複数行、複数タスクの入力
Makefileでは複数行のコマンドを入力することも可能。
また、複数のタスクを1つのMakefileにまとめることも可能。
hello.txt:
echo Hello > hello.txt
echo World! >> hello.txt
.PHONY: clean
clean:
rm -f *.txt
ここでは、hello.txtに文字を出力するタスクと、txtファイルをすべて消去するタスクをまとめました。
makeは指定されたコマンドを実行するので、2つ目のように、ファイル生成以外のシェルコマンドを入力しても動きます。
でも、先程「1行目に生成するファイル名を入力する」ということを習いました。
なので、今回のように生成するファイルが無い場合は、.PHONY: タスク名
と記載して「ファイル生成しませんよ〜。この名前はタスク名ですよ〜」と明記するらしいです。(PHONYは偽物という意味)
ちなみに、.PHONY: clean
を省いてタスクを生成してもMakefileは作れますし、実行可能です。
しかし、仮にタスク名と同名のファイルやディレクトリがカレントディレクトリにある場合、タスクが実行されなくなるので、ファイル生成時以外は.PHONY
を積極的に書くようにすると吉。
さて、先程作った複数のタスクがあるMakefileに話を戻します。
複数タスクがある場合、make実行時に何も指定がなければ先頭のタスクが実行されます。
先頭以外のタスクを実行するときは、タスク名を指定して実行します。
$ make
echo Hello > hello.txt
echo World! >> hello.txt
$ cat hello.txt
Hello
World!
$ make clean
rm -f *.txt
$ cat hello.txt
cat: hello.txt: No such file or directory
Makefile以外のmakeファイルの実行
-f
オプションを使うと、Makefile以外のmakeファイルを実行できます。
コンパイルツールとしてのmake
以上のように、複数のコマンドを記載したmakeファイルが作れます。
makeをコンパイルツールとして活用する際にも基本的には上の手順と同じです。
コンパイルのコマンドを順番にmakeファイルに記しておき、makeで実行すると上のコマンドから処理されていくので、予め設定しておいた手順でコンパイルができます。
コンパイルの場合分けをするのも、タスクを追加して実行時にタスクを指定することで容易に可能になります。
これらの手順をmakeファイルで事前に指定してくれているので、実行者としては自動でコンパイルしてくれる・ビルドしてくれる便利なツールに見えるわけですね。
具体的なコンパイル手順については、『C言語を使うならMakeを使おうよ』を読んでこれから勉強しようと思います。
詳しいmakeについての話は、こちらのページが参考になりそうです。
シェルスクリプトと何が違うの?
私が一番引っかかったのはここでした。
今までの説明だと、「makeファイルにシェルコマンドを書いておき、そのファイルを実行するとコマンドが実行される」ということになり、それってシェルスクリプトじゃねーかって思ってました。
makeの記事を漁ってもコンパイルの仕方について書かれているばかりで、モヤモヤは募るばかり。
そんな中、ズバリな記事を見つけました。
Makefileとシェルスクリプトの違いとして以下の2点が挙げられていました。
「Makefileに複雑なコマンド列を書くとエスケープが大変」ということです。$をエスケープし忘れて期待しない挙動になることがあります。
(中略)
Makefileでは1つのターゲットについて2行以上のビルド用コマンドを記述できますが、それぞれの行は別々のプロセスのシェルで実行されるという事に注意しなくてはいけません。
つまり、エスケープ処理でミスる可能性があるし、行同士で状態の共有ができないので、複雑なシェルコマンドをつらつらと書き連ねる場合にはシェルコマンドを使ったほうが良いということです。
上記の記事では複雑な処理を記載する際には、「外部のシェルスクリプトに記載した後、そのシェルスクリプトの実行コマンドをmakeファイルに記載する」という手法を推奨していました。
おわりに
これでようやくmakeを使ったコンパイルについて学べそうです。
実際にmakeを使ってみて気がついたことやハマったことがあればまた記事にしたいと思います。
無知すぎて上記の説明に誤りが多々あるかもしれないので、何かあればお教えいただきたいです。