GNU makeをインストール

今回からは、makeとMakefileをライフハック的に使う方法を取り上げていく。まず、システムにGNU makeがインストールされているかどうかを確認する。次のように「make --version」が動作すれば、GNU makeがインストールされている状態だ。

% make --version
GNU Make 4.2.1
このプログラムは x86_64-pc-linux-gnu 用にビルドされました
Copyright (C) 1988-2016 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 以降 <http://gnu.org/licenses/gpl.html>
これはフリーソフトウェアです: 自由に変更および配布できます.
法律の許す限り、 無保証 です.
% 

GNU makeは開発目的で使われることがほとんどなので、デフォルトのインストール状態ではインストールされていないことが多い。上記コマンドが実行できない場合には、各Linuxディストリビューションに応じた方法でインストールを行おう。

  • GNU makeがインストールされていない

    GNU makeがインストールされていない

例えば、Ubuntuであれば次のようにGNU makeをインストールする。

GNU makeをインストール - Ubuntu 20.04 LTS

sudo apt install make
  • GNU makeのインストールとmakeコマンドの確認

    GNU makeのインストールとmakeコマンドの確認

インストール後、「make --version」の動作が確認できればOKだ。

Makefileの基本アイデア

makeコマンドは、Makefileに書いてある内容を実行するコマンドだ。つまり、「GNU makeを使う」というのは、「Makefileの書き方を覚える」ということである。

Makefileの基本的な考え方は「元ファイル」「元ファイルから生成されるファイル」「生成されるファイルを生成する方法」の3つを書くというものだ。次のようなファイルが、Makefileの基本的なアイデアに則ったMakefileファイルということになる。

file_output: file_input
    cat file_input > file_output

Makefileに書いてある内容は、次の通りだ。

項目 内容
file_input 元ファイル(実際のパス)
file_output 元ファイルから生成されるファイル(実際のパス)
cat file_input > file_output 生成されるファイルを生成する方法(コマンド)

まず、「元ファイル」「元ファイルから生成されるファイル」を「元ファイルから生成されるファイル: 元ファイル」という形式で書く。次の行からは、ファイルを生成するためのコマンドを書いていく。このとき、行頭をタブにすることが必要だ。処理の記述は、複数行になってもよい。

  • Makefileの基本フォーマット

    Makefileの基本フォーマット

上記を1つの塊として捉え、これをいくつもMakefileに書いておくことができる。

とりあえず動作を確認してみよう。まず、元ファイルとしてfile_inputを用意し、上記内容のMakefileを用意する。

実験の準備を整えた状態

% ls -l
合計 1
-rw-r--r-- 1 daichi なし  0  2月 15 09:19 file_input
-rw-r--r-- 1 daichi なし 56  2月 15 09:26 Makefile
% 

ここで「make file_output」とコマンドを実行する。makeコマンドの引数として「生成されるファイル」を指定するのが、makeコマンドの基本的な使い方だ。実行すると次のようになる。

make file_outputの実行結果

% make file_output
cat file_input > file_output
% 

Makefileのタブから始まる行に書いたコマンドが表示されていることがおわかりいただけるだろう。この状態でファイルリストを表示させると、次のようにfile_outputというファイルが生成されている。「cat file_input > file_output」というコマンドが実行されて、file_outputというファイルが生成されたのだ。

make fileoutput実行後に、fileoutputが生成されていることを確認

% ls -l
合計 1
-rw-r--r-- 1 daichi なし  0  2月 15 09:19 file_input
-rw-r--r-- 1 daichi なし  0  2月 15 09:37 file_output
-rw-r--r-- 1 daichi なし 56  2月 15 09:26 Makefile
% 

この状態でもう一度「make file_output」とコマンドを実行すると、次のようになる。

make file_outputの2回目と3回目の実行結果

% make file_output
make: 'file_output' は更新済みです.
% make file_output
make: 'file_output' は更新済みです.
% 

これがmakeの基本的な仕組みだ。「元ファイル」「元ファイルから生成されるファイル」という2つのファイルがアクションの基準になっており、「元ファイル」の最終更新時刻が、「元ファイルから生成されるファイル」の最終更新時刻よりも新しい場合にのみ、タブから始まる行の処理が実行される。

「cat file_input > file_output」というコマンドを実行するとfile_outputというファイルが作成され、file_outputというファイルの最終更新時刻はfile_inputよりも新しくなる。「元ファイル」から「元ファイルから生成されるファイル」が生成された状態ということであり、makeとしてはもうこのファイルを作成する必要はない、と判断しているわけである。これがmakeとMakefileの最も基本的な考え方であり、使い方ということになる。

コマンドを複数行に書く

Makefileではタブから始まる行は複数行にわたって連続させることができる。アクションの開始がファイルの最終更新時刻の比較から起こることはさきほど説明したので、今度は先ほどのMakefileにファイルの最終更新時刻を表示するコマンドを追加する。次のようになる。

コマンドを複数行書いた場合のMakefile

file_output: file_input
    cat file_input > file_output
    stat -c '%n %z' file_input
    stat -c '%n %z' file_output

catとstatの行はそれぞれタブから始まっている。こんな感じでコマンドを書いていくことができる。

実行すると次のようになる。

make file_outputの実行結果

% make file_output
cat file_input > file_output
stat -c '%n %z' file_input
file_input 2022-02-15 09:21:28.914936800 +0900
stat -c '%n %z' file_output
file_output 2022-02-15 09:39:23.436719200 +0900
% 

ここで注目したいのは、make file_outputを実行した後の出力だ。

make file_outputの実行結果の説明

% make file_output
Makefileに書いてあるコマンド(2行目)
Makefileに書いてあるコマンド(3行目)
上記コマンドの出力結果
Makefileに書いてあるコマンド(4行目)
上記コマンドの出力結果
% 

makeコマンドはMakefileに書いてあるコマンドを行ごとに出力(エコーバック)し、さらにそのコマンドの実行結果を出力している。これがmakeの基本的な動作となる。そのため、出力が煩雑になるのがmakeの実行時の特徴の一つと言えるかもしれない。

コマンドのエコーバックを抑制する

makeにはこの挙動(コマンドのエコーバック)を抑制する方法が用意されている。この書き方は最初に覚えたい。次のように、タブから始まるコマンドを「タブ」から「タブ@」のように書く。これでその行のコマンドは表示(エコーバック)されなくなる。

コマンドのエコーバックを抑制した場合のMakefile

file_output: file_input
    @cat file_input > file_output
    @stat -c '%n %z' file_input
    @stat -c '%n %z' file_output

実行すると次のようになる。

make file_outputの実行結果の説明

% make file_output
file_input 2022-02-15 09:21:28.914936800 +0900
file_output 2022-02-15 09:41:16.700829500 +0900
% 

コマンド行はエコーバックされておらず、コマンドの実行結果だけが出力されていることがおわかりいただけるはずだ。makeコマンドの実行結果を読みやすくさせる場合には、このように「@」を使ったエコーバックの抑制を使うことになる。

コメントを書く

何をするにしてもドキュメントというのは大切だ。コマンドだけ書いてあっても、説明が書いてないために、後で見たときに意味がわからないということは往々にしてある。

お気づきかもしれないが、タブから始まる行はシェルのコマンド入力と同じだ。つまり、「#」から書き始めればその行はコメントということになり、文章を書いておくことができるようになる。例えば次のような感じだ。

コマンド部分にコメントを書き込んだ場合のMakefile

file_output: file_input
    # file_inputからfile_outputを作成
    @cat file_input > file_output
    #
    # file_inputとfile_outputの更新日時を表示
    @stat -c '%n %z' file_input
    @stat -c '%n %z' file_output

実行すると次のようになる。

make file_outputの実行結果の説明

% make file_output
# file_inputからfile_outputを作成
#
# file_inputとfile_outputの更新日時を表示
file_input 2022-02-15 09:21:28.914936800 +0900
file_output 2022-02-15 09:42:26.576640500 +0900
% 

タブから始まる行はインタラクティブシェルにおける1行と同じようなものなので、コメントのつもりで書いているものの、コマンドとしてエコーバックされてくる。場合によっては、これはとてもmakeの出力をわかりにくいものにすることがある。

コメントとエコーバック抑制を組み合わせることもできる

「@」によるエコーバックの抑制は、次のように「#」によるコメントと組み合わせることができる。

コメントにエコーバックも使った場合のMakefile

file_output: file_input
    @# file_inputからfile_outputを作成
    @cat file_input > file_output
    @#
    @# file_inputとfile_outputの更新日時を表示
    @stat -c '%n %z' file_input
    @stat -c '%n %z' file_output

実行すると次のようになる。

make file_outputの実行結果の説明

% make file_output
file_input 2022-02-15 09:21:28.914936800 +0900
file_output 2022-02-15 09:44:30.782121900 +0900
% 

コメント行として書いた行のエコーバックが抑制されていることがわかる。

空行を使ってMakefileが読みやすくする

Makefileには空行を加えることもできる。タブから始まるコマンド行の行間には空行を加えても良いのだ。処理の意味ブロックごとに空行で区切る、といった書き方をしておくと、後から読みやすくなる。

コマンド行のエコーバックで表示される内容も考えてちょっと書き換えて、次のようなMakefileにしてみる。

空行を加えた場合のMakefile

file_output: file_input
    # file_inputからfile_outputを作成
    cat file_input > file_output

    # file_inputとfile_outputの更新日時を表示
    stat -c '%n %z' file_input file_output

実行すると次のようになる。

make file_outputの実行結果の説明

% make file_output
# file_inputからfile_outputを作成
cat file_input > file_output
# file_inputとfile_outputの更新日時を表示
stat -c '%n %z' file_input file_output
file_input 2022-02-15 09:21:28.914936800 +0900
file_output 2022-02-15 09:46:55.894618200 +0900
% 

Makefileがそこそこに読みやすく、makeコマンドの出力結果もそれなりに読みやすい感じになったのではないだろうか。

まずは基本をきっちり身に付ける

Makefileのシンタックスはそれほど難しいものではない。実際には複雑な書き方もできるのだが、基本となる発想は今回説明した通りだ。まずは、この考え方と書き方を覚えてしまいたい。

処理の基となるファイルがあり、そこから生成される成果物のファイルがある。生成処理を行うかどうかはファイルの最終更新時刻の比較で判断しており、元ファイルが更新されて生成されるファイルよりも新しくなると更新処理が実行できるようになる。

これは業務にも利用できる。処理すべきデータがあり、処理結果がある。処理すべきデータが更新されていないなら、処理を行う必要もない。どのような処理を行うかをデータファイルを中心として整理することができる。物事の整理方法としてMakefileの書き方はなかなか便利なのだ。

まずはこの基本となる書き方をマスターしておこう。

参考