make

make について

make とは,Makefile と呼ばれるレシピファイルに書かれたレシピに沿って自動的に作業をしてくれるツールである. 特徴としては,

  1. レシピなので,書き方が単純.使いまわししやすい.
  2. 「モノ」の依存関係と,その局所的な手順だけ書けば良い.とても単純.
  3. 必要な作業手順を自動的に判断してくれる. 人間が手順の順番を気にしなくて良い.
  4. 作業をやり直す際,必要な作業「だけ」やってくれる. シェルスクリプトだと相当作りこまないと,頭からやり直しになる.それに比較すると大変便利.
  5. 作業の途中でエラーがあって止まったとする.その箇所だけ手作業で対応した場合,もう一度 make すると,続きからきちんとやってくれる.
  6. より簡単な記述をするための高度な文法もあり,習熟すればするほど簡単に書ける.

というところが挙げられる. 見てわかるように,大変に便利なものである. 今回はこの make について学ぼう.

これまでに学んだ, シェルスクリプトなども自動的な作業をするツールであるが、 途中で訂正ややり直しが発生するような場合(プログラム開発など)は make の方が明らかに便利である.

参考資料

  • GNU Make … 泣く子も黙る GNU の Make. まあ,ここを情報の起点としておけば間違いない.最新バージョンは 4.2 あたり. まあ,make を自分でインストールする必要になることはまずないだろうから、既にインストールされているものを使えば良い.
  • GNU Make ver.3.79 マニュアルの日本語訳 … 新堂安孝氏が作成された、GNU Make の日本語マニュアル.感謝して,ありがたく読ませてもらおう. マニュアルの原作者はかの リチャード・M・ストールマン, ローランド・マグラス (GNU プロジェクト) だと思われる.
  • GNU Make ver.3.77 マニュアルの日本語訳 … いのまた みつひろ氏が訳された GNU Make の日本語マニュアル.ライセンスは GPL だ.こちらも、感謝してありがたく読ませてもらおう. 原作者はかの リチャード・M・ストールマン, ローランド・マグラス (GNU プロジェクト).

他にも,web 上には make について書いたものが沢山ある. 自分にあったものを探してみよう.

make でサラダを作ってみよう

まずは例えから入ろう. make が料理人で,Makefile はそのレシピに相当すると考えて,レシピを書くことを考える. そして、簡単なサラダを作るレシピを考えてみよう. ただし,書き方は make の流儀で,である.

最初に,簡単なサラダを作る,という作業を,

  1. 完成品,途中経過品,材料,といった「モノ」の 依存関係
  2. および、その途中経過での 操作

にわけて丁寧に考えてみよう. まずは依存関係だけ書いてみる.


■ サラダ作成時の「モノ」の 依存関係

ターゲット品 そのターゲットに必要な品
i. サラダ完成品 1. 適度な大きさに切られた清潔かつ新鮮な野菜
2. ドレッシング
3. 綺麗な器
ii. 適度な大きさに切られた
清潔かつ新鮮な野菜
4. 清潔かつ新鮮な野菜
iii. 清潔かつ新鮮な野菜 5. 新鮮な野菜
iv. ドレッシング 6. 酢
7. サラダ油
8. 醤油
9. 塩
10. コショウ

次に,この依存関係の左の「ターゲット品」を作成するためにやる「作業」を追加で書き入れてみよう.


■ サラダ作成時に作ったり利用したりする「モノ」の 依存関係作業

ターゲット品 or 作業 そのターゲットに必要な品 or 作業内容
i. サラダ完成品 1. 適度な大きさに切られた清潔かつ新鮮な野菜
2. ドレッシング
3. 綺麗な器
(上のターゲット i. を作るための作業) 1 を 3 に入れ,2 をふりかける
ii. 適度な大きさに切られた
清潔かつ新鮮な野菜
4. 清潔かつ新鮮な野菜
(ii. を作る作業) 4 を適当にカットする
iii. 清潔かつ新鮮な野菜 5. 新鮮な野菜
(iii. を作る作業) 5 を洗う
iv. ドレッシング 6. 酢
7. サラダ油
8. 醤油
9. 塩
10. コショウ
(iv. を作る作業) 6-10 を適量,適度に混ぜる

この表に従っての作業は以下のようになる.

まず、一番最初のターゲット品 i. についてターゲット品がもしも完成していなければ、その材料である右の欄の「必要品」をチェックする. ここで必要な品が揃っていればその直下の 作業 で作成するし、不足があればそれがターゲット品として書かれているレシピ部分を探す.

そして、またその品の右の欄の必要品ををチェックして… …以下同様に…ということを再帰的に繰り返し、終わったところから再帰的に戻ってくる.

こうして、(レシピが間違っていなければ) サラダがいずれ完成することになるのは理解できるだろう.

これがレシピファイル Makefile を読んでの make の動作の基本となる. このように、実際の作業の流れの解釈は make という料理人がやってくれるのだから,われわれの仕事はこの簡単なレシピを書くだけで済む.

さて,次に make の文法でこのレシピを書くことを考えよう. 通常はこのレシピファイルは Makefile という名前である. そしてその中身は, make の文法に沿うならば以下のようになる.


■ 上のレシピを make の文法に沿って Makefile として書くと次のようになる ■

サラダ完成品:適切清潔新鮮野菜 ドレッシング 綺麗な器
--(tab)-- 必要品の最初を最後に入れ,二番目をふりかける

適切清潔新鮮野菜: 清潔新鮮野菜
--(tab)-- 必要品を適当にカット

清潔新鮮野菜: 新鮮野菜
--(tab)-- 必要品を洗う

ドレッシング: 酢 サラダ油 醤油 塩 コショウ
--(tab)-- 必要品を適量,適度に混ぜる

ただし, --(tab)-- は, タブ(tab キーを一回押すと入力できる)のことである. 画面表示でスペース等と区別がつきにくいので、このように表記している.

で、見て分かるように、この文法は以下の通り.

Makefile を書くのに最低限必要な文法・知識

これは以下のたった 3つ.

1. 依存関係を 「作りたいターゲットの品 : その作成に直接必要な材料」 と書く.
2. 作業は次行に 「--(tab)-- 作業」 と書く.
3. これを十分なだけ繰り返す.

さて,こうやってレシピすなわち Makefile が完成したら,あとは,シェルで

make

と打つだけで(材料が揃っているならば), make コマンドがこのレシピにそって作業をして,サラダが完成するというわけである.

サラダ作成を途中からやり直す?

さて,例えの続きといこう.

上のレシピを書き,料理人にサラダを作ってもらった後で,酢が古かったことに気づいて新しい酢でサラダを作り直したくなったと想定しよう. ただし,途中でできた品は充分に余っているとしておこう.これはコンピュータ上では、途中経過ファイルが保存されている状態である.

さてこの場合,必要な作業は

  1. ドレッシングを再度作成する
  2. 適切な、清潔新鮮野菜はさっき作って余っているものを使えるので、これに新しいドレッシングをかけてサラダを完成させるだけ.

という,たった 2つで良い.

しかし,この「この 2つの作業だけで良い」という具体的な事実を料理人(make)にどうやって指示すればよいだろう?

make を使う場合は、実はこの指示は具体的に伝えなくてよいのだ. make が様々なものの依存関係とその 製造時間 を比較して勝手に判断してくれるのである.

つまり,酢を取り替えたあとでもう一度

make

と打つだけで,勝手にこの 2行程だけやり直してくれるのである. このように,

make の便利な点

make と打つだけで必要な作業だけやり直してくれる!

のである. これが make のとても便利な点である.

単純な例

では,プログラムで実際にやってみよう. 最初はごく簡単に,プログラムをコンパイルして,実行するという単純な二段階の作業をさせてみよう.

  実習

以下の手順に従い,適当な実習用ディレクトリで make を動かしてみよう. ただし,ただやるだけではなく,全体の流れを理解しながらやろう.タイプするだけでは意味がナイぞ.

  1. まず,f_sin.c という名前のファイルを作り,次の中身を書き込んでおく.

    #include <stdio.h>
    #include <math.h>
    main () {
        double x,dx;
        dx = 1.0/100;
        x = 0.0;
        for ( x = 0.0; x <= 0.5+dx; x += dx) {
            printf("%6.3f %6.3f\n", x, sin(x));
        }
        return 0;
    }
    

    何をするプログラムか, 読解しておこう.

  2. 同じディレクトリに Makefile という名前のファイルを作り,次の中身を書き込んでおく.

    f_sin.dat: f_sin.exe
    --(tab)--  \rm -f f_sin.dat
    --(tab)--  ./f_sin.exe > f_sin.dat
    
    f_sin.exe: f_sin.c
    --(tab)--  gcc -lm f_sin.c -o f_sin.exe
    

    上でも書いているように、2, 3, 6行目の --(tab)-- は「TAB」キーを押して入力するタブ文字を意味しているので注意しよう. ちなみに、ここで「タブ」以外の文字を使っていて make をすると,「分離記号を欠いています (8個の空白でしたが,TAB のつもりでしたか?)」なんて言われたりする.
    何をするレシピか, もちろん、読んで確認しておこう.

  3. make してみる.

    make
    
  • 問題がなければ,f_sin.c というプログラムがコンパイルされて f_sin.exe という実行ファイルが作られ、そしてそれが実行され,その結果の f_sin.dat というデータファイルができているはずだ. その内容を,例えば次のようにして覗いてみよう.

    less f_sin.dat
    
  • 結果のデータファイルを消してみよう.

    rm f_sin.dat
    
  • 再び make してみる.
    この時,どの作業が行われるか,注意深く見ておこう.先と異なるはずだ.

    make
    
  • 再び実行結果ができているはずなので確認しよう.

    less f_sin.dat
    
  • 今度はプログラムを修正してみよう.
    具体的には,f_sin.c の for の行を

    for ( x = 0.0; x <= 1.0+dx; x += dx) {
    

    と, 0.5 だったところを 1.0 に修正してみる.

  • 再び make してみる.

    make
    
  • 今度の実行結果は異なるはずだ.見てみよう.

    less f_sin.dat
    

結局、いろんな変更をしても,いつでも make コマンドだけで済む! 便利だ!

ちょっと複雑になった例

次に,今の例をちょっとだけ修正して,プログラムをコンパイルして,実行して,さらにそのデータファイルからグラフを作成するという,三段階の作業をさせてみよう.

  実習

先の続きの形になる.続きとして,以下のようにやってみよう.

  1. 先と同じディレクトリに sin2eps.gpl という名前のファイルを作り,次の中身を書き込んでおく.

    set output "f_sin.eps"
    set term postscript
    plot "f_sin.dat" with lines
    

    なお、最後の行をきっちり改行しておくことを忘れないようにしよう.
    なお、gnuplot が古くなければ、2行目を set term pdf とすることで pdf を作らせることが出来る. 阪大の CentOS は大変に古いのでこれはできないが…

  2. Makefile の中身を以下のように修正する(先頭に三行が加わる).

    f_sin.eps: f_sin.dat
    --(tab)--  gnuplot "sin2eps.gpl"
        
    f_sin.dat: f_sin.exe
    --(tab)--  \rm -f f_sin.dat
    --(tab)--  ./f_sin.exe > f_sin.dat
        
    f_sin.exe: f_sin.c
    --(tab)--  gcc -lm f_sin.c -o f_sin.exe
    

    繰り返しになるが、--(tab)-- は「タブ」キーで入力する 1文字をこう書いているだけのことに注意! さて、何をするレシピになったか, これも読んで確認しておこう.

  3. make してみる.

    make
    
  • 問題がなければ,f_sin.eps という画像ファイルができているので,見てみよう. GUI でそのファイルをマウスでダブルクリックするもよし,次のコマンドでもいけるだろう. sh gv f_sin.eps &

このコマンド名は環境によっては, gv ではなく, ggv かもしれない. gv ではダメなときは試してみよう.

このように,作業が何段階かになっていても make は問題なく使えることを理解しよう.

もうちょっとだけ続くんじゃ…

さて,今度は,上の作業を 3つのプログラムをコンパイルし,3つの実行ファイルを実行し,3つのデータファイルを作らせて,それを統合したグラフファイルを1つ作るというものにしよう. なに,理屈がわかっていれば簡単だ.

  実習

先の続きの形になる.続きとして,以下のようにやってみよう.

  1. f_cos.c という名前のファイルを作り,次の中身を書き込んでおく.

    #include <stdio.h>
    #include <math.h>
    main () {
        double x,dx;
        dx = 1.0/100;
        x = 0.0;
        for ( x = 0.0; x <= 1.0+dx; x += dx) {
            printf("%6.3f %6.3f\n", x, cos(x));
        }
        return 0;
    }
    

    何をするプログラムか, 読解しておこう.

  2. f_sqr.c という名前のファイルを作り,次の中身を書き込んでおく.

    #include <stdio.h>
    #include <math.h>
    main () {
        double x,dx;
        dx = 1.0/100;
        x = 0.0;
        for ( x = 0.0; x <= 1.0+dx; x += dx) {
            printf("%6.3f %6.3f\n", x, x*x);
        }
        return 0;
    }
    

    これも何をするプログラムか, 読解しておこう.

  3. Makefile の中身を以下のように修正する(だいぶ変わるので注意深く作業しよう).

    all.eps: f_sin.dat f_cos.dat f_sqr.dat
    --(tab)--  gnuplot "all2eps.gpl"
        
    f_sin.dat: f_sin.exe
    --(tab)--  \rm -f f_sin.dat
    --(tab)--  ./f_sin.exe > f_sin.dat
        
    f_cos.dat: f_cos.exe
    --(tab)--  \rm -f f_cos.dat
    --(tab)--  ./f_cos.exe > f_cos.dat
        
    f_sqr.dat: f_sqr.exe
    --(tab)--  \rm -f f_sqr.dat
    --(tab)--  ./f_sqr.exe > f_sqr.dat
        
    f_sin.exe: f_sin.c
    --(tab)--  gcc -lm f_sin.c -o f_sin.exe
        
    f_cos.exe: f_cos.c
    --(tab)--  gcc -lm f_cos.c -o f_cos.exe
        
    f_sqr.exe: f_sqr.c
    --(tab)--  gcc -lm f_sqr.c -o f_sqr.exe
    

    やはり --(tab)-- に注意. そして、何をするレシピになったか, これもやはり読んで確認しておこう.

  4. 先と同じディレクトリに all2eps.gpl という名前のファイルを作り,次の中身を書き込んでおく.

    set output "all.eps"
    set term postscript
    plot "f_sin.dat" with lines, "f_cos.dat" with lines, "f_sqr.dat" with lines
    

    やはり、最後の行をきっちり改行しておくことを忘れないように!

  5. make してみる.

    make
    
  6. 問題がなければ,all.eps という画像ファイルができているので,上と同じ方法で見てみよう.

  7. プログラムのいずれかを適当に修正してみよう.内容はグラフがおかしくならないようなものならなんでも良い.

  8. 再び make してみる.

    make
    
  9. 今度の実行結果は異なるはずだ.見てみよう.

    gv all.eps &
    

発展: make の文法をきちんと使うと楽できる例

先の Makefile を見ると,似たようなことが何回も書かれていて,面倒な上に間違えやすい. もちろん,make の文法はこういうことは克服できるようになっている. 例えば先の Makefile と同じ動作をするような,シンプルな Makefile は次のようになる.

    .SUFFIXES : .dat .exe
    DATA = f_sin.dat f_cos.dat f_sqr.dat
    
    .c.exe:
    --(tab)--  gcc -lm $< -o $@
    
    .exe.dat:
    --(tab)--  \rm -f $@
    --(tab)--  ./$< > $@
    
    all.eps: $(DATA)
    --(tab)--  gnuplot "all2eps.gpl"
    
    $(DATA):

上の Makefile 中には繰り返しの記述が無いことに注意しよう.無駄もバグの温床もなくなったことになる.

  実習

余裕があるならば make の文法を調べ,上の Makefile を理解しよう.

レポート

以下の課題について能う限り賢明な調査と考察を行い,
AppliedMath7-Report-08
という題名をつけて e-mail にて教官宛にレポートとして提出せよ. なお,レポートを e-mail の代わりに TeX で作成した書面にて提出してもよい.

課題

  1. Makefile にさらに追加して,できたグラフファイルを圧縮する機能をつけてみよ.
  2. Makefile を変更して、make を実行すると、以前作ったグラフファイルをどこかへバックアップしてから新しいグラフを作るようにしてみよ.
  3. TeX が使える者は,今回の授業例を修正して TeX の編集作業に make を使えないか考えてみよ.