2009年11月10日火曜日

【make】2 ルール (その2)

TT#4、近山先生のお話に「シェルはバッチ処理由来のジョブコントロールを引きつぎすぎていて使いづらい。全部は無理だけど、そういうこともLispの中でやって、自分の世界にひきこもれたら幸せです。」という趣旨のことがあった。いわゆるキッチンシンクのことだ。自分にとってそれはEmacs + Emacs Lisp + Common Lispになるのかなぁ。近山先生もEmacsがあれば、まあEmacsを使ってたかな、という話でもあった。

さて、make。進むにつれ、これPAIPの冒頭にあるGPS(General Problem Solver)すなわちmeans-ends analysisそのものじゃん、ということに気がついた。これは結構強力。ASDF、Rake、Antとかはこれよりさらに強力な機能をもっているのか?だとしたら、すごいな。。。


** 2.4 パターンルール
- パターン(マッチ)をつかったルール。
- 拡張子がポイント。MS Windowsとちがって、Unixで
拡張子はナンセンスと思っていたが、浅はかだった。
- 組み込み暗黙ルールというものあり。makeにあらかじ
めパターンルールが組み込まれている。

例1 .oファイルを作るルール。
------
%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
------

例2 .lファイルから.cファイルを作るルール。
------
%.c: %.l
@$(RM) $@
$(LEX.l) $< > $@
------

例3 サフィックス無し(実行ファイル)を作るルール。
------
%: %.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
------

*** 2.4.1 パターン
- '%'はシェルの'*'みたいなもの。これをつかってパ
ターンを記述する。ただし一つの単語の中で、'%'は
一回しかつかえない。
- パターンマッチの制御は次のとおり。

まず、ターゲットに着目し、マッチするファイル名が
あるかどうか探索する。マッチがあれば、マッチ部で
前提条件の'%' を置換する。置換後のファイル名が存
在するかを探索する。存在しない場合は、そのファイ
ル名がターゲットで存在するかどうか探索する。これ
ら探索で、ターゲットにマッチしたファイル名を構築
するための連鎖が特定されれば、それを実行する。こ
の連鎖をルール連鎖と呼ぶ。

ターゲットに着目したマッチが全部終わったら、前
提条件に着目したマッチを行う。前提条件のパター
ンにマッチするファイル名が存在するなら、マッチ
部でターゲットの'%'を置換する。マッチするファイ
ル名が無い場合は、ターゲットでマッチするものを
探し、連鎖をつくっていく。連鎖が終端されればそ
れを実行する。もちろんこれもルール連鎖と呼ぶ。

例。

------
%: %.sh
cat $< >$@
chmod a+x $@
------

探索対象ディレクトリに'hoge.sh'があれば、前提条
件部がマッチして'hoge'が構築される。

*** 2.4.2 静的バターンルール
- パターンルールの一種であり、特定のターゲットリ
ストのみを対象とする。

------
$(OBJECTS): %.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
------

この場合、OBJECTSに含まれているファイル名だけを
対象に、%.oのパターンマッチが実施される。


*** 2.4.3 サフィックスルール
- 'GNU make'ではない'make'で使われている古い記法。
- ポータビリティのために、現在でもこれを使うことも
ある。

例1。
------
.c.o:
$(COMPILE.c) $(OUTPUT_OPTION) $<
------

これはダブルサフィックスルールという。
これは、次のバターンルールと同じ。

------
%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
------

例2。
------
.p:
$(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
------

これはシングルサフィックスルールという。
これは、次のバターンルールと同じ。

------
%: %.p
$(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
------

- サフィックスルールの管理は、.SUFFIXESで行う。

------
.SUFFIXES: .pdf .p
------

などとすると、自分で定義したサフィックスルール
を有効にできる。

makeでデフォルト定義されているルールを無効化す
るには次のようにする。

------
.SUFFIXES:
------

** 2.5 暗黙ルールのデータベース
- 'make -p' で一覧が出力される。

**** make -p の一覧
--------
~ $ make -p
# GNU Make 3.81
# Copyright (C) 2006 Free Software Foundation, Inc.
# これはフリーソフトウェアです. 利用許諾についてはソースを
# ご覧ください.
# 商業性や特定の目的への適合性の如何に関わらず, 無保証です.

# This program built for x86_64-pc-linux-gnu
make: *** ターゲットが指定されておらず, makefile も見つかりません. 中止.

# Make データベース出力 Tue Nov 10 02:39:19 2009

# 変数

# 自動変数
<D = $(patsubst %/,%,$(dir $<))
# 自動変数
?F = $(notdir $?)
# 環境変数
DESKTOP_SESSION = default
# デフォルト
CWEAVE = cweave
# 自動変数
?D = $(patsubst %/,%,$(dir $?))

[snip]

# 環境変数
PORTABLE_ENVIRONMENT_TYPE = ubuntu
# 環境変数
SESSION_MANAGER = local/adonis:/tmp/.ICE-unix/5548
# デフォルト
COMPILE.mod = $(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH)
# デフォルト
ARFLAGS = rv
# デフォルト
LINK.r = $(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH)
# 環境変数
WINDOWID = 4194362
# デフォルト
COMPILE.f = $(FC) $(FFLAGS) $(TARGET_ARCH) -c
# デフォルト
LINT.c = $(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH)
# デフォルト
LINT = lint

[snip]

# 変数セットのハッシュテーブルの状態:
# Load=137/1024=13%, Rehash=0, Collisions=13/161=8%

# Pattern-specific Variable Values

# パターン指定変数の値なし.

# ディレクトリ

# SCCS: 状態を調べられませんでした.
# . (device 2049, inode 1286082): 109 個のファイル, 51 個の適用不能ファイル名.
# RCS: 状態を調べられませんでした.

# 109 個のファイル, 51 個の適用不能ファイル名 (3 個のディレクトリ内).

# 暗黙ルール

%.out:

%.a:

%.ln:

%.o:

%: %.o
# 実行するコマンド (ビルトイン):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

%.c:

%: %.c
# 実行するコマンド (ビルトイン):
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@

%.ln: %.c
# 実行するコマンド (ビルトイン):
$(LINT.c) -C$* $<

%.o: %.c
# 実行するコマンド (ビルトイン):
$(COMPILE.c) $(OUTPUT_OPTION) $<

%.cc:

%: %.cc
# 実行するコマンド (ビルトイン):
$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@

%.o: %.cc
# 実行するコマンド (ビルトイン):
$(COMPILE.cc) $(OUTPUT_OPTION) $<

%.C:

%: %.C
# 実行するコマンド (ビルトイン):
$(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@

%.o: %.C
# 実行するコマンド (ビルトイン):
$(COMPILE.C) $(OUTPUT_OPTION) $<

%.cpp:

%: %.cpp
# 実行するコマンド (ビルトイン):
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@

%.o: %.cpp
# 実行するコマンド (ビルトイン):
$(COMPILE.cpp) $(OUTPUT_OPTION) $<

[snip]

%:: %,v
# 実行するコマンド (ビルトイン):
$(CHECKOUT,v)

%:: RCS/%,v
# 実行するコマンド (ビルトイン):
$(CHECKOUT,v)

%:: RCS/%
# 実行するコマンド (ビルトイン):
$(CHECKOUT,v)

%:: s.%
# 実行するコマンド (ビルトイン):
$(GET) $(GFLAGS) $(SCCS_OUTPUT_OPTION) $<

%:: SCCS/s.%
# 実行するコマンド (ビルトイン):
$(GET) $(GFLAGS) $(SCCS_OUTPUT_OPTION) $<

# 86 個の暗黙ルール, 5 (5.8%) 以上.

# ファイル

# ターゲットではありません:
.web.p:
# 暗黙ルールの探索は行われませんでした.
# 修正時刻がチェックされることはありません.
# ファイルは更新されていません.
# 実行するコマンド (ビルトイン):
$(TANGLE) $<

# ターゲットではありません:
.l.r:
# 暗黙ルールの探索は行われませんでした.
# 修正時刻がチェックされることはありません.
# ファイルは更新されていません.
# 実行するコマンド (ビルトイン):
$(LEX.l) $< > $@
mv -f lex.yy.r $@

# ターゲットではありません:
.dvi:
# 暗黙ルールの探索は行われませんでした.
# 修正時刻がチェックされることはありません.
# ファイルは更新されていません.

# ターゲットではありません:
.F.o:
# 暗黙ルールの探索は行われませんでした.
# 修正時刻がチェックされることはありません.
# ファイルは更新されていません.
# 実行するコマンド (ビルトイン):
$(COMPILE.F) $(OUTPUT_OPTION) $<

[snip]

# ファイルハッシュテーブルの状態:
# Load=69/1024=7%, Rehash=0, Collisions=276/1520=18%
# VPATH 探索パス

# `vpath' 探索パスはありません

# 一般の (`VPATH' 変数) 探索パスなし.

# # of strings in strcache: 0
# # of strcache buffers: 0
# strcache size: total = 0 / max = 0 / min = 4096 / avg = 0
# strcache free: total = 0 / max = 0 / min = 4096 / avg = 0

# Make データベース終了 Tue Nov 10 02:39:19 2009

~ $
--------

*** 2.5.1 暗黙ルールを活用する
- makefileを書かずとも暗黙ルールだけでかなりやっ
てくれる。

例。これはすごい。
------
2.5.1 $ touch foo.y
2.5.1 $ ci foo.y
foo.y,v <-- foo.y
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>> .
initial revision: 1.1
done
2.5.1 $ ls
foo.y,v
2.5.1 $ make --just-print foo
co foo.y,v foo.y
foo.y,v --> foo.y
revision 1.1
done
yacc foo.y
mv -f y.tab.c foo.c
cc -c -o foo.o foo.c
cc foo.o -o foo
rm foo.c foo.o foo.y
2.5.1 $
------

- ルール連鎖の中で作成されたファイルを中間ファイ
ルと呼ぶ。中間ファイルはmakeが終了前に削除する。

- 暗黙ルールが害をおよぼすこともある。その場合、
コマンドが空のパターンルールを書けばよい。

例。

------
%.o: %.c
------

*** 2.5.2 ルールの構造
- 暗黙ルールはカスタマイズしやすいように、典型的
には次のような構造をもっている。

------
%.o: %.c
# 実行するコマンド (ビルトイン):
$(COMPILE.c) $(OUTPUT_OPTION) $<
------

ここで変数の定義は次のとおり。

------
# デフォルト
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
# デフォルト
CC = cc
# デフォルト
OUTPUT_OPTION = -o $@
------

** 2.6 特殊ターゲット

- .PHONYがその一種。makeの動作に指示を与えるもの。
- 全部で12種類ある。
- フォーマットは、コマンドなしルール。

------
ターゲット: 前提条件
------

** 2.7 自動的な依存関係の修正

- まず、コンパイラはソースを読んで、ライブラリの
依存関係をmakefileの形式で書き出してくれる。

------
2.7 $ cat hoge.c
#include <stdio.h>

2.7 $ gcc -M hoge.c
hoge.o: hoge.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/4.3.2/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.3.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h
2.7 $
------

- おお、これは便利。
- これを自動的に自分のmakefileに取り込む方法がい
くつかあり。ここでは割愛。(本には記載あり)

** 2.8 ライブラリ管理
- ライブラリの基礎
- ライブラリは、正式にはアーカイブライブラリ、
と言う。
- 包含しているファイルをメンバと呼ぶ。
- 関連するオブジェクトファイルを、まとめて、管
理する。
- アーカイブの作成・変更はarで行う。以下、例。

------
2.8 $ ls
count_words count_words.o counter.o include lexer.o makefile src
2.8 $ ar rv libcounter.a counter.o lexer.o
ar: creating libcounter.a
a - counter.o
a - lexer.o
2.8 $
------

- ライブラリを使うにはライブラリファイルをコマ
ンドラインで指定する。コンパイラやリンカは、
その取扱いを知っており、拡張子で判断して適用
する。具体的にはソースの中の未解決のシンボル
をライブラリから探す。以下、例。

------
cc hoge.o libpiyo.a /lib/libfl.a -o hoge
------

-lオプションを使う方法。

------
cc hoge.o libpiyo.a -lfl -o hoge
------

- アーカイブライブラリは、オブジェクトファイル
だけでなく、シンボルのインデックスももつこと
ができる。GNUのarはこのインデックスの作成と
更新を自動的にやってくれる。自動的にやってく
れない古いarをつかっている場合はranlibを使う。

*** 2.8.1 ライブラリの作成と更新
- makefileでライブラリを作成する典型。

------
libhoge.a: hoge.o piyo.o
$(AR) $(ARFLAG) $@ $?
------

- 既出だが、ライブラリのメンバをターゲットにする
方法。

------
libhoge.a(piyo.o): piyo.o
$(AR) $(ARFLAG) $@ $<
------

- ranlibが必要な場合は次のようにする。

------
libhoge.a: hoge.o piyo.o
$(RM) $@
$(AR) $(ARFLAG) $@ $^
$(RANLIB) $@
------

*** 2.8.2 ライブラリを必須項目として使う
- かきぶりは2つあった。

------
hoge: hoge.o /lib/libfl.a
$(LINK) $^ -o $@
------
------
hoge: hoge.o -lfl
$(LINK) $^ -o $@
------

- 後者の方がすぐれている。理由は次のとおり。

- 前者は絶対パスになっているので、配置を変えた
らmakefileの修正が必要。後者はライブラリの探
索パスだけ変えればよい。
- 後者だとmakeはシェアードライブラリの方がこの
ましいと判断し、それがあるならそれを優先する。

*** 2.8.3 二重コロンルール

- 二重コロンは、前提条件毎にコマンドを変えるとき
に使う。暗黙ルールからの例。

------
%:: RCS/%
# 実行するコマンド (ビルトイン):
$(CHECKOUT,v)

%:: SCCS/s.%
# 実行するコマンド (ビルトイン):
$(GET) $(GFLAGS) $(SCCS_OUTPUT_OPTION) $<
------


こつこつ。

0 件のコメント: