CentOS 5をプログラミング環境で使う

RPMパッケージ作成メモ

RPMパッケージを作成するためのメモ書き

LinuxのRPMパッケージを自分で作るためのノウハウ集です。

RPMパッケージ作成に必要なツールのインストールと環境設定

rpmbuildパッケージ

大抵のLinux OSのディストリビューションには含まれていますが、オプション扱いでインストールされていないこともあります。

# yum install rpmbuild
   :

環境設定

RPMパッケージ作成はroot権限で作業するとエラー時に悲惨な目にあうので、必ず一般ユーザ権限で作業します。

ユーザのホームディレクトリに、RPMパッケージ作業用ディレクトリを作成します。

~$ mkdir -p rpm/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
~$ ls rpm
BUILD  RPMS  SOURCES  SPECS  SRPMS
~$

rpmbuildコマンド実行時に、上述のホームディレクトリに作った作業ディレクトリを使用するための設定ファイルを~/.rpmmacrosに記述します。

%_topdir /home/torutk/rpm
%packager Toru Takahashi <torutk@example.org>
%_sysconfdir /etc

注記)GPG鍵による署名については、本ページでは記載していませんが、インターネット上などで公開する場合は、思わぬ悪意から守るために署名をするべきです。

ソース・パッケージからバイナリ・パッケージを作成する

src.rpmを入手すれば、簡単にバイナリ・パッケージをビルドすることができます。

~$ rpmbuild --rebuild xyz-1.2.3-4.src.rpm
   :

~/rpm/RPMS/i386の下に、バイナリパッケージが生成されます。

パッケージ作成時のエラー対処

エラーが発生する場合、大抵はspecファイルを調整することになります。

ソース・パッケージからspecファイルを取り出す

tarballからバイナリパッケージのビルド(spec対応tarballの場合)

specファイルが提供されているtarballの場合、比較的簡単にRPMバイナリパッケージを生成することができます。

まず、tarballからspecファイルを取り出し、~/rpm/SPECSの下に置きます。

tarballを~/rpm/SOURCESの下に置きます。

~$ cd rpm/SPECS
SPECS$ rpmbuild -ba xyz.spec
    :

rpmbuildコマンドの主要オプションは以下です。

-bp  %prepセクションを実行
-bc  %prepと%buildセクションを実行
-bi  %prepと%buildと%installセクションを実行
-bb  %prepと%buildと%installセクションとバイナリパッケージ作成を実行
-ba  %prepと%buildと%installセクションとバイナリパッケージ作成とソースパッケージ作成を実行
-bl  %filesセクションの検査を行う
--short-circuit -bc  %buildセクションを実行(%prepを飛ばして)
--short-circuit -bi  %installセクションを実行(%prepと%buildを飛ばして)
--nobuild  何もビルドをせず、specファイルの検査を行うのに使う

specファイルを作成してソースコードからバイナリパッケージをビルド

specファイルを1から作成する点を除き、上記tarballからのビルドと一緒です。

RPMパッケージファイルの命名習慣

名前 - バージョン - リリース . ディストリビューション . アーキテクチャ . rpm
名前
ソフトウェア名を指定します。
バージョン
数字とピリオドの組み合わせで、通常3桁で指定します。(例: 3.4.5)
リリース
RPMパッケージのリリース番号で、ソースコードは変更なくパッケージのビルド手順上の修正がある場合にリリース番号をインクリメントしていきます。
ディストリビューション
ディストリビューションを識別する文字列を指定することがあります。(例:fc10、el5)
アーキテクチャ
パッケージのバイナリが実行可能なアーキテクチャを指定します。(例: i386、i686、noarchなど)

specファイルの書き方

RPMパッケージ最大の難関が、このspecファイルを作成することです。specファイルを極めることが、RPMパッケージ職人につながります。

specファイルの構成

specファイルの構成を簡単に記述すると以下になります。

イントロダクション セクション
description セクション
prep セクション
build セクション
install セクション
clean セクション
files セクション

イントロダクション セクションの記述

specファイルの先頭に記述します。

Name: xyz
Version: 1.2.3
Release: 1%{?dist}
Group: Utilities
Vendor: Example Company
URL: http://www.example.com/product/xyz
Packager: Toru Takahashi <torutk@example.com>
License: MIT
Summary: Tools for doing something about xyz.
Summary(ja): xyzについてなにかをするツール
Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildArch: i386
Source: xyz-1.2.3.tar.gz
Patch1: xyz-cvs.patch

Name、Version、Releaseは3大重要項目ですから、しっかり定義します。

Groupは、/usr/share/doc/rpm-4.1/GROUPSに定義されているものから選択するのがよいでしょう。

Summary(ja)は、日本語ロケールでのパッケージ概要を表示するために使われます。(省略可)

ソースファイル(アーカイブ)が複数あるときは、Source0, Source1, …とSourceに数値を連番で付けて指定します。

パッチファイルが複数あるときは、Patch0, Patch1, …とPatchに数値を連番で付けて指定します。

Source0: xyz-1.2.3.tar.gz
Source1: abc-0.1.tar.gz
Patch0: xyz-1.2.3-p3.patch
Patch1: xyz-1.2.3.cvs.patch

これらソースファイルとパッチファイルは、パッケージ作成時はSOURCESディレクトリの中に置きます。

VendorとPackagerは、パッケージを作成する団体・作業者を記述します。

Licenseは、ソースコードのライセンスを記載します。

BuildArchには、このパッケージのアーキテクチャ(CPU種別)を指定します。x86 Linux(CentOS 5)では、この指定を省略した場合、i386が適用されます。コンパイルオプションでCPU限定の指定をする場合、i686のように限定するCPUアーキテクチャ名を記述します。CPU依存バイナリがないパッケージ(スクリプト言語等)は、noarchを指定してもよいでしょう。

Release

Releaseはソースファイルには手を入れずに、ビルドに関する設定を修正したときにインクリメントします。ディストリビューション識別は共通のSPECファイルから複数ディストリビューション(バージョン)に対応できるよう、rpmbuildコマンドのコマンドラインオプションでdistマクロを指定したときに付与されるように記述しています。
※SPECファイルにdistを定義すると、ディストリビューション(バージョン)ごとにSPECファイルを修正しなくてはならないため

$ rpmbuild -ba --define="dist .el5" xyz.spec

Tips SourceにはURLでアーカイブを指定してもよい

Sourceには、URLでファイルを指定することができます。rpmbuild実行時にURLからファイルをダウンロードします。

マクロ定義の確認

specファイルで使用するマクロがあります。この値を知りたいときは、rpmコマンドの--evalオプションで確認できます。

$ rpm --eval %{_libdir}
/usr/lib
$
マクロ 定義
_prefix
/usr -
_exec_prefix
/usr
%{_prefix}
_lib
lib
-
_bindir /usr/bin
%{_exec_prefix}/bin
_libdir /usr/lib
%{_exec_prefix}/%{_lib}
_datadir /usr/share
%{_prefix}/share
_includedir /usr/include
%{_prefix}/include
_sysconfdir
/etc
-
_initrddir
/etc/rc.d/init.d
%{_sysconfdir}/rc.d/init.d
※Fedora 9以降では非推奨となっている(代わりに%_initddirを使う)

一覧するには、rpmbuildコマンドの--showrcオプションで可能です。

_docdir

マクロ定義に_docdirはないが、specファイルで_docdirを使用しているものがあります。_docdirは実行時に確定するマクロで、事前に定義されているマクロ_defaultdocdirが適用されるようです。

descriptionセクション

パッケージ内容を詳細に記述するセクションです。

%description
The xyz utilities are used for software development activities. 
The main activities are in construction and unit testing.
    :

%description -l ja
XYZユーティリティは、ソフトウェア開発作業に役立つツールを収めています。
特に、ソフトウェアのビルドおよび単体テスト作業に使うことを目的としています。
  :

本記述は、rpmコマンドでパッケージ内容を調べるときに見ることになるので、丁寧に記載しましょう。記述量はおおむねコンソール画面で半分程度の量がいいのではないかと思います。(80桁で10行強)

prepセクション

ソースコードのアーカイブファイルを作業ディレクトリ(BUILD)に展開し、パッチファイルがあればパッチを適用する処理を記述するセクションです。

パッチファイルがなく、ソースコードのアーカイブがRPM命名習慣に則って作成されていれば、以下の記述だけで十分です。

%prep
%setup -q

パッチがある場合、%patch を指定します。

%patch0 -p0
%patch1 -p0

%setupの実行内容

%setupは、SOURCESディレクトリにあるソースコードアーカイブをBUILDディレクトリの下に展開します。そして、ソースコードのディレクトリ内にカレントディレクトリを移します。

たとえば、イントロダクション・セクションに以下の記述を行っていた場合、

Name: xyz
Version: 1.2.3
Release: 1
Source: xyz-1.2.3.tar.gz

まず、BUILDディレクトリで、xyz-1.2.3.tar.gzを展開します。そして、BUILD/xyz-1.2.3へcdします。このcdするディレクトリは、%{name}-%{version}です。したがって、あらかじめソースコードアーカイブがこの命名に従って作られていないとエラーとなってしまいます。

しかし、いくつかのソースアーカイブはRPMの命名に従っていません。そのときは、%setupに-nオプションでcdするディレクトリ名を指定します。

%setup -q -n xyz_1_2_3

%patchの実行内容

UNIXのpatchコマンドを呼び出します。パッチファイル作成時のディレクトリ指定によって適切な-pオプションを指定します。

%patchコマンドは、%setupコマンドでソースコードアーカイブが展開されそのディレクトリ内にcdした後に実行されるので、パッチファイル作成時にソースコードアーカイブのトップレベルディレクトリがパッチファイルに含まれていれば、-p 1でトップレベルディレクトリを除外します。

buildセクション

展開・パッチが適用されたソースコードをビルドします。

configure/makeでビルドするtarballの場合は以下の記述となります。

%build
%configure
make

%configureは、その実行しているマシン環境のデフォルト設定をコマンドラインオプションで展開します。以下はCentOS 5.2のx86マシン上での展開内容です。

$ rpm --eval "%configure"

  CFLAGS="${CFLAGS:--O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions \
 -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i386 -mtune=generic \
 -fasynchronous-unwind-tables}" ; export CFLAGS ;
  CXXFLAGS="${CXXFLAGS:--O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions \
 -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i386 -mtune=generic \
 -fasynchronous-unwind-tables}" ; export CXXFLAGS ;
  FFLAGS="${FFLAGS:--O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions \
 -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i386 -mtune=generic \
 -fasynchronous-unwind-tables}" ; export FFLAGS ;
  for i in $(find . -name config.guess -o -name config.sub) ; do
           [ -f /usr/lib/rpm/redhat/$(basename $i) ] && /bin/rm -f $i && \
 /bin/cp -fv /usr/lib/rpm/redhat/$(basename $i) $i ;
  done ; 
  ./configure --build=i686-redhat-linux-gnu --host=i686-redhat-linux-gnu \
        --target=i386-redhat-linux-gnu \
        --program-prefix= \
        --prefix=/usr \
        --exec-prefix=/usr \
        --bindir=/usr/bin \
        --sbindir=/usr/sbin \
        --sysconfdir=/etc \
        --datadir=/usr/share \
        --includedir=/usr/include \
        --libdir=/usr/lib \
        --libexecdir=/usr/libexec \
        --localstatedir=/var \
        --sharedstatedir=/usr/com \
        --mandir=/usr/share/man \
        --infodir=/usr/share/info

installセクション

ビルド完了後、インストールを実行します。ここでのインストールは、RPMパッケージを作成するための作業用なので、本当のインストール先ではなく、作業用のインストール先を指定します。

configure/makeでビルドするtarballの場合は以下の記述となります。

%install
make install PREFIX=$RPM_BUILD_ROOT/usr

make installが用意されていない場合、installコマンドやcpコマンド等を使ってファイルのコピーを行います。シェルスクリプトとほぼ同様に記述できます。

installコマンドの使い方

#ディレクトリを作成
install -m755 -d $RPM_BUILD_ROOT/usr/share/%name-%version/config

#ファイルを1つコピー
install -m755 README $RPM_BUILD_ROOT/usr/share/%name-%version/README

#ファイル群を指定ディレクトリの下へコピー(再帰的にはコピーされない)
install -m755 config/* $RPM_BUILD_ROOT/usr/share/%name-%version/config

最初%{name}-%{version}のように記述していたら、シェル実行時に{xyz}-{1.2.3}のように展開されたため、{}を除去しています(CentOS 5.2/rpmbuild 4.4.2)。

installコマンドでディレクトリを再帰的にコピーする方法が見当たらず、現状は都度記述しています。

サブディレクトリがあるディレクトリの中のファイルをワイルドカードで一括指定するとエラーとなります。

xyz-1.2.3
  +-- config
  |     +-- xyz_1st.dat
  |     +-- xyz_2nd.dat
  |     +-- templates
  |           +-- xyz_template.dat
  |           +-- xyz_template2.dat

ここで、以下記述をすると、templatesが複数回インストール処理されることになりエラーが発生します。

install -m755 config/* $RPM_BUILD_ROOT/etc/xyz/config
install -m755 config/templates/* $RPM_BUILD_ROOT/etc/xyz/config/templates

面倒ですが、個別に指定するしかないようです。(あるいはinstallコマンドではなくcpコマンド等でサブディレクトリごと一括コピーする)

install -m755 config/xyz* $RPM_BUILD_ROOT/etc/xyz/config
install -m755 config/templates/* $RPM_BUILD_ROOT/etc/xyz/config/templates
ドキュメントファイルについて

後のfilesセクションで指定するドキュメントファイルについては、このinstallセクションで記述していなくても、コピーされるようです。

cleanセクション

%clean
rm -rf $RPM_BUILD_ROOT

filesセクション

インストールするファイルを個別指定するのが基本形です。

%files
/usr/bin/xyzd
/usr/share/man/man1/xyz.1.gz

ワイルドカードを使って複数ファイルを指定することもできます。

/usr/share/man/man1/xyz.*

空のディレクトリを指定場所に作成する場合、%dirマクロを使います。

%dir /etc/xyz

中にファイルが存在するディレクトリを指定すると、サブディレクトリも含めてインストール対象となります。

/usr/share/xyz-1.2.3
注意)パッケージをアンインストールすると、この方法で指定したディレクトリは一括削除されます。例えば、/usr/bin といった指定はしてはいけません。ファイルを個別に指定するか、/usr/bin/* のようにワイルドカード指定します。

ドキュメントファイルとしてマークし、ドキュメント・ディレクトリを作成してインストールするファイルを%docマクロで指定します。

%doc README NEWS

ドキュメントファイルが複数ディレクトリに散在していても、ファイル指定すれば一箇所に集められます。

%doc docs/README news/NEWS changelogs/ChangeLog

これらは、%docdirの直下にファイルとして置かれます。

ドキュメント・ディレクトリを%docdirマクロで指定します。%docで指定したファイルはここに集められます。

%docdir /usr/share/man/man1

設定ファイルとしてマークします。アップデートや再インストール時、設定ファイルに変更が入っていれば、拡張子を変えて保存します。明示的に上書き禁止を指定することもできます。

%config /etc/rc.d/init.d/*
%config(noreplace) /etc/xyz/xyz.conf

ファイルのパーミッションを設定する場合、%attrマクロを使います。

%attr(0644,root,root)  /etc/xyz/xyz.conf

まとめて一括指定する方法もあります。こちらが一般的です。

%files
%defattr(-,root,root)
  :

別なファイルにファイル一覧を記述しておき、取り込むことができます。

%files -f xyz_file.list

TODO:

manファイルは拡張子.gzを付ける

%filesセクションに記述するmanファイルは、拡張子.gzを指定します。

%install
install xzy/xyz.1 $RPM_BUILD_ROOT%{_mandir}/man1

%files
/usr/share/man/man1/xyz.1.gz

ドキュメントファイル

ドキュメントファイルは、%docdirで指定したドキュメントディレクトリ直下にコピーされます。これは、%docでディレクトリ付き指定をしていた場合でも、ファイルが直下にコピーされるからです。一方、%docでディレクトリを指定した場合、ディレクトリとして%docdir直下にコピーされます。これは、rpmbuildの仕様なのか実装依存なのかは未確認です。

%docdir /usr/share/doc/xyz-1.2.3
%doc README LICENSE changelogs

changelogsがディレクトリだった場合、/usr/share/doc/xyz-1.2.3/changelogs/* としてインストールされます。

インストール後実行したいスクリプトの定義

%post
/sbin/chkconfig --add xyzd

アンインストール時に実行したいスクリプトの定義

アンインストール前に実行するスクリプトを%preunに、アンインストール後に実行するスクリプトを%postunに定義することができます。

%preun
if [ "$1" = 0 ]; then
    /sbin/service xyzd stop > /dev/null 2>&1
    /sbin/chkconfig --del xyzd
fi
exit 0

%postun
if [ "$1" -ge 1 ]; then
    /sbin/service xyzd condrestart > /dev/null 2>&1
fi
exit 0

変更ログ

%changelog
* Fri July 4 2008 Toru Takahashi <torutk@example.com>
- modify configure option

注意点

環境変数の定義はセクション毎

例えば、prepセクションで環境変数を定義しても、installセクションでは環境変数を参照しても未定義となってしまいます。セクション毎に別プロセスで実行されているように思います。

Emacsでspecファイルの編集

Emacsには、rpm-spec-modeがあり、specファイル編集支援機能を使うことができます。

マクロ

条件マクロ

条件式展開

書式

%{?評価するマクロ:式}   評価するマクロが存在(定義済み)であれば、式を展開する
%{!?評価するマクロ:式}  評価するマクロが存在しない(未定義)であれば、式を展開する

条件分岐

%if %{hoge}       否定は%if ! %{hoge}  複数条件は%if %{hoge} && %{huga}
なんちゃら
%else
かんちゃら
%endif

問題解決

rpmbuild

%filesセクション記述に誤りがあると、やり直しが大変

--short-circuitオプションは、-bcと-biオプションにしか有効でないので、%installセクション実行は完了し、%filesセクションに基づきソース・バイナリパッケージを生成するときにエラーが発生すると、再度ソースの展開からやり直すことになります。

大規模なソースコードのビルドになるほど%filesセクション記述が複雑で誤りが発生しやすいので、その都度ソースからビルドし直すステップが走ると時間と労力の浪費です。

-blオプションで%filesセクションの検査を実施する

%installsセクションの完了後、%filesセクションの検査を実施しておけば、誤りを未然に発見・修正できます。

rpmのソースコードを修正する

-bcと-biオプション以外でも--short-circuitが有効となるように、rpmのソースコードを修正してしまいます。

まず、CentOS 5のミラーサイト等(CD/DVDからでも可)から、rpm-4.4.2-48.el5.src.rpmを取得します。

specファイル

ディレクトリを移動して処理実行

%buildセクションや%installセクションで、ディレクトリを移動してスクリプトを実行する場合、以下のように記述します。

%build
make
(cd apps; make)

環境変数を定義したのに参照したら空

%prep セクションで環境変数を定義したのに、%buildセクションで環境変数を参照したら中身が空でまるで定義されていないかのようです。どうやら、セクションが異なると環境変数が引き継がれないように見えます。そこで、面倒ですが、各セクションで環境変数を定義します。

%build
export PRJ_ROOT=/home/torutk/alfa
    :
%install
export PRJ_ROOT=/home/torutk/alfa
    :

シンボリックリンクの作成

シンボリックリンクを作成する場合、%post、%postunにシンボリックリンク作成・削除の記述をしてはなりません。RPMパッケージをアップデートするとき、RPMの処理としては、最初に新しいバージョンのインストール(rpm -i)を実行し、その後古いバージョンのアンインストール(rpm -e)を実行します。そのため、%postで作成したシンボリックリンクが%postunで消されてしまいます。

また、%postで作成したファイルはパッケージ管理下には含まれないという課題も解決することができます。

シンボリックリンクのファイルを%installで作成し、ファイルとしてインストールする(%filesに記述)

そこで、シンボリックリンクのファイルを%installで作成し、%filesにそのファイルを記述します。すなわち、通常のファイルと同じようにインストールされるようにします。

初期化スクリプト(initscripts)の設定

Linuxの初期化スクリプト(OS起動時にランレベルに応じて起動するスクリプト、/etc/rc.d/下に置かれるスクリプト)をインストール時に設定する場合の記述方法です。

Requires(post): chkconfig
Requires(preun): chkconfig
Requires(preun): initscripts
Requires(postun): initscrpits

%post
/sbin/chkconfig --add hoged

%preun
if [ $1 = 0 ]; then
    /sbin/service hoged stop > /dev/null 2>&1
    /sbin/chkconfig --del hoged
fi

%postun
if [ $1 -ge 1 ]; then
    /sbin/service hoged condrestart > /dev/null 2>&1
fi

%preunで [ $1 = 0 ]を評価しているのは、パッケージがアップデートにより削除されるのではなく、真に削除されるときのみを判定するためです。

%postunで[ $1 -ge 1] を評価しているのは、パッケージがアップデートであることを判定するためです。

デバッグ情報

RPMバイナリパッケージのライブラリファイルはstripされるため、シンボル情報が欠落し、開発・デバッグに不便です。そのときは、RPMビルド時に生成されるdebuginfoパッケージをインストールします。debuginfoパッケージがインストールされていると、gdbは自動でデバッグ版ライブラリとソースコードを参照します。

RPMパッケージ作成の情報源

書籍

日本語の書籍

Web上の資料