はじめに
みなさんはspecファイル書いたことありますか?
specファイルを雰囲気フワフワな状態で書いていたエンジニア(私)が、
これを機にちゃんとまとめようと思った備忘録的なそれです。
specファイルってなんぞ
rpmパッケージを作る時に必要になるレシピ的なもの
rpmbuild
コマンドを実行するときspecファイルに沿ってrpmパッケージを作成して行きます。
今回rpmbuild
コマンドを実行した時どのような流れでspecファイルが利用されていくのかに着目していきたいと思います。
おことわり
rpmbuild
コマンドは実行時$HOME/rpmbuild/
ディレクトリをトップディレクトリとして実行されます。
任意のディレクトリでrpmbuild
コマンドを実行した場合は--define
オプションを用いて_topdir
の定義を一時的に変更する必要があります。
つまり$HOME/myrpm
配下でrpmbuild
コマンドを実行しようとした場合、rpmbuild --define="_topdir $HOME/myrpm"
となります。
この記事ではspecファイルに注目するため、このようなrpmbuild
コマンドの細かい部分はそれとなーく流します。
ディレクトリ構成
rpmbuild
コマンドを実行した時に作成されるディレクトリ構成はこちら
_topdir
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
それぞれのディレクトリの用途は以下の通りです。
ディレクトリ名 | 用途 | 作成されるタイミング |
---|---|---|
BUILD | rpmパッケージを作成するときに使用する作業ディレクトリ |
rpmbuild コマンド実行時に作成される |
BUILDROOT | アプリケーションを仮想インストールする際にルートになるディレクトリ |
rpmbuild コマンド実行時に作成される |
RPMS | 完成したrpmパッケージが置かれるディレクトリ |
rpmbuild コマンド実行時に作成される |
SRPMS | 完成したsrpmファイルが置かれるディレクトリ |
rpmbuild コマンド実行時に作成される |
SOURCES | rpmパッケージに含めるソースコードを置くディレクトリ | 自分で作成する |
SPECS | rpmパッケージ作成に用いるspecファイルを置くディレクトリ | 自分で作成する |
実際にrpmファイルを作成していくときは、SPECS
ディレクトリにspecファイルを置き、SOURCES
ディレクトリにソースコードを置き、
rpmbuild
コマンドを実行し、完成したrpmパッケージをRPMS
ディレクトリから取る形になると思います。
specファイルの構成
specファイルは以下のようないくつかの章が合わさって構成されます。
- 基本情報
- スクリプト部
- prepセクション
- buildセクション
- installセクション
- checkセクション
- cleanセクション
- ファイルリスト部
- 更新履歴
rpmbuild
コマンドを実行したとき主に利用されるのがスクリプト部だと思います。
実際に私がrpmパッケージを作成したときもスクリプト部をいじっていた時間がほとんどでした。
各章・各セクションをちゃんとまとめたいと思ったのがこの記事を書くに至った経緯でもあります。
マクロの定義
specファイル内では自分でマクロを定義することができます。
頻繁に使用する記述をマクロで定義しておくと、いろいろと便利になります。
%define version 1.0
例えばこのようなマクロを定義した場合、specファイル内で%{version}
と書くと1.0
に置換されます。
また、標準で定義されているマクロもあります。先程述べた_topdir
も標準で定義されているマクロのひとつです。
標準で定義されているマクロはrpmbuild --showrc
コマンドで確認できます。めちゃめちゃあるので全部把握するのは難しいかも..
基本情報
この章にはrpmパッケージの基本情報を記述します。
これらはrpm -qi
などでパッケージ情報を呼び出す際に表示されるものになります。
基本情報として利用できるタグの一覧がこちら。
タグ | 説明 | 必須 |
---|---|---|
Summary | パッケージの簡単な説明 | ○ |
Name | パッケージ名。ここで定義した名前は以降%{name} というマクロで利用出来ます。 |
○ |
Version | パッケージのバージョン。ここで定義した値は以降%{version} というマクロで利用出来ます。 |
○ |
Release | パッケージのリリース番号。ここで定義した値は以降%{release} というマクロで利用できます。 |
○ |
License | アプリケーションのライセンス | ○ |
Group | アプリケーションの種類。CentOS7.5では/usr/share/doc/rpm-4.11.3/GROUPS 内に列挙されています。 |
|
Packager | パッケージメンテナの名前。複数いる場合はカンマ区切り | |
Url | アプリケーションの情報を提供しているURL |
また、パッケージ作成に必要なソースやパッチもここで列挙します。
タグ | 説明 | 必須 |
---|---|---|
Source0 | パッケージの作成に必要なソースファイル名を指定します。指定された名前のソースファイルをSOURCES ディレクトリからさがします。ここで指定したファイルは以降%{SOURCE0} というマクロで利用出来ます。 |
○ |
Source1... | 必要なソースファイルが2つ以上ある場合はSource1,Source2..と連番で列挙していきます。ここで指定したファイルは以降%{SOURCE1} や%{SOURCE2} のようなマクロで利用できます。 |
|
Patch0... | パッチファイルを指定します。2つ以上ある場合はPatch1,Patch2..と連番で列挙していきます。 | |
BuildRoot | パッケージ作成時に仮想インストールされるディレクトリを指定します。ここで定義した値は以降%{buildroot} というマクロで利用できます。 |
他のパッケージとの依存情報も列挙します。
タグ | 説明 | 必須 |
---|---|---|
Requires | 作成したrpmパッケージが動作するのに必要なパッケージ名を列挙します。yum コマンド等でインストールする時、ここで列挙されているパッケージも同時にインストールされます。 |
|
BuildRequires | パッケージの作成に必要なパッケージ名を列挙します。rpmbuild コマンドを実行するときに列挙されているパッケージが足りてないとエラーになります。 |
|
Conflicts | 共存できないパッケージ名を列挙します。 | |
BuildConflicts | パッケージ作成時にインストールしておけないパッケージ名を列挙します。 | |
Obsoletes | パッケージをインストールする際に、アンインストールするパッケージ名を列挙します。 |
最後にdescription
タグでパッケージの詳しい解説を記述します。
%description
This Application is
very very happy Application
長い!そして多い!
こんな量の情報を一つのファイルに書くのでそれはそれは巨大なファイルになりますよ。
そしてこれはまだまだ序の口。次は実際にパッケージのインストールなどを行うスクリプト部です。
スクリプト部
実際にrpmパッケージを作成していくスクリプトを書きます。
スクリプト部は以下のフォーマットで記述します。
%セクション名
シェルスクリプト
次のセクション名が現れるまでのシェルスクリプトを順番に実行します。
rpmbuild
コマンド実行時に-v
オプションをつけるとわかりますが、呼び出されるシェルは/bin/sh -e
として呼び出されます。
私が試しているCentOS7.5の環境ではbash
にリンクされているので、specファイルのシェルもbashの構文に則っている必要があります。
まぁspecファイルの中であんまりごちゃごちゃするものでも無いと思うので気にするほどじゃないかもしれませんが...
セクション一覧がこちら
セクション | 説明 |
---|---|
prep | ソースをビルドする前処理 |
build | ソースのビルド処理 |
install | ソースをインストールする処理 |
check | ビルド結果を検証する処理 |
clean | パッケージ完成後の後処理 |
pre | RPMパッケージをインストールする時、パッケージ展開前に行う処理 |
post | RPMパッケージをインストールする時、パッケージ展開後に行う処理 |
preun | RRMパッケージをアンインストールする時、パッケージ削除前に行う処理 |
postun | RPMパッケージをアンインストールする時、パッケージ削除後に行う処理 |
triggerin | あるパッケージがインストールされていた、もしくはされたときに行う処理 |
triggerun | あるパッケージの削除前に行う処理 |
triggerpostun | あるパッケージの削除後に行う処理 |
verifyscript | RPMパッケージを検証するときに追加で行う処理 |
この中から必要なセクションをspecファイルに記述していきます。すべて記述しなきゃいけないわけではありません。
なんなら全部なくても大丈夫です。何も実行されずrpmも作成されませんが...
最低限prep
,build
,install
はないとrpmパッケージは作成されません。check
,clean
もあるとなおいいでしょう。
その他のセクションは必要に応じて利用してください。
rpmbuild
コマンドはprep→build→install→check
の順に実行しrpmパッケージを作成します。
rpmパッケージの作成が完了したらclean
セクションを実行します。
prepセクション
ソースのビルドをする前処理を行います。
以前rpmbuild
コマンドを実行したゴミ等が残っている可能性があるため、
下記のように最初にBuildRoot
のクリーンを行うことが一般的です。
%prep
rm -rf %{buildroot}
prep
セクションでは%setup
マクロを使ってソースコードの展開を行ったり、
%patch
マクロを使ってソースコードにパッチをあてることが可能です。
それぞれのマクロの挙動は下記の通りです。
%setupマクロ
主にソースコードの展開をおこないます。
Source0
にhogehoge.tar.gz
のようなファイルが指定されていることが想定されています。
実際の動きとしては下記の通り
-
%{name}-%{version}
で指定されているディレクトリを削除します。 -
Source0
に指定されているファイルをgzip
で解凍し、tar
で展開します。 -
%{name}-%{version}
で指定されているディレクトリにcd
で移動する。 -
chmod
でパーミッションを変更する
chmodしてるのは知らなかった...
rpmbuild
コマンドは実行するときに一時ファイルにシェルスクリプトを生成し、それを/bin/sh
に読ませる形で実行しているので、
実際に生成された一時ファイルを見ることで何をやっているかがわかります。%setup
マクロが実際に行っている処理は以下のようになっていました。百聞は一見にしかず
cd '/home/shun/rpmbuild/BUILD'
rm -rf 'sample-script-1.0'
/usr/bin/gzip -dc '/home/shun/rpmbuild/SOURCES/sample-script-1.0.tar.gz' | /usr/bin/tar -xvvf -
STATUS=$?
if [ $STATUS -ne 0 ]; then
exit $STATUS
fi
cd 'sample-script-1.0'
/usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
GroupやOtherに書き込み権限があった場合はなくすようにしていたり、chmodで不要な権限は外しているんですねー
これは%{name}
がsample-script
、%{version}
が1.0
だったのでsample-script-1.0
というディレクトリを対象にしています。
対象にするディレクトリを変更したり%setup
マクロにはさまざまなオプションがあるのですがそれはまた別の機会に...
%patchマクロ
主に%setup
マクロで展開したソースにパッチをあてます。
特にオプションなく%patch
マクロを実行した場合patch -p0
が実行されます。しかし、git diff
などでパッチファイルを作ると-p1
オプションをつけるのが一般的です。
その場合は%patch
マクロに-p1
というふうにオプションを付けてあげれば実現可能です。
またパッチファイルが2つ以上設定されている場合は%patch0
や、%patch1
とすることで複数のパッチをあてることができます。
例えば以下のようになると思います。
%patch0 -p1
%patch1 -p1
この場合patch
マクロが実際に行っている処理は以下のようになっていました。
echo "Patch #0 (sample-patch01.patch):"
/usr/bin/cat /home/shun/rpmbuild/SOURCES/sample-patch01.patch | /usr/bin/patch -p1 --fuzz=0
echo "Patch #1 (sample-patch02.patch):"
/usr/bin/cat /home/shun/rpmbuild/SOURCES/sample-patch02.patch | /usr/bin/patch -p1 --fuzz=0
buildセクション
主にパッケージをビルドする処理を書きます。
%setup
マクロで展開したディレクトリにcd
してからスクリプトは実行されます。
Makefile
にビルドするルールを記述しmake
のみで終わらせるのが一般的?
個人的にはそのほうがspecファイルがスッキリするのでお気に入りです。
またautotools
を用いて開発されたソースなどで、configure
スクリプトが用意されている場合%configure
マクロが利用できます。
私自身がautotools
を使ったことが無いので今回は割愛... autotools
勉強して出直してきます。
installセクション
パッケージを仮想インストールする処理を書きます。
BUILD
ディレクトリでビルドしたアプリケーションをBUILDROOT
ディレクトリをルートディレクトリとし、仮想インストールします。
%build
セクション同様%setup
マクロで展開したディレクトリにcd
してからスクリプトは実行されます。
仮想インストールなので%{buildroot}
以下にインストールする必要があるので少し工夫が必要かもしれません。
私の場合はMakefile
内でDESTDIR
というような値のない変数を定義し、specファイル内で利用する時にmake install DESTDIR=%{buildroot}
としたりします。
以下Makefile
の例になります。
DESTDIR = #値は空のまま
.PHONY: install
install:
install -d -m 0755 ${DESTDIR}/usr/bin
install -m 0755 sample-script ${DESTDIR}/usr/bin/
specファイルの中身は以下のようになります。
%install
make install DESTDIR=%{buildroot}
こうすることでrpmbuild
コマンド実行時には%{buildroot}
配下にディレクトリやスクリプトがインストールされます。
さらに、rpm
コマンドなどで実際にインストールする際には%{buildroot}
の値が定義されないので、通常のルートディレクトリ配下にインストールされるようになります。
これはあくまで一例にすぎないので、正しくインストールができさえすれば何でも大丈夫です。
checkセクション
パッケージのビルドが正常に完了したかチェックする処理を書きます。
アプリケーションによってテストスクリプトなどがある場合はここで実行しましょう。
cleanセクション
rpmパッケージが完成した後の後処理を行う処理を書きます。
下記のようにBuildRoot
の削除などを行いましょう。
%clean
rm -rf %{buildroot}
ファイルリスト部
rpmパッケージに含まれるファイル名を列挙します。
%install
までスクリプトを実行した段階で列挙されたファイルが実際にインストールされている必要があります。
列挙されているファイルがインストールされていなかったり、逆に列挙されていないファイルがインストールされていたりした場合rpmbuild
コマンドは失敗します。
%attr
%attr
を用いるとファイルのパーミッションやuserID, groupIDまで設定が可能です。
例えば以下の用に設定したとします。
%files
%attr(0755,root,root) /usr/bin/sample-script
これは/usr/bin/sample-script
というファイルが0755
のパーミッションでuserIDがroot
、groupIDがroot
でインストールされることをあらわします。
%defattr
%defattr
を用いると以降すべてのファイルのパーミッション、userID、grouIDが指定出来ます。
例えば以下の用に設定したとします。
%files
%defattr(0755,root,root)
/usr/bin/sample-script1
%attr(0644,root,root) /usr/bin/sample-script2
/usr/bin/sample-script3
この場合sample-script1
とsample-script3
は%defattr
で設定したパーミッション0755
、userIDがroot
、groupIDがroot
になります。
ただし、sample-script2
は%attr
で上書きしているのでパーミッションが0644
となります。
%config
設定ファイル(書き換えられる可能性のあるファイル)であることを示します。
%config
にはnoreplace
とmissingok
があり、何も指定しなかった場合はデフォルトでnoreplace
になります。
noreplace
%config(noreplace)
としたファイルに変更があった場合、アンインストール時や再インストール時に新しいファイルに置き換えられなくなります。
具体的に以下のような記述をしたとします。
%files
%config(noreplace) /etc/sample/sample.conf
インストールされた設定ファイルに変更を加えます
$ echo "推しメンすちすちビーム" >> /etc/sample/sample.conf
パッケージをアンインストールすると、設定ファイルはリネームして残されます。
$ sudo rpm -e sample-script
警告: /etc/sample/sample.conf は /etc/sample/sample.conf.rpmsave として保存されました。
$
$ ls -1 /etc/sample
sample.conf.rpmsave
パッケージを再インストールすると再度設定ファイルが作られます。
$ sudo rpm -ih sample-script-1.0-1.el7.x86_64.rpm
################################# [100%]
更新中 / インストール中...
################################# [100%]
$ ls -1 /etc/sample/
sample.conf
sample.conf.rpmsave
またパッケージ側で設定ファイルに変更があった場合は、新しい設定ファイルはインストールされずに、
ファイル名に.rpmnew
を付けた形でインストールされます。
$ echo "推しメンすちすちビーム" >> /etc/sample/sample.conf
$
$ sudo rpm -Uh sample-script-1.0-2.el7.x86_64.rpm
################################# [100%]
更新中 / インストール中...
警告: /etc/sample/sample.conf は /etc/sample/sample.conf.rpmnew として作成されました。
################################# [ 50%]
整理中 / 削除中...
################################# [100%]
$
$ ls -1 /etc/sample/
sample.conf
sample.conf.rpmnew
missingok
%config(missingok)
としたファイルは、ファイルが存在しなくてもエラーになりません。
これはrpm -V
コマンドなどでチェックしたときにエラーにならないようにするためのものです。
更新履歴
rpmパッケージの更新履歴を英語で記述します。
記述するフォーマットはパッケージによって微妙に異なりますが、基本的には以下のフォーマットで大丈夫です。
* 曜日 月 日 西暦 パッケージャーの名前 <メールアドレス> バージョン-リリース番号
- 更新内容
これを最新の更新情報が上に来るように書き加えていきます。
実際は下記のようになるとおもいます。
%changelog
* Thu dec 6 2018 shun kawai <shun@example.com> 1.0-2
- second release
* Wed dec 5 2018 shun kawai <shun@example.com> 1.0-1
- first release
最後に
自作のスクリプトなどでもrpmパッケージになっていればrpm
コマンドやyum
コマンドでインストール・アンインストールができるから便利!
debパッケージも作れるようになるとなお良いのかもしれません。勉強します。
参考にしたサイト