授業資料/11 の変更点


#CONTENTS

//  第 11 回 -- make

* make [#x2934b0e]

make とは,Makefile と呼ばれるレシピファイルに書かれたレシピに沿って作業を自動的にしてくれるツールである((さしずめ make は料理人というところか)).
特徴としては,

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

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

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


&br;&br;
** Reference: make 参考資料 [#e5495a35]

&br;&br;
- [[&ref(/materials/JNorth_arrow-right-sm.png); GNU Make:http://www.gnu.org/software/make/]]
… 泣く子も黙る GNU の Make. まあ,ここを情報の起点としておけば間違いない.最新バージョンは 3.82 か? まあ,make を自分でインストールする必要になることはまずないだろう.

&br;&br;
- [[&ref(/materials/JNorth_arrow-right-sm.png); GNU Make マニュアルの日本語訳:http://www.ecoop.net/coop/translated/GNUMake3.77/make_toc.jp.html]]
… GNU Make のマニュアルを日本語に訳された方(いのまた みつひろ様)が居る.ライセンスは GPL だ.感謝して,ありがたく読ませてもらおう.

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

&br;&br;
** 101 of make: make の基礎 [#n5f6a579]

*** Make salad: サラダに例える: Makefile [#ce4b3c27]

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

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

&br;
- 完成品,途中経過品,材料,といった「モノ」の''依存関係''
- と,その途中経過での''操作''
&br;&br;

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

&br;&br;
■ サラダ作成時の「モノ」の''依存関係'' ■
| ターゲット品 | そのターゲットに必要な品 |h
| サラダ完成品 | ・適度な大きさに切られた清潔かつ新鮮な野菜&br; ・ドレッシング&br; ・綺麗な器 |
| 適度な大きさに切られた&br; 清潔かつ新鮮な野菜 | ・清潔かつ新鮮な野菜 |
| 清潔かつ新鮮な野菜 | ・新鮮な野菜 |
| ドレッシング | ・酢&br; ・サラダ油&br; ・醤油&br; ・塩&br; ・コショウ |

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

&br;&br;
■ サラダ作成時に作ったり利用したりする「モノ」の''依存関係''と''作業'' ■
| ターゲット品 | そのターゲットに必要な品 |h
| サラダ完成品 | ・適度な大きさに切られた清潔かつ新鮮な野菜&br; ・ドレッシング&br; ・綺麗な器 |
|>|RIGHT:BGCOLOR(yellow):必要品の最初を最後に入れ,二番目をふりかける |
|>||
| 適度な大きさに切られた&br; 清潔かつ新鮮な野菜 | ・清潔かつ新鮮な野菜 |
|>|RIGHT:BGCOLOR(yellow):必要品を適当にカット |
|>||
| 清潔かつ新鮮な野菜 | ・新鮮な野菜 |
|>|RIGHT:BGCOLOR(yellow): 必要品を洗う |
|>||
| ドレッシング | ・酢&br; ・サラダ油&br; ・醤油&br; ・塩&br; ・コショウ |
|>|RIGHT:BGCOLOR(yellow): 必要品を適量,適度に混ぜる |

&br;&br;
この表に従って,一番最初のターゲット品について
ターゲット品がもしも無ければ「作業」で作り,その時に必要な品がなければそれがターゲット品として書かれている部分を読んで以下同様に…ということを再帰的に繰り返せばサラダが完成することになる.
丁寧に考えれば流れがわかるだろう.
よって,これで make 流のレシピの完成である.
しかも実際の作業は make という料理人がやってくれるのだから,われわれの仕事
はこのレシピを書くだけで終了なのだ.

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

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

>  サラダ完成品:␣適切清潔新鮮野菜␣ドレッシング␣綺麗な器
>  ____␉____必要品の最初を最後に入れ,二番目をふりかける
>  &br;
>  適切清潔新鮮野菜:␣清潔新鮮野菜
>  ____␉____必要品を適当にカット
>  &br;
>  清潔新鮮野菜:␣新鮮野菜
>  ____␉____ 必要品を洗う
>  &br;
>  ドレッシング:␣酢␣サラダ油␣醤油␣塩␣コショウ
>  ____␉____ 必要品を適量,適度に混ぜる

  * ただし, ____␉____ は, タブ(tab キーを一回押すと入力できる)のことである.

&br;&br;
ここで必要な文法はたった2つで,
&br;&br;
CENTER:&size(24){''依存関係は 「ターゲット品 : 必要品」 と書く''};
&br;&br;
CENTER:&size(24){''作業は次行に 「____␉____作業」 と書く''};

&br;&br;
だけである.
簡単だろう!

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

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

&br;&br;
*** What will happen if you change something and "make" again? サラダに例える: 途中からやり直すとしたら? [#h82a8bf0]

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

するとこの場合,必要な作業は

+ ドレッシングの作成
+ サラダの完成(適切清潔新鮮野菜はさっき作って余っているものを使えるので)

という,たった2行程で良い.
しかし,この「2行程で良い」という具体的な事実を料理人(make)にどうやって指示すればよいか?
実はこの指示は具体的に伝えなくてよいのだ.
make が「モノ」の製造時間を比較して勝手に判断してくれるのである.

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

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

&br;&br;
CENTER:&size(24){''make と打つだけで必要な作業だけやり直してくれる!''};
&br;&br;

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

&br;&br;
* The simplest example: 単純な例 [#b1e94e0c]

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

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

+ まず,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;
  }
&ref(/materials/warning.png); 何をするプログラムか, 読解しておこう.
&br;
&br;
+ 同じディレクトリに Makefile という名前のファイルを作り,次の中身を書き込んでおく.
>  f_sin.dat:&#9251;f_sin.exe
>  ____&#9225;____ \rm&#9251;-f&#9251;f_sin.dat
>  ____&#9225;____ ./f_sin.exe&#9251;>&#9251;f_sin.dat
>  &br;
>  f_sin.exe:&#9251;f_sin.c
>  ____&#9225;____ gcc&#9251;-lm&#9251;f_sin.c&#9251;-o&#9251;f_sin.exe
<
&ref(/materials/warning.png); 2, 3, 6行目は「タブ」+「作業コマンド」の形式で書くこと!!!この形式でないとまずい((タブでない文字を使うと,make したときに,「分離記号を欠いています (8個の空白でしたが,TAB のつもりでしたか?)」なんて言われたりする.親切だなあ.)).
&br;
&ref(/materials/warning.png); タブを明記するために,いつもと少し違う表記をしている.
コピペできないので自力で入力しよう.
&br;
&ref(/materials/warning.png); 何をするレシピか, 読んで確認しておこう.
&br;
&br;
+ ''make'' してみる.
> ''make''
<
&br;
&br;
+ 問題がなければ,f_sin.c というプログラムがコンパイルされ,実行され,その結果の f_sin.dat というデータファイルができているはずだ.
その内容を,例えば次のようにして覗いてみよう.
> ''less&#9251;f_sin.dat''
<
&br;
&br;
+ 結果のデータファイルを消してみよう.
> ''rm&#9251;f_sin.dat''
<
&br;
&br;
+ 再び ''make'' してみる.
&br;
&ref(/materials/warning.png); この時,どの作業が行われるか,注意深く見ておこう.先と異なるはずだ.
> ''make''
<
&br;
&br;
+ 再び実行結果ができているはずなので確認しよう.
> ''less&#9251;f_sin.dat''
<
&br;
&br;
+ 今度はプログラムを修正してみよう.
具体的には,f_sin.c の for の行を
    for ( x = 0.0; x <= 1.0+dx; x += dx) {
と修正してみる(0.5 だったところを 1.0 に修正する).
&br;
&br;
+ 再び ''make'' してみる.
> ''make''
<
&br;
&br;
+ 今度の実行結果は異なるはずだ.見てみよう.
> ''less&#9251;f_sin.dat''

&br;
&ref(/materials/warning.png); いろんな変更をしても,いつでも ''make'' コマンドだけで済む! 便利なことに気付こう.

&br;&br;
* Simple example: ちょっと複雑になった例 [#u26f3208]

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

&br;&br;
&ref(/materials/notes.png); 実習: 
先の続きの形になる.続きとして,以下のようにやってみよう.

+ 先と同じディレクトリに sin2eps.gpl という名前のファイルを作り,次の中身を書き込んでおく.
  set output "f_sin.eps"
  set term postscript
  plot "f_sin.dat" w l
&br;
&ref(/materials/warning.png); 最後の行の最後の文字は l(小文字のエル)だ.
&br;
&ref(/materials/warning.png); 最後の行をきっちり改行しておくことを忘れないように!
&br;
&br;
+ Makefile の中身を以下のように修正する(先頭に三行が加わる).
>  f_sin.eps:&#9251;f_sin.dat
>  ____&#9225;____ gnuplot&#9251;"sin2eps.gpl"
>  &br;
>  f_sin.dat:&#9251;f_sin.exe
>  ____&#9225;____ \rm&#9251;-f&#9251;f_sin.dat
>  ____&#9225;____ ./f_sin.exe&#9251;>&#9251;f_sin.dat
>  &br;
>  f_sin.exe:&#9251;f_sin.c
>  ____&#9225;____ gcc&#9251;-lm&#9251;f_sin.c&#9251;-o&#9251;f_sin.exe
<
&ref(/materials/warning.png); 「作業」の行は「タブ」+コマンドであることを忘れずに!
&br;
&ref(/materials/warning.png); ここも,タブを明記するためにいつもと少し違う表記をしている.
コピペできないので自力で入力しよう.
&br;
&ref(/materials/warning.png); 何をするレシピになったか, これも読んで確認しておこう.
&br;
&br;
+ ''make'' してみる.
> ''make''
<
&br;
&br;
+ 問題がなければ,f_sin.eps という画像ファイルができているので,見てみよう.
GUI でそのファイルをマウスでダブルクリックするもよし,次のコマンドでもいけるだろう.
> ''gv&#9251;f_sin.eps&#9251;&''

&ref(/materials/warning.png); このコマンド名は環境によっては,''gv'' ではなく,''ggv'' かもしれない. ''gv'' ではダメなときは試してみよう.

このように,作業が何段階かになっていても問題なく使える.
&br;
このように,作業が何段階かになっていても make は問題なく使える.

&br;&br;
* Something complicated: もうちょっとだけ続くんじゃ… [#nb0ea731]

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

&br;&br;
&ref(/materials/notes.png); 実習: 
先の続きの形になる.続きとして,以下のようにやってみよう.

+ 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;
  }
&ref(/materials/warning.png); 何をするプログラムか, 読解しておこう.
&br;
&br;
+ 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;
  }
&ref(/materials/warning.png); これも何をするプログラムか, 読解しておこう.
&br;
&br;
+ Makefile の中身を以下のように修正する(だいぶ変わるので注意深く作業しよう).
>  all.eps:&#9251;f_sin.dat&#9251;f_cos.dat&#9251;f_sqr.dat
>  ____&#9225;____ gnuplot&#9251;"all2eps.gpl"
>  &br;
>  f_sin.dat:&#9251;f_sin.exe
>  ____&#9225;____ \rm&#9251;-f&#9251;f_sin.dat
>  ____&#9225;____ -./f_sin.exe&#9251;>&#9251;f_sin.dat
>  ____&#9225;____ ./f_sin.exe&#9251;>&#9251;f_sin.dat
>  &br;
>  f_cos.dat:&#9251;f_cos.exe
>  ____&#9225;____ \rm&#9251;-f&#9251;f_cos.dat
>  ____&#9225;____ -./f_cos.exe&#9251;>&#9251;f_cos.dat
>  ____&#9225;____ ./f_cos.exe&#9251;>&#9251;f_cos.dat
>  &br;
>  f_sqr.dat:&#9251;f_sqr.exe
>  ____&#9225;____ \rm&#9251;-f&#9251;f_sqr.dat
>  ____&#9225;____ -./f_sqr.exe&#9251;>&#9251;f_sqr.dat
>  ____&#9225;____ ./f_sqr.exe&#9251;>&#9251;f_sqr.dat
>  &br;
>  f_sin.exe:&#9251;f_sin.c
>  ____&#9225;____ gcc&#9251;-lm&#9251;f_sin.c&#9251;-o&#9251;f_sin.exe
>  &br;
>  f_cos.exe:&#9251;f_cos.c
>  ____&#9225;____ gcc&#9251;-lm&#9251;f_cos.c&#9251;-o&#9251;f_cos.exe
>  &br;
>  f_sqr.exe:&#9251;f_sqr.c
>  ____&#9225;____ gcc&#9251;-lm&#9251;f_sqr.c&#9251;-o&#9251;f_sqr.exe
<
&ref(/materials/warning.png); 「作業」の行は「タブ」+コマンドであることを忘れずに!
&br;
&ref(/materials/warning.png); ここも,タブを明記するためにいつもと少し違う表記をしている.
コピペできないので自力で入力しよう.
&br;
&ref(/materials/warning.png); 何をするレシピになったか, これも読んで確認しておこう.
&br;
&br;
+ 先と同じディレクトリに all2eps.gpl という名前のファイルを作り,次の中身を書き込んでおく.
  set output "all.eps"
  set term postscript
  plot "f_sin.dat" w l,"f_cos.dat" w l,"f_sqr.dat" w l
&br;
&ref(/materials/warning.png); 最後の行の縦棒っぽい文字は全て l(小文字のエル)だ.
&br;
&ref(/materials/warning.png); 最後の行をきっちり改行しておくことを忘れないように!
&br;
&br;
+ ''make'' してみる.
> ''make''
<
&br;
&br;
+ 問題がなければ,all.eps という画像ファイルができているので,見てみよう.
GUI でそのファイルをマウスでダブルクリックするもよし,次のコマンドでもいけるだろう.
> ''gv&#9251;all.eps&#9251;&''
<
&br;
&br;
+ プログラムのいずれかを適当に修正してみよう.内容はグラフがおかしくならないようなものならなんでも良い.
&br;
&br;
+ 再び ''make'' してみる.
> ''make''
<
&br;
&br;
+ 今度の実行結果は異なるはずだ.見てみよう.
> ''gv&#9251;all.eps&#9251;&''


&br;&br;
* Advanced syntax: make の文法をきちんと使うと楽できる例 [#i34c794b]

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

>  .SUFFIXES&#9251;:&#9251;.dat&#9251;.exe
>  DATA&#9251;=&#9251;f_sin.dat&#9251;f_cos.dat&#9251;f_sqr.dat
>  &br;
>  .c.exe:
>  ____&#9225;____ gcc&#9251;-lm&#9251;$<&#9251;-o&#9251;$@
>  &br;
>  .exe.dat:
>  ____&#9225;____ \rm&#9251;-f&#9251;$@
>  ____&#9225;____ ./$<&#9251;>&#9251;$@
>  &br;
>  all.eps:&#9251;$(DATA)
>  ____&#9225;____ gnuplot&#9251;"all2eps.gpl"
>  &br;
>  $(DATA):

&br;
&ref(/materials/warning.png); 上の Makefile 中には繰り返しの記述が無いことに注意しよう.無駄もバグの温床もなくなったことになる.
&br;
&ref(/materials/notes.png); 実習:(余裕があるならば) make の文法を調べ,上の Makefile を理解しよう.

&br;&br;
* Report: レポート [#y47d9aaf]

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

** Exercises: 課題 [#uac504e0]

+ Makefile を修正して,プログラムファイル(.c とついているもの)のバージョン管理をさせるようにしてみよ.
&br;
&br;
+ Makefile にさらに追加して,できたグラフファイルを圧縮する機能をつけてみよ.
&br;
&br;
+ TeX が使えるものは,今回の授業例を修正して TeX を絡められないか考えてみよ.

&br;&br;
* about Icons, ClipArts [#y81824b1]

For details, see [[&ref(/materials/JNorth_arrow-right-sm.png); this>materials]].

// ━┃┏┓┛┗┣┳┫┻╋


// コマンドライン入力は「行頭を > で始める」.
// コマンドライン出力は「行頭をブランクで始める」.

// 実習アイコン
// &ref(/materials/notes.png); 

// 注意アイコン
// &ref(/materials/warning.png);

// Link アイコン
// &ref(/materials/JNorth_arrow-right-sm.png);

// OK アイコン
// &ref(/materials/OK.png);

// NG アイコン
// &ref(/materials/NG.png);

// サンプルアイコン
// &ref(/materials/Gnome-Preferences.png);

// 大文字での強調 
// CENTER:&size(24){''ほげほげ''};

// programu source 表記
// #highlighter(language=ruby,number=on,cache=on){{}}

// パイプ
// &brvbar;

// 空白記号
// &#9251;

// タブ記号
// &#9225;