src.rpm を作ろう


rpm の利点として、パッケージ間の依存関係を見てくれるとか、バージョンアップが容易とか、いろいろと見聞きするが、オレ的にはなんといっても綺麗サッパリとアンインストールできること、これが一番嬉しいね。

だってそうだろ? rpm コマンドがどうやって依存関係を見ているか、といえば、パッケージの作成者が「オマエは hogehoge に依存してるんだぞコラ」と指定しているに過ぎないのだから。例えば、A というパッケージが B というパッケージ(というより B に含まれている何らかのファイル)に依存しているとしよう。A のパッケージ作成時に B に対する依存関係を指定するのを忘れたらハイそれまでよ。A は B の有無に関わらずインストールできるだろう。だが B がインストールされていなければ、もちろん正常に動きはしない。

つまるとこ、rpm による依存関係の管理なんて、人為的なミスをいくらでも含んでしまう可能性がある脆弱なものなのだ。

バージョンアップが容易というのも幻想といったら言い過ぎだろうか。だって、make install でブチ込むのだって充分容易だと思うぜ。どうせ上書きじゃん。むしろバージョンアップなどを行なった場合は、ファイルをしかるべき場所に配置することそのものよりも、設定の変更や微調整などに手間がかかるのが普通だ。例えば、極端な例だが(だが本当にあったことだ)、XFree86 がバージョンアップして、パッケージがアップデートされたとしよう。

それまでは X_LOCALE 付きで make されていたのが、バージョンアップにともなって X_LOCALE なしになったとする。XFree86 は単体でもアップデートできるよね。rpm -Uvh とかで叩っ込めば入るだろう。だが、XFree86 の上で動く多くのパッケージは、依存関係を満していてさえ、動かなくなるものが続出するはずだぜ。片っ端から差し替える必要があるだろう。

と、必ずしも rpm が寄与しない場合もあり得るワケだが、綺麗サッパリとアンインストールできること、これには rpm はものスゲ〜寄与してると思う。綺麗に消せることが、なんでそんなに嬉しいのかって?

そりゃ嬉しいさ。何らかの問題を解決するための1つの方法に、比較実験というのがあるよね。単なる試行錯誤と違うところは、変更可能な要素の1点だけを変更して比較する、というところにある。3つも4つもの要素を一挙に変更して、エイヤで試す、という方法だと、あたっていれば一発ツモだが、外しているといつまでたっても収束しない。

確実に把握できる範囲の1点を変更し、比較し確認し、また次の1点を、というように追っていくと、総当たり的に時間がかかりそうだが、得てしてこちらの方が早いうえ、確実に収束に近づいていく。こういう作業の繰り返しをする上で、最も重要なのは、1歩前の状態に確実に戻れるようにしておく、ということだ。なにか1部分を変更して、悪化したなら前の状態に戻せないと困るだろ? 最悪なのは、いろいろいじっているうちにワケワカになり、全部捨てて最初から、になってしまうことだ。

パッケージを作っておいて、rpm でインストールする、という作法は、こういう場合にこそ効果があると思う。まず最初にとにかくパッケージを作り、インストールして使ったところ不都合を発見したとする。そうしたら、後顧の憂いなく rpm -e でサッパリ消して、クサそうな箇所を微妙に調整して再度インストールして、を安心して繰り返すことができる。

make install で入れたとすると、同様な作業をする場合には上書きでインストールすることになろう。インストールされるファイルの品揃えが同じであれば良いが、増減があった場合などはどうする? その都度、手で消すか。カオスに陥る危険性がチラホラ見えているように思う。

というワケで、長々と書いてしまったが、赤帽(ないしは rpm ベースの物)を使っている以上、試行錯誤しちゃうぞ系の人は rpm を作ってからすったもんだすることをすすめるなぁ。この「覚え書き」もとうとう 50回も書いたので、このあたりで tar.gz などで固められて公開されているソースから src.rpm を作成する手順と、その時にハマりがちなことをまとめてみたいと思う。

最も良い手段は、手頃なサイズの src.rpm をインストールし、展開される spec ファイルを読むことなのだが、それ自体が初めてやろうと思えば大変なのかも知れないしね。だから、参考までに書いておく。

まず最初に、rpm を作成するうえでの作業の方針を決めておこう。オレ的にはこんな感じだ。

作業は一般ユーザで行なう。

もちろん、経験豊富な人は root でなんでも作業して構わない、と思う。これはフツ〜消さないだろ、というファイルをウカツにも消してしまったり、ということも皮膚感覚で避けるだろうし、逆に経験の少ないユーザほど、え、そんなゴイスな(絶句)というようなことを平気でカマしてしまうものだから。

一般ユーザで作業するためには、まずは rpm の作成に必要なお約束のディレクトリを用意しなければならない。デフォルトだと /usr/src/redhat 以下で作業をするようになっているが、そんなところ普通は root でなきゃ書き込めないからねぇ。

というわけで、オレの場合は、/usr/local/src/redhat というディレクトリを用意し、そのオーナーを自分にしてから、各々必要なディレクトリを掘った。

mkdir -p /usr/local/src/redhat/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
mkdir -p /usr/local/src/redhat/RPMS/{i386,noarch}

という具合だ。もしも、パーティションなどを凝った分割にしておらず、/home に大きな領域を取れるなら、自分のホームディレクトリの下にそのまま掘ってもいいだろう。

mkdir -p $HOME/redhat/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
mkdir -p $HOME/redhat/RPMS/{i386,noarch}

とするわけだ。

また、設定ファイルを作っておくのを忘れないようにしよう。rpm 2.5系であれば ~/.rpmrc に

topdir: /usr/local/src/redhat

と書く。rpm 2.9系であれば ~/.rpmmacros に

%_usrsrc /usr/local/src

と書くか

%_topdir /usr/local/src/redhat

と書いてやろう。$HOME にディレクトリを掘った人は、~(チルダ)を使わずフルパスで書いておこう。

ディレクトリ構成について

基本的に、/usr/local 以下は使わない。もちろん、/usr/local/bin に PATH を通しても、/usr/local/lib を LD のサーチパスに含めても構わない(というか普通はそうするだろう)が、rpm でインストールされるものには使わないことにする。

ぢゃあ /usr/local/bin などは何に使うの?というと、それは rpm にせず、手で make install で直接ブチ込む物のために使うのだ。パッケージにしづらいもの、するほどのものでもないがちょっと試してみたいもの、などは /usr/local/bin へ、rpm を作成してインストールするものは /usr/local は使わず /usr 以下へ、と使いわけをしよう。

なにも赤帽だからといって、必ず rpm でなければインストールできない、などということはないのだから。

また、設定ファイルの類はなるべく /etc に集中させよう。複数の設定ファイルが必要な場合は、rpm にするしないに関わらず、どこかにディレクトリを掘ってインストールされるようになっているハズだ。見通しの良さ、/etc 以下をバックアップしさえすれば設定ファイルのバックアップが完結する容易さを考慮しよう。

ログファイルは /var/log の下に吐き出すようにしよう。集中させておくと、何か不審な動きがあったときは、まず何はなくとも /var/log の下を見にいくことで何らかのキッカケも掴めよう。これも /etc と同じで、もともとそうなっているものと思うが、油断していると /usr/local/lib の下あたりにディレクトリを掘ってログを書き込もうとするものがあったりするので注意だ。

最後に、rpm はその作成過程で「仮想的なインストール」と言った動作をする。このときに、本来の / (ルートディレクトリの意)ではなく「ビルド・ルート」にインストールするのだが、それは /var/tmp の下に作ることに決めている。たとえば hoge というパッケージを作るときは、/var/tmp/hoge-root をビルド・ルートとする、といったような感じだ。

テンポラリファイルを作るなら、/tmp の下でもいいではないか、とも思うが、/var/tmp の下は誰も使っていないのに比して、/tmp の下には他のプロセスで使われている様々な「生きた」テンポラリファイルが散在している。ウッカリ消そうものなら意外な惨事に繋がりかねないので、安心のために /var/tmp 以下を使おう、というワケだ。

src.rpm とは何者か

そもそも src.rpm とは何者だろうか。その前に、まず rpm の命名マナー(規約とまではゲンミツじゃないようなのでね)を把握する必要があるだろう。

例として、そうだなぁ、パッケージ置き場の slang-1.2.2j055-3.src.rpm でも見てみよう。slang というのがパッケージの狭義な名前だ。1.2.2j055 というのがバージョン番号。3 はリリース番号だ。つまり、ファイル名としては「パッケージ名」-「バージョン番号」-「リリース番号」となっているワケだ。

「パッケージ名」には - を使ってもよいことになっているが、「バージョン番号」と「リリース番号」には使ってはイカンらしい。それ以外はもう、ほとんどなんでもアリアリだ(笑)

では src.rpm の src というのは何か、というと、そんなん source の略の src に決まってるやんけ、と言われればそうなのだが、この部分には本来は対象とするアーキテクチャが指定されることになっている。i386 とか sparc とか alpha とか。Perl や shell の script など、どのアーキテクチャでも共通なものは noarch となっている。

src.rpm というのは、 確かに source を意味するのだが、i386.rpm や noarch.rpm などと同じく rpm コマンドによってインストールして始めて使えるようになる1つのパッケージなのだ。

中には何が入っているのであろうか。見てみるのが一番だよね。てなわけで見てみる。rpm パッケージはすべて、rpm -qpl で内容物を確認できる。src.rpm も例外なくね。

Toru:~/work$ rpm -qpl SRPMS/slang-1.2.2j055-3.src.rpm 
slang-1.2.2j055.spec
slang1.2.2_j052-fix00.patch
slang1.2.2_j055-rawhide.patch
slang1.2.2_j055.tar.gz
tcp.c

tar.gz は ソースを固めたものだ。Slackware などだと、これを tar で展開して、./configure して make して… と作業するところの、いわゆる tar ball だ。その他に patch が2つに c のソースらしきものが1つ、あと slang-1.2.2j055.spec という spec ファイルが1つ。

この spec ファイルこそが、rpm パッケージを作る上での作り方、レシピといったものだ。これを記述することが rpm 作成のゴールでもある。

rpm は i386.rpm でも noarch.rpm でも必ず rpm -ivh などとしてインストールしてから使うのだが、src.rpm も例外ではない。i386.rpm をインストールすると、個々のパッケージによって様々な場所にファイルが配置されるが、src.rpm の場合はインストールされる場所が決まっている。先にディレクトリを掘り、~/.rpmrc や ~/.rpmmacros で指定した、例のディレクトリにインストールされるのだ。

各ディレクトリの役割と rpm 作成の流れ

ここで 'redhat' 以下の各ディレクトリの役割を書いておこう。

RPMS この下にさらに i386 や noarch などがあり、spec ファイルに従って作成されたインストーラブルな i386.rpm や noarch.rpm などが置かれる。
BUILD パッケージを作成する過程で、ソースの展開、パッチあて、実際の make に使われる。
SOURCES src.rpm をインストールすると、その中に含まれていたソースの tar ball やパッチなど、spec 以外のものはすべてここにインストールされる。
SPECS src.rpm をインストールすると、含まれている spec ファイルがここにインストールされる。
SRPMS spec ファイルに従って作成された src.rpm が置かれる。

spec ファイルに従ってパッケージが作成されていく過程は、大まかに 4 段階に分けることができ(本当はもっと細かいのよ)、それぞれの段階まで進めて終了するオプションが用意されている。左側がオプションで、右側がそのオプションに対応する動作の内訳だ。

-bp spec ファイルの記述に従って、ソース の tar ball を BUILD 以下に展開し、パッチなどが指定されていれば、それをあてるまでを行なう。
-bc -bp までの工程に加え、BUILD 以下に展開されたソースを用いて実際に make を行なう。
-bi -bc までの工程に加え、spec ファイルで指定された「ビルド・ルート」以下に「仮想的なインストール」を行なう。といってもやっていることは正真正銘のインストールであって、入る場所が普通と違うだけだ。

さらに追加のオプションとして便利に使えるものに、--short-circuit というものがある。rpm -bi --short-circuit などと繋げて指定するのだが、これを指定するとソースの展開と make をスキップして「仮想的なインストール」を直接行なわせることができる。

-ba 全工程を行なう。-bi までの工程に加え、spec ファイルに従って実際にインストーラブルな rpm パッケージを RPMS の下に生成すると同時に、ここまでの作業で使った spec ファイル自身と、必要とされたソース、パッチなどを src.rpm として固めて SRPMS の下に生成する。

同様なものに -bb というオプションもある。b はバイナリの b だ。-ba とほとんど同じ工程を踏むが、src.rpm は生成しない。反対に src.rpm だけを作る -bs というオプションもある。s は ソースの s だね。

/usr/local/src/redhat が rpm 作成時のトップディレクトリとすると、その位置で

rpm -ba SPECS/slang-1.2.2j055.spec

などとすると、slang の i386.rpm(バイナリ・パッケージね)と src.rpm(ソース・パッケージ)を生成してくれる、というワケだ。

以降、トップディレクトリ、と記述したときは、それはオレの例の場合だと /usr/local/src/redhat のことを示すとしよう。$HOME などにディレクトリを用意した人は適宜そう読み替えて欲しい。rpm コマンドで行う作業も、ほとんどの場合はこの「トップディレクトリ」で行なうことを前提とする。例えば、rpm -ba SPECS/hoge.spec などというように。実際にオレはそうしているからねぇ。

spec ファイルを記述する前に

rpm を作成するということは、最終的には spec ファイルを書き、それをもって rpm -ba を行なうことに帰着するのだが、それは即ち入手した tar ball を 展開し、make し、インストールする、という一連の作業をすべて内包していると言えよう。まずは手作業でそれらが正しく行なえない間は、rpm を作るためのレシピにあたる spec ファイルを記述することなんて、できるわけないよね。

だから、まずは tar ball を展開し、出てきたファイルに含まれているであろうドキュメント類をよく見よう。INSTALL や README などは必ず目を通す。とにかく、手作業で直接 make しインストールできるまで、対象とするソースの make 方法やインストール方法に精通しよう。逆に言えば、普段は何でも make install で突っ込んぢゃってるよ、というツワモノならば、spec を書いて rpm を作るのなんて、rpm 固有の作法を覚えるだけでよいので、実際には大して難しいことではない、ということになる。

さて、ネラっている tar ball を入手したとしよう。日本語化パッチはコレでね、などとすでにパッチが公開されているような場合は、それをも入手していることと思う。それら、ソースに属するものはすべて、SOURCES ディレクトリの下に置いておく。

これは 赤帽を使う人なら、もう習慣化してしまってもいいと思うぞ。ftp なり Mozilla なり w3m でもなんでもいいけど、ソースの tar玉をダウンロードするときは SOURCES ディレクトリに落とそう。そういうことが日常できるようにするためにも、一般ユーザで作業をするのがグ〜なのだ。/usr/src/redhat/SOURCES なんて、フツ〜は書き込めないでしょ?

仮に hoge-1.0.tar.gz というのをゲットし、SOURCES に置いたとする。BUILD ディレクトリに下りて、tar zxvf ../SOURCES/hoge-1.0.tar.gz として展開してみよう。こうして書くと結構なタイプ量なようだが、実際にはトップディレクトリで

cd B<TABキー>
tar zxvf ../SO<TABキー>hoge<TABキー>

くらいだから、TABキーでガンガン補完して楽しよう。で展開したら、INSTALL や README などがあればざっと目を通す。特に、ビルドの仕方を把握するのが重要だ。大きく分けると以下のようになると思う。

これらを把握した上で、rpm でパッケージにするために必要な変更を施していくわけだ。rpm 化のために必要な変更というのはオレの場合は概ね決まっていて、大雑把に3つに分けることができる。

と、こんなところか。

もう一度書くが(こればっか)変更するファイルというのはお約束が決まっていて、configure, configure.in, Makefile.in, Makefile くらいのものだ。xmkmf を使うもので、Imakefile などに変更が必要だったことはほとんどない。 xmkmf が生成する Makefile はかなり強烈に抽象化されていて、どこにインストールするかのみならず、その際の uid, gid などもインストールの時点で環境変数によって外部から変更が可能になっているからね。xmkmf を使っているものを rpm にする場合は、あまりヤヤコシイことは考えなくてもいいと思う。

パッチを作る

Makefile に対する修正について長々と書いたが、configure を使っている場合は、実際にはほとんど修正しなくても configure が面倒を見てくれることが多い。というか、そのための configure だもんね。むしろ、そういう面倒を見てくれないようでは、その configure はちょっと腐りかけている(笑)とさえ言えよう。mutt-0.95.4ijp2.spec ファイルから抜粋すると

CFLAGS="$RPM_OPT_FLAGS -I/usr/include/slang" LDFLAGS="-L/usr/lib" \
./configure --with-slang --enable-locales-fix --enable-pop \
--prefix=/usr --with-sharedir=/etc \
--sysconfdir=/etc --enable-imap --disable-warnings --disable-domain

などとエラく長くなっている。簡単にザッと説明すると、CFLAGS はコンパイラに与えるオプションだ。$RPM_OPT_FLAGS というのは rpm の作成時に与えられる環境変数で、~/.rpmrc などに記述してあるものがそのまま置換されて入ってくる。LDFLAGS というのはリンカに与えるオプションね。

肝心なのは、--prefix=/usr --with-sharedir=/etc --sysconfdir=/etc といった指定だ。これだけで、/usr/local ではなく /usr を使う、設定ファイルの類は /etc に置く、ということが指定できてしまう。手元に展開したソースがあるなら、

./configure --help | less

などとして見てみよう。ズラッと表示されると思うが、特に Directory and file names: というところを見ると、

Directory and file names:
  --prefix=PREFIX         install architecture-independent files in PREFIX
                          [/usr/local]
  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                          [same as prefix]
  --bindir=DIR            user executables in DIR [EPREFIX/bin]
  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
  --datadir=DIR           read-only architecture-independent data in DIR
                          [PREFIX/share]
  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
                          [PREFIX/com]
  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
  --includedir=DIR        C header files in DIR [PREFIX/include]
  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
  --infodir=DIR           info documentation in DIR [PREFIX/info]
  --mandir=DIR            man documentation in DIR [PREFIX/man]
  --srcdir=DIR            find the sources in DIR [configure dir or ..]
  --program-prefix=PREFIX prepend PREFIX to installed program names
  --program-suffix=SUFFIX append SUFFIX to installed program names
  --program-transform-name=PROGRAM
                          run sed PROGRAM on installed program names

などと表示されることと思う。[]に囲まれているのがデフォルト値だ。一番上の --prefix=PREFIX を見ると [/usr/local] になっている。だから、--prefix=/usr としてやるのだ。だが --sysconfdir=DIR のデフォルト値を見ると [PREFIX/etc] となっており、このままでは /usr/etc に設定ファイルがインストールされてしまいそうだ。これでは困るので、さらに --sysconfdir=/etc と指定している、という具合だ。

とはいえ、configure だけですべて解決できないこともある(腐ってたりしてね)し、対象とするソースが Makefile を手で修正してね、という配布形態である場合などは、もう Makefile を修正するしか方法がないのも事実。そういう場合にはパッチを作成してやる必要がある。

実際に修正する場合には、その前にオリジナルを保存しておこう。後にパッチを作るためにね。適当に .orig などをつけて、例えば cp Makefile.in Makefile.in.orig などとしておけばよいだろう。

まずは configure や Makefile.in などを思いどおりに修正した時点で、こちらの意図したような make をしてくれるか確認しよう。

configure に引き数を指定する必要があるなら指定して実行しよう。ここでは $RPM_OPT_FLAGS は考えなくてよいので、CFLAGS などを与える必要があるなら -g -O2 -Wall とでもしておこう。configure は無事に通ったかな? ダメだったなら通るまで試行錯誤だ(笑)

得体の知れないエラーが出て紀香困っちゃう(はぁと)な場合は、>& err.log などとしてログを取ってからゆっくりエディタで読もう。ログを取りながら表示もしたいなら tee だね。2>&1 | tee err.log とでもしよう。

無事に configure などの前準備も済んだら、いよいよ make だ。これもログを取りながらね。無事にエラーなく make できたら、次はもうインストールまでしちゃう。わはは。フリだけね。make -n install などと -n オプションをつけて make を実行し、 様子を見るのだ。具体的な例を書くと

DESTDIR=/var/tmp/hoge-root make -n install 2>&1 | less

とか

make prefix=/var/tmp/hoge-root/usr -n install 2>&1 | less

とかね。で、確かに /var/tmp/hoge-root 以下にインストールするような動作になっているか丹念に見よう。大丈夫そうだったら、今度こそ本当にインストールしてみよう。mkdir -p /var/tmp/hoge-root としてディレクトリを掘ってから、今度は -n 抜きで make install しちゃう。

エラーは出なかっただろうか。Operration not permitted とか言われなかった? 大丈夫だったなら、cd /var/tmp/hoge-root して、ディレクトリの中身を見てみよう。usr, etc, var など、自分が意図したとおりにインストールされているか確認しよう。なにしろこのディレクトリは、実際に rpm としてインストールされた場合にはルートになる場所だからね。確認、確認。

問題なければ、ここまでの修正はグ〜だ。次は、この修正を反映するパッチを作ろう。

hoge-1.0.tar.gz を BUILD 以下に展開し、hoge-1.0 というディレクトリができたとする。Makefile.in と configure を修正した場合は、パッチの作成としてはこんな感じだろうか。BUILD ディレクトリにいるとして、

diff -uNr hoge-1.0/configure.orig hoge-1.0/configure > ../SOURCES/hoge-1.0-config.patch
diff -uNr hoge-1.0/Makefile.in.orig hoge-1.0/Makefile.in >> ../SOURCES/hoge-1.0-config.patch

などとすればよいと思う。例では2回目の diff で >> で1つのパッチにくっつけちゃってるけど、違うファイル名で別々にしても構わない。spec にその分多く書かねばならなくなるけどね :-P

作成したパッチは必ずエディタで開いて確認しておこう。オペミスで実は空っぽだったりすると後で泣くぞ。悲しいぞ〜(笑) パッチがものすげ〜大きさだったなら、gzip で固めるのもアリだね。

spec ファイルを記述しよう

やっとここまできたか(笑) src.rpm の tips を書くつもりだったのに、make 一般の話になってしまっているでわないか(汗) まぁしかし、自分の考えをまとめるうえで、書くというのは実にグ〜なのでよしとしよう。しかしなげぇな。とても一息には書けんよ。休憩休憩。こんななげぇの読んでるキミ。キミも休憩しなさい(笑)

…………

休憩終わり。さて、spec を書くには、まずはお約束を知らねばならないよね。これにはやはり、読むのが一番の早道だ。mutt-0.95.4ijp2.spec を例に(汗)簡単に説明しておこう。まぁ定型文書だから、慣れれば、また1つ2つ書いてそれをテンプレートとして使い回すようにすれば、特別難しいようなもんでもないから(笑)

あ、あと余談だがオレは spec ファイルを基本的に多国語対応させていない。ほとんどの場合は英語だけで、極たま〜に日本語でも書くという程度だ。I18n が大事だ、などと普段から言ってるくせしてなんだ、といわれちゃうかも知れないが… だって、めんどくさいんだもん(笑) 文字化けするとうっとうしいしさぁ。

というわけで、mutt-0.95.4ijp2.spec の内容は以下のとおり。

Summary: The Mutt Mail User Agent + Japanese Patch
Name: mutt
Version: 0.95.4ijp2
Release: 4
Copyright: GPL
Group: Applications/Mail
Source: ftp://frp.guug.de/pub/mutt/mutt-0.95.4i.tar.gz
Patch:  localhost://mutt-0.95.4i-jp2.pat.gz
URL: http://www.mutt.org
Distribution: Kondara RPMS
Packager: Toru Hoshina <hoshina@best.com>
Vendor: Kondara RPM Project
Buildroot: /var/tmp/mutt
Requires: slang >= 1.2.2j055
# Requires: slang_jp >= 1.2.2j055

%description
Muttは軽くてパワフルなメールリーダです。スレッド表示、MIME、POP3、PGPをサポートし、キーバインディングその他広範なカスタマイズが可能です。

%prep
rm -rf   $RPM_BUILD_ROOT

%setup -n mutt-0.95.4 -q
%patch -p1

%build
chmod +x configure
CFLAGS="$RPM_OPT_FLAGS -I/usr/include/slang" LDFLAGS="-L/usr/lib" \
./configure --with-slang --enable-locales-fix --enable-pop \
--prefix=/usr --with-sharedir=/etc \
--sysconfdir=/etc --enable-imap --disable-warnings --disable-domain
make

%install
rm -rf $RPM_BUILD_ROOT
make prefix=$RPM_BUILD_ROOT/usr sharedir=$RPM_BUILD_ROOT/etc \
  sysconfdir=$RPM_BUILD_ROOT/etc \
  docdir=$RPM_BUILD_ROOT/usr/doc/mutt-%{version} install
cp -r ${RPM_BUILD_ROOT}/usr/share/locale/ja/ ${RPM_BUILD_ROOT}/usr/share/locale/ja_JP.ujis

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-   ,root,root)
%config /etc/Muttrc
%dir /etc/charsets
%config (missingok) /etc/charsets/*
%doc contrib/Mush.rc contrib/Pine.rc README contrib/sample.* NEWS
%doc COPYRIGHT doc/manual.txt contrib/language*
%attr(-   ,root,mail) /usr/bin/mutt
%attr(-   ,root,mail) /usr/bin/mutt_dotlock
/usr/man/man1/mutt.1
/usr/man/man1/mutt_dotlock.1
/usr/share/locale/*/LC_MESSAGES/mutt.mo

%changelog
* Sun Apr 11 1999 Toru Hoshina<hoshina@best.com>
- abandoned pgcc :-P
- rebuild against rawhide 1.3.3.

* Sun Apr 04 1999 Toru Hoshina<hoshina@best.com>
- added mutt_dotlock, i completely forgot to add it :-P
以下、changelog が延々と(笑)

ね。すげぇ短かいでしょ。

まず最初のブロックの、Summary から Buildroot までだが、ここはお約束なので使い回してくれ。簡単に説明すると…

Summary: はサマリね。正確には忘れた(笑)が文字長が決まってたと思う。控え目に 60 字くらいにしとこう。Name:, Version:, Release: はいいよね。mutt-0.5.4ijp2-4 の、それぞれ - で区切られた部分だ。つまり、そのまま (Name:)-(Version:)-(Release:).src.rpm などというファイル名になる、というワケ。Copyright:, Group: は適宜。

Source: にはソースの tar ball のファイル名を、できれば URL で書こう。src.rpm をインストールして spec を見た人が、入手先を容易に判断できるようにね。mutt には Source: は1つしかないが、モノによってはこれが1つじゃないこともある。tcltk なんかだと5つも6つも、XFree86 に至っては20個くらい(笑) そういう場合は、Source0:, Source1: などと数字を付けて記述するべし。区別が付くようにね。

Patch: もソースと同様だ。XFree86 だと60個以上も(しつこいわ)

Distribution:, Packager:, Vendor: は、わはは、まぁシャレだよシャレ(笑) JRPM や RHCN などに contribute するつもりなら、それぞれに指定の書式に従おう。

Buildroot: /var/tmp/mutt だが、これが今までに何度もでてきたビルド・ルートの正体だ。mutt なので mutt ね。そのまんま。慣習的には mutt-root なんだろうが… ほっほっほ。

Requires: だが、これが rpm による依存関係の管理を支えているものだ。slang >= 1.2.2j055 って書いてあるのは、slang という名前のパッケージがインストールされていて、かつバージョンが 1.2.2j055 以上ならインストールしてもよい、という意味だ。ね?たったこれだけなんだぜ。依存関係がどうのとリキんだところでこの程度なのよ、rpm なんて。

ここで、Requires: slang とだけ書けば、slang が入ってさえいればよいという意味になり、Requires: slang = 1.2.2j055 と書くと slang のバージョンが 1.2.2j055 でない場合はインストールできなくなってしまう。そう。たとえ slang 1.3.0 が入っていようがインストールできないのだ。注意しよう。

複数のパッッケージに依存している場合は、カンマで区切って必要なだけ書くことができる。

mutt-0.95.4ijp2.spec には出てこないが、他に、割とよく使うタグとして NoSource: というのがある。また、たまに使うものとして NoPatch: と BuildArchitectures: も覚えておくといいだろう。

パッケージ置き場にも末尾が nosrc.rpm というものがあるが、それはビルドに必要なソースが完全には含まれていない src.rpm を意味するのだが、それを作る場合に使うのが NoSource: タグだ。NoSource: 0 1 2 などと、src.rpm に含みたくないソースの番号を指定する。NoPatch も同様だ。

src.rpm のサイズを小さくするのが主眼だが、著作権上の問題で含むことができないソース(といってもそういうものはバイナリ配布のプラグインなどがほとんどなのだが)のような場合は、NoSource: で逃げないと配布できない src.rpm になってしまうでしょ?
あと、RedHat の2枚目CD の src.rpm をベースに手を入れたもののような場合、例えば tcltk とか XFree86 とか、src.rpm がエラくデカい割に、実際に必要なソースの大部分は CD-ROM で大多数の人が持っているだろうということが期待できるようなものは、NoSource: 指定してできるだけ小さくしてあげるのがネットワーク越しに持っていく人のためってもんだよね。

BuildArchitectures: というのは、ターゲットとなるアーキテクチャを固定するためのタグなんだけど、オレは BuildArchitectures: noarch という組み合わせでしか使ったことがない。Perl の script だったりフォント集だったり、アーキテクチャに依存しないものはこれで noarch を指定するようにしている。

その他、もうイヤになるほどタグがあるので、詳しく知りたい場合はrpm.orgから Mazimum RPM の PostScriot を落してきて見るといい(というかそれしか全貌を知る方法はないのでわ…)。オレは近所の Barns & Nobles で買ったよ。$40 くらいしたぞ(汗)

%description には文字通り、description を書く。パッケージの簡単な説明だね。
%prep はお約束で rm -rf $RPM_BUILD_ROOT としている。なくてもいいのかも知れんが、あって困るものでもないし。$RPM_BUILD_ROOT は Buildroot タグで指定したディレクトリに置換される。嘘っこのビルド・ルートを、あらかじめ消しとけよ、という意味だ。おまじないだなぁ。

%setup

ここからちょっとヤヤコシくなる。%setup にはオプションがあるのだ。mutt の例では -n mutt-0.95.4 -q というオプションがついているでしょ?

そもそも %setup ってのがなにを意味しているか、というと、Source: タグで指定されたソースを、BUILD ディレクトリに展開してくれ、まぁ言ってみればコマンドなのだ。単に %setup とだけ書くと、それは実際には BUILD 以下に tar zxvf するのと等しい。で、良く使うオプションだけ書くと

とまぁこんなところだろうか。他にもオプションはあるのだが、実際にオレが頻繁に使っているのは上にあげたものだけだ。まずはこれだけ使えれば不自由することはないと思う。

ここで注意することと言うと、%setup が終わったあとで、rpm はどこを見ているか、を意識しておくことだ。%setup で hoge-1.0.tar.gz を展開して、BUILD/hoge-1.0 ができたとすると、その時点での $PWD は BUILD/hoge-1.0 だ。-c オプションを使ってディレクトリを掘らせた場合は、そのディレクトリに cd している状態なので注意ね。

また、%setup 以降は、shell script を自由に書いてよい。ファイルのコピーや tar ball の展開なども、実は自分で好き勝手に tar zxvf fugahoge などとしてもよいのだ。この時に便利に使える環境変数をいくつか挙げておこう。

RPM_SOURCE_DIR トップディレクトリの下の SOURCES を差す。
RPM_BUILD_DIR トップディレクトリの下の BUILD を差す。
RPM_BUILD_ROOT これはもう何度か出てきてるよね。ビルド・ルートだ。
RPM_OPT_FLAGS これも何度か出てきたかな。デフォルトだと "-O2 -m486 -fno-strength-reduce" になっていると思う。~/.rpmrc に指定することで変更できる。make 時にコンパイラに渡す、という使い方をするのが普通だが、モノによっては特殊なコンパイル・オプションが義務付けの場合もあるので場合に応じて、だね。

この RPM_OPTS_FLAGS を CFLAGS として用いるように src.rpm を調整することのメリットは… そうだなぁ。例えば、~/.rpmrc を

optflags: i386 -g -O0 -m486 -fno-strength-reduce
#optflags: i386 -O2 -m486 -fno-strength-reduce

なんて書き換えてから、対象とする src.rpm を rpm -ba で作り直し、インストールし直したとしよう。こうすると、実際にインストールされた状態でデバッガにかけることができる。

Makefile をいち〜ち直して make し直すよりもはるかに楽チンだし、テスト環境のデバッガ上ではうまく動くのに、インストールしちゃうと動かないのは何故だ?などというマギレもない。

で、デバッグが終わったら、rpm -e で綺麗サッパリ消してしまい、~/.rpmrc を元に戻してから rpm --rebuild し、また入れ直す。あぁ幸せ、という寸法だ(笑)

RPM_PACKAGE_NAME
RPM_PACKAGE_VERSION
RPM_PACKAGE_RELEASE
前述の RPM_BUILD_DIR と組み合わせると、ソースを展開したディレクトリ名を得ることができる。一応(笑) でも正直に使うとすげ〜長さになって、実はあまり嬉しくないっつ〜か手で書いた方が早いぞ。

しかも %setup -n hoge などとディレクトリ名を強制的に付けてしまうと、どうせこの環境変数は使えないので、ちっとも嬉しくない(笑)から実はあまり使ってない。すまん。

%patch

これはもう見たまんまだ。patch コマンドを実行するマクロと考えるのがしっくりくるだろうか。オプションで与えることのできる -p も、普通の patch コマンドのものとまったく同じだ。唯一マクロらしい点といえば、対象としているパッチが gzip などで圧縮されていたりする場合でも、自動的に guunzip して patch を実行してくれることぐらいか。

Source: タグと同様に 0 オリジンで、Patch: と数字を省略した場合は Patch0: と等価だ。XFree86 などの大物の spec だと、%patch50 とかまであって、笑うしかない。

%setup 以下には shell script を書くことができるが、%patch もその中で自由に使える。マクロ扱いする所以もここにあるのだが、例えば

%setup -q -n hoge
%patch0 -p1
%patch1 -p0 -b .rh
cd fuga
%patch2 -p0
cd ..
cp $RPM_SOURCE_DIR/hoge.xpm $RPM_BUILD_DIR/hoge/hoge-icon.xpm

などと書くことができる。%setup 後の PWD がどこかを意識していれば、自由に移動して構わないし、上記の hoge.xpm などのように、tar ball に含まれていないファイルでも、うまく環境変数を使って指定し、操作することができる。この例の場合では、hoge.xpm はちゃんと Source1: などとして指定する必要があるのだが。

%patch1 -p0 -b .rh の -b .rh ってのはなにか、というと、パッチあてる前のファイルを、ファイル名の末尾に .rh というのを付けて取っといてちょ、という意味だ。オレ的にはあまり使わないけど、パッチが複数になってくると、うまくあたらないヤツも出てくるでしょ。そういうときに、また最初っからソースを展開して… ってやるのは切なすぎるよね。

こうしてバックアップを取っておけば、まさにパッチが失敗しているその段階から作業を簡単に続行することができるので幸せなのだ。特に XFree86 なんて、あのドデカい tar 玉を3つ展開するだけで、web日記巡りができそうだもんね(大げさ)

この段階まで spec が書けたら、実はもうすでにテストができる。前述の rpm 作成の流れのうち、最初の rpm -bp は %setup 以下の script までを実行してね、という意味だからね。ソースの tar ball を用意し、パッチを用意し、ここまでの spec が書けたなら、もうトップディレクトリで rpm -bp SPECS/hoge-1.0.spec などとして実行して構わない。

というか、オレ的にはいつもそうやって spec を書いている。最後まで書いた段階で実はうまくパッチがあたらないと判明するのも、今の時点でわかるのも同じじゃん。

%build

いよいよ実際の make 工程だ。だけど、tar ball を展開して中を見て、パッチも作り、と作業を進めてきたなら実際にやることはそれほどなかったりする。

手で make できることがわかっていて、その時の作業の内訳を覚えている(なりログを残しているなり)だろうから、それを %build の下にただ書くだけだよ。書くこと自体はちっとも難しくないよね?

mutt の例でいうと、注意する点は、そうだなぁ

chmod +x configure

なんてしてるが、これは mutt の tar玉に含まれている configure が、もともと実行フラグが立ってなかったからだ。これをしとかないと、configure が動けないのだ。こういうヒジョーに細かい作業も、忘れずに網羅する、ということと、あと

CFLAGS="$RPM_OPT_FLAGS -I/usr/include/slang" LDFLAGS="-L/usr/lib" \

などとして $RPM_OPT_FLAGS を渡しているが、こうやって「CFLAGS に渡す」という方針で Makefile を修正したのなら、渡してやらなきゃダメだ。これを忘れると、mutt の例ならば CFLAGS が空になってしまうよね。コンパイルオプションなし(笑) 場合によっては後で泣くぞ。

さて、%build 以下の script も書けたなら、もうサクサクとテストしちゃおう。トップディレクトリで rpm -bc ね。rpm -bc SPECS/hoge-1.0.spec などという感じ。この時に

rpm -bc SPECS/hoge-1.0.spec 2>&1 | tee hoge.log

などとしてログを残そう。特に1回目の rpm -bc は絶対に残す。デカいヤツだと1時間とか2時間とかかかるでしょ。あ、やべ、エラーだわ、と直面してからログを残すためにもう一回 rpm -bc してボ〜ッと待つくらいなら、最初から取っとくほうがずっといいに決まっている。エラーがなければ、中をザッと見て rm すりゃいいんだから。

無事に rpm -bc は通ったかな? 通ったら、実際に make によって生成された実行ファイルをちょっとチェックしてみよう。mutt の場合なら BUILD/mutt-0.95.4/mutt ができている。モノによっては、もっと深いところにあったり、実際にインストールされるまで隠れてるお茶目なヤツとかもいるから、ちゃんと探し出そう。例えば

BUILD/hoge-1.0/src/.libs/libhoge.so.1.0.0

などというように、ピリオドで始まるディレクトリの下に潜んでたりするぞ(笑)

確かに実行ファイル(なりライブラリ)ができていて、hoge という名前だったとしよう。おもむろに

strings hoge | less

で中を見る。strings ってのはバイナリ・ファイルの中から可読な文字列を拾い出すコマンドだ。これを less にパイプして、//var/tmp などとして、/var/tmp という文字列がないか探そう。ヒットしちゃった場合は要注意だ。例の場合だと特に

/var/tmp/hoge-root/etc
/var/tmp/hoge-root/var

なんて具合だと、こりゃかなりズイマー((c)吉田さん)だ。Makefile か configure か、とにかくその周辺に施したビルド・ルートへの対応方法が適切でない可能性があるぞ。このままインストールしちゃうと、/var/tmp/hoge-root/etc の下に設定ファイルを探しにいっちゃったりするかも。Makefile をよ〜く見直してみよう。

ビルド・ルートを/var/tmp にしなかった場合は、指定したものを適宜探す。怪しいところがなかったら次に進む。

%install

これも %build と同じで、すでにビルド・ルートへのインストールを手で一通りやってあったりするなら(というか、オレはそうしてる)、それを単にズラズラ書くだけだ。spec ファイルならではの注意点というと、rm -rf $RPM_BUILD_ROOT としてビルド・ルートをあらかじめ空にしていることくらいかな。

なくてもいいのかも知れんけど、心の平和のため(笑)にお約束で入れている。2回も3回も消しにいくことになったとしても、消えてなくて以前の(信用できない)ビルド・ルートに実わ上書きインストールしていた、なんてのよりずっとマシだ。

また、mutt の例では結局のところ、オプションを山ほど与えて make install しているだけだが、Makefile が使えなくて、全部コマンドでやってやらなければならない場合もあろう。そういう場合は、必要に応じてディレクトリも掘ってやるわけだけど、環境変数 RPM_BUILD_ROOT 大活躍の巻だ。例えば

mkdir -p $RPM_BUILD_ROOT/etc/X11/fs

なんて具合に使おう。%install の script が書けたなら、またしても速攻テストだ(笑) rpm -bi ね。

rpm -bi SPECS/hoge-1.0.spec 2>&1 | tee hoge.log

とか、rpm -bc した直後だったら

rpm -bi --short-circuit SPECS/hoge-1.0.spec 2>&1 | tee hoge.log

とすると、make の過程をすっ飛ばして %install の script だけ直に実行してくれる 。

まずは Operation not permitted のエラーが出ていないかチェックしよう。ビルド・ルート以下のディレクトリにインストールさせるように修正したつもりが、抜けがあったり spec の記述自体にミスがあったり、なんらかの原因で実際に /usr/bin とか /etc とかにインストールしにいっちゃってたりしないだろうか? または、install -g 0 -o 0 なんてのが残ってたり、どこかでそっと chown root してるなんてことは?

その手の問題がなくて、滞りなくインストールが終わっているようなら、ビルド・ルートに指定したディレクトリに cd して、ディレクトリの構成、ファイルの配置、過不足などがないか入念にチェックしよう。問題ない、と思ったらビルド・ルートの下のファイル・リストを取っておこう。

find /var/tmp/hoge-root > hoge.lst

とかしたり。で、これは次の %files で使う。

%files

インストールまで無事に進んだら、後はいよいよパッケージの箱詰めだ(笑) どういったファイルがパッケージに含まれているのか、を記述するのだが、ここから先は、パッケージが実際にインストールされた際にファイルがどこに置かれるか、を絶対パスで記述することになっているので注意だ。

あらかじめ取っておいたリスト、例でいえば hoge.lst を使って、ビルド・ルートの /var/tmp/hoge-root などを消すなどして調整していくと、上記の mutt の例のようにワイルド・カードを使ったりして短かく記述することができる。

基本的にはディレクトリは含めなくていい。例えば /usr/man とか /usr/share/locale などというディレクトリが mutt のパッケージに含まれるワケないよねぇ。そういった、システムに元々あるディレクトリは気にせず、実際に今回の %install でインストールしたファイルだけを含めるようにしよう。実際に今作っているパッケージで独自に作るディレクトリだけは明示的に書いてやろう。%dir /etc/charsets というようにね。

インストールされるファイルのオーナや属性についても、ここでは意識しなければならない。だがこれにも便利なマクロが用意されていて、それが %attr() と %defattr() だ。どちらも3つの引き数を取るようになっていて、左からファイル属性、ユーザ、グループとなっている。l

ファイル属性は数字で表記する。644 とかね。setsuid したければ、4755 (かな?) などとする(んだよね(笑)) - は省略という意味で、現在の値を変更しない。

よくある使い方としては、まず最初にガンと%defattr(- ,root,root)して、デフォルトでどのファイルも chown root.root 扱いね、としてしまう。その後、必要に応じてファイル個々に対して %attr() を使う、という感じかな。

その他のものも簡単に説明すると、%config は、このファイルは設定ファイルだぞ、ということを宣言する、というか rpm コマンドに対して指示する。いつか、このパッケージがバージョンアップした場合に、rpm -Uvh などでアップデートしても、%config で指定されたファイルは .rpmsave という拡張子が付けられて保存しておいてくれるようになる。

設定ファイルなんて、個々の環境に合わせて編集されちゃってるのが常だが、バックアップを取ってない状態で消しちゃうと結構な騒ぎになったりするよね。そういうのを防げるし、新しくインストールされたものと、.rpmsave とを比較しながら設定情報を引き継ぐのにも役に立つ。

とか言ってると、うっかりしてると /etc の下が .rpmsave だらけに(笑)

%config (missingok) というのは、rpm -V などで verify した場合に、なくなってるファイルがあってもいち〜ち騒ぐんじゃねぇ、ということを rpm に指示する。パッケージがどういう使われ方をするかを決め打つわけにはいかないので、設定ファイルとして用意されているものはあるだけパッケージに含めておくわけだけど、セキュリティ・ポリシーに合わせるために消されたり別名にされたりすることも多いはずだ。そういったファイルを見つけても、いち〜ち鬼の首を取ったように報告するなよコラ、とあらかじめ言っとくワケ。

%doc は見たとおりだ。/usr/doc/パッケージ名の下に突っ込んどくドキュメント類を列挙しておく。それだけ。

%clean

rm -rf $RPM_BUILD_ROOT と黙って書いておこう。これは意外に大事で、パッケージの作成が終わったにも関わらずビルド・ルートにドッサリとファイルを置いたまま放置しておくことがないように、忘れずに加えておこう。

XFree86 とか kernel とか tcltk とか latex とか、その手の大物をビルド・ルートに放置しておくと、/var に比較的小さ目なパーティションを割り当ててたりするとパンクしてしまい、log は書けね〜わ印刷できね〜わ、Mail も News も受け取れね〜!と泣くことになる。

さぁ、ここまで書けたかな。書けたなら、あとはお約束で

%changelog
* Thu Apr 15 1999 Toru Hoshina <hoshina@best.com>
- 1st release.

などと Change Log を書いて、一応できあがりだ。

rpm -ba SPECS/hoge-1.0.spec 2>&1 | tee hoge.log

などとカマしてみよう。パッケージはできたかな? できなかった場合は、ログを見て File not found なんてのが出てないか調べてみよう。インストールまでうまくいってたのにパッケージができないとすると、%files の記述ミスってのが一番多いから。

これは既存のパッケージをベースにしてバージョンアップした src.rpm を作ろうとするときにも遭遇するエラーで、実はオレもよくハマる(笑)

hoge-1.0-1.src.rpm というのが手元にあるとして、hoge-1.1.tar.gz がリリースされましたね、などという怪情報をゲットしたとしよう。もう速攻で hoge-1.0.spec を修正し、Source: だけを hoge-1.1.tar.gz に修正して はぁっ!! rpm -ba SPECS/hoge-1.1.specっ ! などとすると、えてしてインストールされるファイルの内訳が変っていたりして玉砕する。

インストールくらいまでビュンビュンいくんだよ。で最後の最後に、以前は FAQ というファイルがあったはずが、hoge-1.1 になって FAQ-1.1 に変わってた、とかいうツマンネ〜ところでパッケージの生成に失敗してたりするんだな(笑) File not found で(ぐはぁ)

もう一度、一般ユーザのススメ

というわけで、最後にもう一度、一般ユーザでパッケージを作るのをすすめておこう。sudo を入れてあるなら、大して不便でもないし、メリット一杯でデメリットは sudo しなきゃいかんということだけだから。

はぁ。長いのを書いちゃったなぁ。お疲れさん。本当のところは、あと

くらいは書いておきたかったんだけど、また別の機会にしよう。おなかも減ったし。このなげ〜文書を読み通せて、ここまで辿りつけた奇特な人なら、あとは他の spec をいろいろ読めば自ずとわかるだろうし。

あとは井上さんの rpm-packaging HOWTOを奥義の書として参照すべし。手元にもらってきて、ブラウザのスタートページにしとくなんてのもいいと思う。いやマジで。NetCenter やら Yahoo やらに拉致られてる場合じゃないぜ。

オレが書いてるのとはもう全然違うアプローチで(一人称なんか出て来ないのよ)、まっとうな技術文書としてとってもわかりやす〜く書いておられるので、是非読もう。いや読め(笑)


メール 戻る