openSUSE:パッケージングガイドライン

移動先: 案内, 検索
パッケージングガイドライン では、 openSUSE ディストリビューションにおけるパッケージ作成を統制するための、核心部分の詳細を説明しています。ここには汎用的なルールのほか、パッケージの法律的な側面におけるルールや、特定のパッケージの機能や種類に対するものなども書かれています。
  • パッケージに存在する固有の問題を指摘するのはレビュー担当者の責任範囲であり、指摘された問題への対応を実施するのはパッケージ作成者の責任範囲です。レビュー担当者とパッケージ作成者は、それらの問題の重要度についてお互いの認識を合わせ、パッケージの送信をブロックするのか、パッケージを取り込んだ後に対応するのかを決定します。
  • パッケージングガイドラインは、一般的な問題とその重要度を説明しているものです。ここに示されているガイドラインは無視すべきものではありませんし、何も考えずに従うだけのものでもありません。担当しているパッケージが、ガイドラインの適用を免除すべきものであるとお考えの場合は、その旨を openSUSE-packaging のメーリングリストにご報告ください。
  • Build Service では rpmlint と呼ばれるツールを利用して、構築完了後に様々なルールを適用し、警告やエラーなどを報告するようになっています。パッケージ作成者は、その出力を必ず確認し、パッケージのエラーや改善に至るためのヒントを得るようにしてください。 rpmlint が生成する様々な警告メッセージについて、詳しくは パッケージングチェック をお読みください。左記のページでは、指摘が間違っている場合の対処方法も説明しています。
  • ガイドラインを変更する必要がある場合は、 変更プロセス に従って実施してください。

目次

一般的なガイドライン

ここには、従うべき一般的なルールを説明しています。

構築済みのバイナリやライブラリを含めないでください

openSUSE のパッケージ内に含まれるすべてのバイナリやライブラリは、ソースパッケージ内に含まれているソースコードから構築されたものでなければなりません。これは下記のような理由によるものです:

  • セキュリティ: ソースコードから構築されたことを保証できない事前構築済みのバイナリやライブラリには、悪意のあるコードや危険なコードなどを含むことができてしまうほか、正しく動作するかどうかもわかりません。また、パッチやバグ修正なども行なうことができなくなってしまいます。
  • コンパイラのフラグの問題: ソースコードから構築されたことを保証できないバイナリやライブラリは、 openSUSE 標準のコンパイラフラグが設定されていない可能性があり、セキュリティや最適化がなされていない可能性があります。

ファイルがバイナリやライブラリであるかどうかを確認するには、下記を参考にしてください:

  • 実行可能なファイルですか?そうであれば、おそらくバイナリです。
  • ファイル名に .so, .so.#, .so.#.#, .so.#.#.# などの拡張子が付いていませんか?ついていれば、それはおそらくライブラリです。
  • 何かご質問があれば、 openSUSE-packaging メーリングリスト (英語) にてお尋ねください。

なお、構築時に非オープンソースのコンポーネントを必要とするパッケージ (たとえば商用のコンパイラを必要とするパッケージなど) は、許可されていません

例外事項

  • コンパイラそのものやクロスコンパイラ環境に関連するものなど、ソフトウエアによっては元となるツールチェインや開発環境無しでは構築できないものがあります。このような条件に該当するパッケージの場合は、 openSUSE Packaging Committee に連絡して、許可をもらってください。
  • また、バイナリファームウエアに対する例外もあります。下記の文書に該当するものであれば、例外として扱います: BinaryFirmware

複数のプロジェクトの組み合わせ

openSUSE のパッケージでは、複数の異なるアップストリーム (提供元) のパッケージを 1 つにまとめるようなことは、できる限り避けることとしています。

spec ファイルのガイドライン

spec ファイルの内容に適用されるルールは、 spec ファイルのガイドライン で説明しています。

アーキテクチャの対応

すべての openSUSE パッケージは、少なくとも対応アーキテクチャのうちの 1 つ (i586 または x86_64) でコンパイルおよびビルドされ、バイナリ RPM ファイルとして構成しなければなりません。パッケージ作成者は、両方のアーキテクチャ向けにビルドできるよう、できる限り努力するものとします。また、コンパイルやビルドを行なう必要のない、アーキテクチャ非依存のコード (noarch) は例外として扱います。

再配置可能 (relocatble) なパッケージ

RPM の機能の中には再配置可能なパッケージを作成する機能がありますが、これは使用すべきではありません。この機能を利用してしまうと、インストーラや zypper, yast などから全く利用できなくなってしまう場合があるほか、正しく動作しない場合もあります。また、他のパッケージガイドラインに従う場合でも、一般的には不要な機能です。しかしながら、パッケージを再配置可能な状態にしなければならない特別な理由がある場合は、パッケージのレビューの際にその旨を 必ず 明記し、その点に対するチェックを行なってもらってください。


法律面

禁止されているソフトウエア

Build_Service_アプリケーションブラックリスト には、アプリケーション (またはその他のソフトウエア) の中で、パッケージ作成が禁止されているものを一覧で示しています。

寄付の要求

パッケージの記述 (説明文/概要/コメントなど) には、アップストリーム (提供元) のプロジェクトやパッケージ作成者に対する寄付要求を含んでいてはなりません。私たちがそのようなプロジェクトに対する寄付を禁止しない場合であっても、プロジェクトやパッケージ作成者は自分自身の Web サイト内でマーケットキャンペーンをするものとし、ダウンストリーム (openSUSE) のパッケージでは行なわないものとします。

実行中に寄付を求めるメッセージが表示されるようなパッケージが現れる場合は、パッケージ作成者の裁量でそのメッセージを維持するか、もしくは修正するかどうかを判断してください。

ライセンス

ソフトウエアのライセンスは、 Open Source Definition (現時点でバージョン 1.9) に従うものとします。ライセンスが Open Source Definition に準拠しているかどうか疑問がある場合は、 バグ報告 を実施してください。なお、記述は英語で、カテゴリは SUSE Tools -> SUSE Linux Legal Issues を指定してください。

spec ファイル

パッケージ作成者は通常、 spec ファイルを通してソフトウエアのライセンス問題に直面します。 spec ファイルではパッケージ全体に対するライセンスを定義できるほか、個別のサブパッケージに対して別々のライセンスを設定することもできます。ライセンスは SPDX に規定された短い名称で指定すべきですが、 SPDX とそれに結び付けられたライセンスの一覧は歴史の浅いものであり、 openSUSE のパッケージ作成者が利用するすべてのライセンスをカバーしているとは言えない状況です。このような制限を克服するため、 SPDX に規定されたライセンスのほか、 openSUSE Factory や openSUSE NonFree に存在するその他の多数のライセンスを含んだ を作成しました。この表内にも対応するライセンスが見つからない場合は、 バグ報告 を実施してください。なお、記述は英語で、カテゴリは SUSE Tools -> SUSE Linux Legal Issues を指定してください。

ライセンスを定義するにあたっては、上記のように名前を単純に指定することができるだけでなく、 openSUSE では spec ファイル内のライセンス指定に対して演算子を設定することができます。たとえば 'and' を指定すると、 2 つのライセンスの両方に従わなければならないことを示すことができますし、 'or' を指定すると、いずれかのライセンスを選択することができるようになります。また、カッコを利用することもできます。たとえば下記のようになります:

License: (MIT or GPL-2.0) and LGPL-2.1+

上記では、実行可能ファイルに対するライセンスと、それに対応する LGPL-2.1+ でライセンスされたライブラリが存在することになります。

spec ファイルを作成するにあたって、もう 1 つ気を付けておくべきことは、サブパッケージに関してです。サブパッケージは、独自にライセンスを指定しない限り、メインのパッケージのライセンスを引き継ぎますが、場合によってはそれが正しくない場合があります。たとえばドキュメンテーション (文書類) に対する個別のサブパッケージを作成する場合、たとえばメインのパッケージが GPL-2.0+ ライセンスで提供されていても、ドキュメンテーションが GFDL-1.1 のライセンスである場合があります。このような理由から、サブパッケージとメインパッケージが同じライセンスで提供されている場合であっても、サブパッケージに対しては個別のライセンス指定を実施することをお勧めします。これにより、後から spec ファイルを読む人にとってもわかりやすい内容になります。

最後に、ライセンス文書は 必ず パッケージ内にコピーすべきです。これは spec ファイル内の %files セクションで %license マクロを使ってファイル名を指定することで実施することができます。ライセンス文書は通常、 COPYING, COPYING.LIB, LICENSE.txt などの名前で含まれています。この辺りに関する議論については https://lists.opensuse.org/opensuse-factory/2016-02/msg00167.html (英語) を参照してください。

コードとコンテンツ

コンピュータが直接実行する実行ファイルと、コンテンツを区別するのは重要です。 実行ファイルのコードは許可されます (ただし、もちろんのことながらオープンソース互換のライセンスであり、法的に疑わしいものではない限り、の話です) が、コンテンツは限定的に許可されます。ルールは下記のとおりです:

コンテンツがユーザ体験を拡張するためのものであるならば、 openSUSE ではパッケージとして作成してかまわないものとなります。これにはたとえば、フォントやテーマ、クリップアートや壁紙などが含まれます。

コンテンツは取り込みにあたってレビューされなければなりません。また、コンテンツのライセンスはオープンソース互換でなければならず、法的に疑わしいものであってはなりません。 上記に加えて、コンテンツには下記の追加の制限が課されます:

  • コンテンツにはポルノに属する内容を含んでいてはならず、アニメや疑似的、ないしは写真のいずれであっても、ヌードを含んでいてはなりません。ポルノに属するものが必要であれば、インターネット上でお探しになるのがよいでしょう。
  • コンテンツは侮辱的なものや差別的なもの、軽蔑的なものであってはなりません。コンテンツの一部にこれらのものが含まれるかどうか自信がない場合は、これらに含まれるものとして扱うのがよいでしょう。

openSUSE への受け入れが可能なコンテンツには、下記のようなものがあります:

  • オフィススイートで使用されるクリップアート
  • 壁紙 (ただし侮辱的なもの、差別的なものであってはならず、自由に再配布してかまわないものに限ります)
  • フォント (オープンソースライセンスで公開されていて、所有権や法的な考慮事項が無いもの)
  • ゲームのデータ (マップデータなど) はコンテンツとはみなされません。なぜなら、ゲームはデータなしで動作しないためです。
  • プログラムのソースコードに同梱されているサウンドやグラフィック、ドキュメンテーションが使用するテーマなども受け入れ可能です。
  • ゲームの音楽やオーディオコンテンツについても、それらが制限なく自由に再配布してかまわないものである限り、受け入れができます。
  • ソースコード内に含まれるサンプルファイルにいても、コンテンツとはみなされません。

openSUSE への受け入れが不可能なコンテンツには、下記のようなものがあります:

  • 漫画の画像ファイル
  • 宗教関連のテキスト

コンテンツに属するものであるかどうかわからない場合は、 openSUSE-packaging メーリングリスト (英語) にてお尋ねください。


パッケージの機能

init スクリプト

openSUSE 12.3 以降では systemd に完全に切り替えたため、 openSUSE にのみ対応するソフトウエアを作成する場合は、 SystemV スタイルの init スクリプトは不要です。ただし、 SLE11 など、古いディストリビューションでも動作するソフトウエアを作成する場合は、 SystemV スタイルの init スクリプトが必要になります。

init スクリプトと unit (service) ファイルについて、推奨は下記のとおりです:

  • init ファイルと service ファイルの両方がある場合は、 openSUSE 12.3 もしくはそれ以降の場合は service ファイルのみをインストールするようにしてください。
  • SLE11 で動作する必要があるパッケージの場合は、少なくとも SystemV init ファイルを用意する必要があります。
  • service ファイルのみを作成している場合は、 rcXXX を /sbin/service にリンクすることで、 rcXXX が動作し続けるようになります。
  • 新しいパッケージを送信する際は、利用者に配慮しましょう。 openSUSE 12.3 もしくはそれ以降に対しては新しい service ファイルを、 SLE11 に対しては SystemV init ファイルを作成してください。

openSUSE でも従来の SystemV init ファイルを使用することができますが、 service ファイルは非常に作成しやすい構造であることから、これを作成しておくことをお勧めします。

desktop ファイル

パッケージに GUI アプリケーションが含まれる場合は、 .desktop ファイルをインストールするようにしておくことをお勧めします。このガイドラインで GUI アプリケーションとは、独自の X ウインドウを作成して、その中で動作するものを意味します。また、インストールされる .desktop ファイルは、 desktop-entry-spec の規定に従っていなければならないほか、特に Name, GenericName, Categories, StartupNotify の各項目を正しく設定しておくようご注意ください。

desktop ファイル内でのアイコンタグ

アイコンタグはアイコンファイルと同じベース名でなければなりません。これはアイコンのテーマ化にあたって必要となるためです:

  • Icon=comical

既定では .png をまず検索し、それで見つからなければ .svg を検索し、最後に .xpm を検索します。

.desktop ファイルの作成

パッケージ内に .desktop ファイルが含まれておらず、インストールも行なっていないような場合は、ソースコードとして独自のファイルを作成する必要があります (例: Source3: %name.desktop) 。 .desktop ファイルは、たとえば下記のように記述します (comical.desktop ファイルの例):

[Desktop Entry]
Name=Comical
GenericName=Comic Archive Reader
Comment=Open .cbr & .cbz files
Exec=comical
Icon=comical
Terminal=false
Type=Application
Categories=Graphics;

%suse_update_desktop_file の使いかた

.desktop ファイルをパッケージに含めるだけでは不十分です。 %install セクションでは少なくとも 1 回は %suse_update_desktop_file を実行しなければなりませんし、 BuildRequires: update-desktop-files を指定する必要もあります。これらは .desktop ファイルを安全に、かつ spec に準拠した形で処理するのに必要な指定です。また、ファイルをインストールしない場合や、 .desktop ファイルに変更を加えたり (たとえばカテゴリの追加や削除など) した場合にも、 %suse_update_desktop_file を使用しなければなりません。たとえば下記のように使用します:

  • check desktop file
%suse_update_desktop_file %{name}
  • install desktop file and change categories
%suse_update_desktop_file -r %{name} System Utility Core GTK FileManager

ユーザとグループ

現時点では、ユーザ名やグループ名から UID/GID への変換はターゲットの (インストール先の) システムでインストール時に動的に決定されることにご注意ください。パッケージ内のスクリプトが動的な方式でこのマッピングを設定する場合であっても、システム管理者側でそれを静的に設定する方法がこともできますが、詳細は調査中で、パッケージの構築時にマッピングを設定できるほうほうについても検討が行なわれています。

様々なアプリケーションや標準ファイルシステムのディレクトリに用いられるユーザや、どの Unix 互換システムにも存在すべき標準的なユーザのようなシステムユーザは特別な RPM によって提供されるべきです。

この RPM はユーザとグループを提供します:

Provides: user(<name>)
Provides: group(<name>)

この RPM はユーザとグループを提供する他、ホームディレクトリの作成も行います。特別なシステムユーザを必要とするアプリケーションにはこれらが必要です:

Requires(pre): user(<name>)
Requires(pre): group(<name>)

このように、システムユーザは必要な時に作成されるだけです。そして管理者は容易にシステムユーザがまだ必要か削除できるかを確認することができます。

systemd-sysusers (sysusers.d(5)) はこれらのアカウントを作成するのに用いられます。また、システムアカウントがどのように見えるかを確認することもできます。

例えば uucp の spec ファイルではシステムユーザは以下の部分に含まれています:

Source1:        system-user-uucp.conf
BuildRequires:  sysuser-tools

%package -n system-user-uucp
Summary:        System user and group uucp
%sysusers_requires

%build
%sysusers_generate_pre %{SOURCE1} uucp

%pre -n system-user-uucp -f uucp.pre

%files -n system-user-uucp
%defattr(-,root,root)
%dir %attr(0750,uucp,uucp) %{_sysconfdir}/uucp

%pre ... -f uucp.pre の部分で uucp.pre の内容をスクリプトレットとして取得しています。このファイルはビルド時に %sysusers_generate_pre マクロによって生成されます。

単独のパッケージ内でだけ必要となるユーザやグループをそのパッケージ内で作成したい場合は、下記のようにします:

Requires(pre): shadow

[...]

%pre
getent group GROUPNAME >/dev/null || groupadd -r GROUPNAME
getent passwd USERNAME >/dev/null || useradd -r -g GROUPNAME -d HOMEDIR -s /sbin/nologin -c "user for PACKAGENAME" USERNAME
exit 0

[...]

HOMEDIR は通常、パッケージが作成し所有したのち、適切なパーミッションが設定されているディレクトリを指定すべきです。 HOMEDIR として適切な場所には、たとえば /var 内のパッケージのデータディレクトリなどがあります。具体的には /var/lib/NAME が考えられます。

パッケージが作成するユーザアカウントは、特別な要件がなければ対話的にログインする必要はありません。そのため、ユーザのシェルには一般に、 /bin/false または /sbin/nologin を設定しておいてください。


末尾にある exit 0 は、ユーザやグループの作成が失敗したような場合であっても、 %pre のスクリプトが成功を報告するように強制するためのものです。これは、ユーザやグループの作成が失敗してもシステム全体を壊すようなことはほとんど起こらないために設定しているものですが、最適な設定であるとは言えません。なお、パッケージの中身を展開する際、ユーザやグルーブが存在していなかった場合、 rpm ではそれらのファイルの所有者を root であるものとみなして設定します。

getentgroupadd/useradd の前に実行するようになっていますが、これは作成しようとしているユーザやグループが、既に存在していないかどうかを確認するためのものです。既に存在している場合は作成を行ないません。これはシステム管理者があらかじめユーザやグループを作成しておき、 UID や GID を事前に決めておくことができるようにするための仕組みです。

%pre 内にある groupadd/useradd は、最初にインストールした時にもアップグレードした時にも実行されます。この場合も、上述の getent のチェックが働くほか、最初にインストールしたあと、何らかの理由でユーザやグループが消えてしまったような場合であっても、アップグレード時にそれを修復できることになります (アップグレードの際にファイルのパーミッションが復元されるのと同じです) 。

パッケージによって作成されたユーザは、決して削除されることがありません。これらのユーザやグループが所有するファイルが、本当に不要なファイルであるかどうかを判断する手段が存在しないためです。それらを無視してユーザやグループを削除してしまうと、どのようなことになってしまうのでしょうか?存在しないユーザやグループが所有するファイルが存在した場合、後から新しいユーザやグループを同じ UID/GID で作成してしまう場合が考えられるため、無関係なユーザやグループがそのファイルを所有してしまうことになり、結果としてセキュリティ上の問題となってしまうためです。また、設定によってはユーザやグループの削除が実施できない場合や、すべきではない場合もあります。たとえばユーザやグループのデータベースをリモートで共有しているような場合が考えられます。不要なユーザやグループの削除は、必要であればシステム管理者が注意して実施すべきものです。

場合によっては、ユーザアカウントを作成せずにグループのみを作成する必要がある場合もあります。これは一般に、何らかのシステムリソースが特定のグループからの制御のみを受け付けるように設定したいような場合で、ユーザアカウントを作成しても何も意味がないような場合に発生します。たとえば (もちろんこれだけではありませんが) 、ゲームプログラムが setgid を実行してユーザ間で共有のハイスコアファイルに書き込むようになっている場合や、何らかのハードウエアデバイスで例外的な許可を設定する必要があり、システムユーザやコンソールにログインしたユーザの権限では動作させることができない、などの場合があります。これらのような場合は、上述の例で groupadd の部分のみ設定してください。

ただし、既に存在するユーザやグループを作成しないようにする場合は、同じ名前のユーザやグループが別のパッケージで作成されている可能性があることにも注意してください。この場合、同じ名前のユーザやグループを共有する競合相手のパッケージから、アクセスを許可する結果になってしまいます。ユーザやグループのガイドラインの本バージョンでは、このような問題への対応は行なっていませんが、将来のバージョンではそのような問題への対処も記述する予定です。

移行 / アップグレード

コンテナ化された展開を導入したため、インストール時に全ての設定やランダムな情報の生成を行うことはお勧めしません。それよりもデータの一貫性と安定性を確保するために、子コンテナにおいて実行されるべきです。

これは、基本的にバイナリを使用したり既存のデータに影響を与える (キーチェーンの生成やデータベースの移行といった) 構成処理はスクリプトレットフェーズからライブシステム上で後で実行するように変更することを意味しています。 rpm によるパッケージのインストールでこれを直接可能にするために、いくつかのオプションがあります。

systemd サービスを使う (例として mariadb パッケージを参照)

systmd サービスを使って、初期設定と展開を実行したり、さらには既存のコンテンツを開始前に移行することもできます。

一般的な systemd ファイルは非常にわかりやすいものです:

[Unit]
Description=MySQL server
Wants=basic.target
Conflicts=mysql.target
After=basic.target network.target 

[Service]
Restart=on-abort
Type=simple
ExecStartPre=/usr/lib/mysql/mysql-systemd-helper  install
ExecStartPre=/usr/lib/mysql/mysql-systemd-helper  upgrade
ExecStart=/usr/lib/mysql/mysql-systemd-helper     start
ExecStartPost=/usr/lib/mysql/mysql-systemd-helper wait

[Install]
WantedBy=multi-user.target

全ての処理はヘルパーシェルスクリプトによって行われます。これはインストールおよびアップグレードのステップそれぞれの開始前に記述されます。これらの点を説明するのはアプリケーションごとに異なりますが、mariadb の場合は次のようになります:

# Create new empty database if needed
mysql_install() {
	if [[ ! -d "$datadir/mysql" ]]; then
		echo "Creating MySQL privilege database... "
		mysql_install_db --user="$mysql_daemon_user" --datadir="$datadir" || \
		die "Creation of MySQL databse in $datadir failed"
		echo -n "$MYSQLVER" > "$datadir"/mysql_upgrade_info
	fi
}
# Upgrade database if needed
mysql_upgrade() {
	# Run mysql_upgrade on every package install/upgrade. Not always
	# necessary, but doesn't do any harm.
	if [[ -f "$datadir/.run-mysql_upgrade" ]]; then
		echo "Checking MySQL configuration for obsolete options..."
		sed -i -e 's|^\([[:blank:]]*\)skip-locking|\1skip-external-locking|' \
		       -e 's|^\([[:blank:]]*skip-federated\)|#\1|' /etc/my.cnf

		# instead of running mysqld --bootstrap, which wouldn't allow
		# us to run mysql_upgrade, we start a full-featured server with
		# --skip-grant-tables and restict access to it by unix
		# permissions of the named socket

		echo "Trying to run upgrade of MySQL databases..."

		# Check whether upgrade process is not already running
		protected="$(cat "/run/mysql/protecteddir.$INSTANCE" 2> /dev/null)"
		if [[ -n "$protected" && -d "$protected" ]]; then
			pid="$(cat "$protected/mysqld.pid" 2> /dev/null)"
			if [[ "$pid" && -d "/proc/$pid" ]] &&
			   [[ $(readlink "/proc/$pid/exe" | grep -q "mysql") ]]; then
				die "Another upgrade in already in progress!"
			else
				echo "Stale files from previous upgrade detected, cleaned them up"
				rm -rf "$protected"
				rm -f "/run/mysql/protecteddir.$INSTANCE"
			fi
		fi
		protected="$(mktemp -d -p /var/tmp mysql-protected.XXXXXX | tee "/run/mysql/protecteddir.$INSTANCE")"
		[ -n "$protected" ] || die "Can't create a tmp dir '$protected'"

		# Create a secure tmp dir
		chown --no-dereference "$mysql_daemon_user:$mysql_daemon_group" "$protected" || die "Failed to set group/user to '$protected'"
		chmod 0700  "$protected" || die "Failed to set permissions to '$protected'"

		# Run protected MySQL accessible only though socket in our directory
		echo "Running protected MySQL... "
		/usr/sbin/mysqld \
			--defaults-file="$config" \
			--user="$mysql_daemon_user" \
			--skip-networking \
			--skip-grant-tables \
			$ignore_db_dir \
			--log-error="$protected/log_upgrade_run" \
			--socket="$protected/mysql.sock" \
			--pid-file="$protected/mysqld.pid" &

		mysql_wait "$protected/mysql.sock" || die "MySQL didn't start, can't continue"

		# Run upgrade itself
		echo "Running upgrade itself..."
		echo "It will do some chek first and report all errors and tries to correct them"
		echo
		if /usr/bin/mysql_upgrade --no-defaults --force --socket="$protected/mysql.sock"; then
			echo "Everything upgraded successfully"
			up_ok=""
			rm -f "$datadir/.run-mysql_upgrade"
			[[ $(grep -q "^$MYSQLVER" "$datadir/mysql_upgrade_info" 2> /dev/null) ]] || \
				echo -n "$MYSQLVER" > "$datadir/mysql_upgrade_info"
		else
			echo "Upgrade failed"
			up_ok="false"
		fi

		# Shut down MySQL
		echo "Shuting down protected MySQL"
		kill "$(cat "$protected/mysqld.pid")"
		for i in {1..30}; do
			/usr/bin/mysqladmin --socket="$protected/mysql.sock" ping > /dev/null 2>&1 || break
		done
		/usr/bin/mysqladmin --socket="$protected/mysql.sock" ping > /dev/null 2>&1 && kill -9 "$(cat "$protected/mysqld.pid")"

		# Cleanup
		echo "Final cleanup"
		if [[ -z "$up_ok" ]]; then
			rm -rf "$protected" "/run/mysql/protecteddir.$INSTANCE"
		else 
			die "Something failed during upgrade, please check logs"
		fi
	fi
}

crontab

前のオプションと似ていますが、systemd の代わりに crontab を使います。

バイナリの調整

最初の実行や移行の時にバイナリ自身によって全ての設定タスクを実行してバイナリの調整を行います。 しかし、そのための変更を取り込むよう上流プロジェトを説得するため、煩雑になります。

logrotate のスクリプト

logrotate では、ログファイルもしくはログファイルの存在するディレクトリが root:root の所有権である必要があります。これは、シンボリックリンク攻撃などに対して脆弱になることが無いようにするための措置です。

どうしてもログファイルの所有者が root 以外のユーザでなければならない場合は、最近になって追加された "su" ディレクティブを使用すべきです。これを指定すると、 logrotate のプロセスがログを切り替える際、事前にそのユーザとグループに成り代わって動作するようになります。

例:
/var/log/radius/radius.log {
    su radiusd radiusd
    [...]
}

修正

パッケージング修正ガイドライン をお読みください。


複数のバージョンをサポートするパッケージ

パッケージングマルチバージョンガイドライン をお読みください。


changelog (変更履歴)

changes ファイル (RPM) の作成方法 をお読みください。なお、 openSUSE では RPM の changelog を設定するにあたって、個別のファイルを用意して対応します。

udev ルール

udev ルールファイルは %{_udevrulesdir} に配置しなければなりません。

%post および %postun で udev をリロードする必要はありません。udev は自動的にルールファイルの変更を検知します (opensuse-packaging での議論) 。

GConf スクリプトレット

SuSEfirewall2 サービス設定


パッケージの種類ごとのガイドライン

アプリケーションによっては、独自のガイドラインが存在する場合があります。下記にはそれぞれの種類ごとに存在するガイドラインを示します。


アーキテクチャをまたぐ (baselib) パッケージ

たとえば -32bit, -64bit, -x86 などの名称が設定されているパッケージのことを指します。
openSUSE:Build_Service_baselibs.conf

ブランディング

debuginfo

パッケージは通常、有用な -debuginfo パッケージを生成すべきではありますが、有用な情報を生成できない場合は、明示的に無効化する場合もあります。この場合は rpmbuild は通常通り -debuginfo パッケージを生成します。 -debuginfo パッケージの生成は可能な限り自動的に行なわれる仕組みで、 Build Service ではプロジェクトごとやリポジトリごと、もしくはパッケージごとに明示的に無効化することができます。たとえば OBS の Web UI から openSUSE:12.3:Update プロジェクト内の Repository タブその中のプロジェクト を見ると、その関係がわかるものと思います。もちろん、 osc コマンドを利用してプロジェクトやパッケージのメタデータを編集することで、そのフラグを設定することもできます。 -debuginfo パッケージを明示的に無効化する場合は、 spec ファイルでその理由を説明してください。

debuginfo フラグを有効化した場合は、 bs-worker が順次動作して、 /usr/bin/buildrpm--define '_build_create_debug 1' フラグ付きで実行するようになります。

また、 /home/abuild/.rpmmacros_build_create_debug のために設定が行なわれますが、これによって不用意なマクロ展開が動作してしまい、 %debug_package などに対して予期しない結果を生む場合があります。 %debug_package は規格化された RPM コンポーネントであり、 Build Service 固有の処理の終了をマークするものです。

注: rpm の古いバージョン (SLE-11 向けビルドで使われています) では noarch サブパッケージをサポートしていません。そのため debuginfo ファイルを作成するものの -degubinfo パッケージが生成されないことになります。これらのバージョンのためにビルドする場合は noarch をサブパッケージに使ってはいけません。

debuginfo パッケージについては、個別の文書に詳しい説明が書かれています。 [1] をお読みください。

Eclipse プラグイン

Emacs

単一の elisp ファイル (.el) は %{_datadir}/emacs/site-lisp に配置してください。複数のファイルを配置する場合は %{_datadir}/emacs/site-lisp/%{name} サブディレクトリを作成し、そのサブディレクトリを load-path リストに追加する初期化ファイルを新たに追加してください:

%define _sitedir %{_datadir}/emacs/site-lisp
%define _startfile %{_sitedir}/suse-start-%{name}.el

cat <<EOF > %{buildroot}%{_startfile}
;; %{_startfile}

(add-to-list 'load-path "%{_sitedir}/%{name}")

;; %{_startfile} ends here
EOF

TODO: byte-compilation & autoloads

フォント

Type 1, OpenType TT (TTF), OpenType CFF (OTF) などの汎用目的のフォントは、 openSUSE:フォントのパッケージング に書かれているとおりにパッケージングするものとし、アプリケーション固有のディレクトリではなく、システム全体のフォントリポジトリ内にインストールするようにパッケージしてください。

ゲーム

ゲームのパッケージについては、 openSUSE:ゲームのパッケージング をお読みください。

GNOME

Go

Go ソフトウエアのパッケージについては、 openSUSE:Go のパッケージング をお読みください。

Haskell

Haskell ソフトウエアのパッケージについては、 openSUSE:Haskell のパッケージング をお読みください。

Java

Java ソフトウエアのパッケージについては、 openSUSE:Java のパッケージング をお読みください。

Lisp

Common Lisp ソフトウエアのパッケージについては、 openSUSE:Lisp のパッケージング をお読みください。

Lua

Lua ソフトウエアのパッケージについては、 openSUSE:Lua のパッケージング をお読みください。

Mono

Mozilla

OCaml

OpenOffice.org の拡張

PAM (Pluggable Authentication Modules)

Perl

Perl ソフトウエアのパッケージについては、 openSUSE:Perl のパッケージング をお読みください。

PHP

PHP ソフトウエアのパッケージについては、 openSUSE:PHP のパッケージング をお読みください。

Python

Python ソフトウエアのパッケージについては、 openSUSE:Python のパッケージング をお読みください。

R

R ソフトウェアのパッケージについては、openSUSE:R のパッケージング をお読みください。

Ruby

Ruby ソフトウエアのパッケージについては、 openSUSE:Ruby のパッケージング をお読みください。

wxWidgets

wxWidgets ソフトウエアのパッケージについては、 openSUSE:wxWidgets のパッケージング をお読みください。

ライブラリ

共有ライブラリ

openSUSE:共有ライブラリパッケージングポリシー

GObject イントロスペクションバインディング (.typelibs)

多くは GNOME スタックからのものですが、多数のライブラリに .typelib ファイルが含まれています。この .typelib は様々なプログラミング言語 (seed, python, vala) とライブラリとの間を仲介するためのもので、共有ライブラリのようにそれらはバージョン設定が行なわれ、場合によっては複数のバージョンのものをインストールする必要が発生します。

パッケージ名は下記の規約に従うものとします (なお、ドット (.) はアンダースコア (_) に置き換えるものとします): typelib-<GIVersion>-<TypeLibName>-<TypeLibVersion>

各項目の意味は下記のとおりです: typelib: 固定で設定する部分で、パッケージタイプを判別するためのもの <GIVersion>: 現在の gobject イントロスペクションのバージョンは 1.0 なので、 1_0 を設定する <TypeLibName>: 実際のバインディングの名前 (大文字と小文字は区別される) <TypeLibVersion>: バインディングのバージョン

これらの要素を組み合わせてファイル名を構成します。たとえば /usr/lib/girepository-1.0/Memphis-0.2.typelib というファイルであれば、 typelib-1_0-Memphis-0_2 のようなファイル名になります。 なお、このようなパッケージを構築する際は、 spec ファイル内に BuildRequires: gobject-introspection を指定するようにしてください。これにより、自動依存関係スキャナが必要な依存関係を処理するようになります。結果として、 typelib-* パッケージはライブラリ本体と、必要な範囲で他の typelib パッケージを必要とするようになります。

静的ライブラリ

パッケージには、 configure スクリプトの実行時に --disable-static を指定するなどして、可能な限り性的ライブラリを含まないようにすべきです。静的なライブラリは例外的な状況でのみ使用すべきもので、ライブラリをリンクするアプリケーションについても、静的ライブラリではなく共有 (動的) ライブラリを利用すべきです。

libtool アーカイブ (foo.la 形式のファイル) についても、パッケージには含めるべきではありません。 libtool を使用するパッケージによっては、 --disable-static を指定していても既定でインストールしてしまうものがありますが、パッケージの作成にあたってはこれらを削除しておくのがよいでしょう。 libtool の古いバージョンに存在するバグや、それを使用するプログラム側のバグによっては、プログラムの修正なしには *.la ファイルを削除できない場合がありますが、このような場合はアップストリームと共同で作業を行ない、問題に対応してください。なお、安定版の openSUSE リリース内にあるライブラリを更新していて、そのライブラリ内に既に *.la ファイルが含まれている場合は、 *.la ファイルの削除は API/ABI の変更として取り扱われるべきものです。言い換えると、それらの削除はライブラリが提供するインターフェイスのほとんどを変えてしまうような変更であり、そう簡単に取り扱うべきものではありません。

例外

静的ライブラリを使用しているパッケージは、たとえばそのライブラリ内に存在するセキュリティ欠陥が修正されたような場合に、どのパッケージを再構築すべきかを判断する必要があることから、きちんと管理しておきたいと考えております。どうしても静的なライブラリを利用したい場合は、下記のガイドラインに従ってください。

静的なライブラリは *-devel-static というサブパッケージ内に配置しなければなりません。また左記のパッケージは、 *-develRequires で参照するようにします。このように、一般的な開発用ファイルが含まれる *-devel パッケージと静的なライブラリを含むパッケージとを分離することで、 BuildRequire*-devel-static が含まれているもの、つまり静的なライブラリを利用しているパッケージを見つけやすくしています。また、この試みは可能な限り共有ライブラリから静的なライブラリを切り離す役割も果たしています。なお、静的なライブラリしか含まないパッケージの場合は、 Requires*-devel を指定する必要はありません。静的なライブラリを明示的に必要とするパッケージのみが BuildRequire: foo-devel-static を指定することになりますので、これで追跡を容易にすることができます。

また、動作にあたって静的なライブラリを必要とする共有ライブラリが存在する場合に限り、静的なライブラリは *-devel パッケージ内に含まれるべきです。この場合、 devel サブパッケージは Provide 指定で *-devel-static を設定しなければならないほか、静的なライブラリに依存しているパッケージは、 BuildRequire*-devel-static パッケージのほうを指定しなければなりません。

システムライブラリの重複

いくつかの理由により、パッケージはシステム内に存在するライブラリのローカルコピーを含んでいるべきではありませんし、それを利用して構築すべきでもありません。このような場合は、システムに存在するライブラリを使用するように修正すべきです。これにより、中枢をなすシステムライブラリ内に存在する古いバグやセキュリティホールを解決および修正するように促すことができるためです。

Tcl

Web アプリケーション

openSUSE で Web アプリケーションをパッケージングする際は、他のアプリケーションの場合と同様にそれらのファイルを種類に従って /etc、/usr、/var などに配置すべきです。

一般的には、/srv の使用は FHS によって管理者用に予約されているため [2] 、パッケージは /srv 内のコンテンツをインストール、削除および変更をすべきではありません。ただ openSUSE では、ソフトウェアのデフォルト設定で /srv 内のディレクトリを指している場合にパッケージが /srv 内に空のディレクトリを作ることができます。

Vala

spec ファイル内で Vala のバージョンを検出する方法

下記では Vala を独自に構築していない場合を想定して記述しています。もしも Vala を独自に構築している場合は、 Vala のバージョンも独自に決めることができますので、 %{version} タグを利用して独自に設定すれば、下記の問題は発生しません。 ここでは、このような面倒な問題への対応方法を示しています:

Vala を利用して構築するパッケージがあり、すべてのバージョンの Vala で問題なく構築できているような (BuildRequires: vala >= 0.14 のようにバージョンを限定する必要がない) 状況で、何らかの理由で Vala のバージョン番号を検出し、使用しなければならない場合を想定します。

このような場合、 Build Service が障害になってしまいます。現時点では Vala を利用するにあたって BuildRequires: vala を指定するだけで、 Perl や Python が提供しているような %{py_ver}%{perl_version} 自動化タグを利用することはできません。パッケージによっては、 Vala のバージョン番号を知る必要があるものもあります (たとえば Babl や Gegl では、既定で .vapi ファイルを /usr/share/vala にインストールしますが、そのようなディレクトリは標準の openSUSE システムでは意味がありません。その代わり、 openSUSE では Vala のバージョン番号を含んだ /usr/share/vala-0.14 などのようなディレクトリを使用します。 openSUSE から利用できるようにするため、これらのディレクトリを使用するのはパッケージ作成者の役割です) 。

もちろん %if 0?{%suse_version} >= 1140 のようなタグを利用することで、 openSUSE 標準の Vala バージョンのディレクトリにインストールすることもできます。ですがこのような方法をとってしまうと、 spec ファイルが肥大化してしまうばかりか、メンテナンスが面倒になってしまいます (ほぼすべての openSUSE バージョンに対してそのような指定を行なわなければならなくなるため) 。

このような場合は、下記のようにして spec ファイル内で %define 関数を使用します:

 # Vala への依存関係を指定する
 BuildRequires: vala
 # Vala のバージョン番号を取得するための関数
 %define vala_version %(rpm -q --queryformat='%{VERSION}' vala | sed 's/\.[0-9]*$//g')

上記のように指定すると、 rpm -q --queryformat='%{VERSION}' vala の実行結果として、たとえば 0.14.0 のような値を得ることができます。この処理は、構築処理が実際に始まるよりも前に動作しますので、 spec ファイル内で一般的に使用することができます。残りの sed シェルコマンドは、正規表現の力を利用して .0 のような接尾辞を削除するためのもので、そこまでの結果を vala_version 変数に格納します。実行結果は %{vala_version} のように指定すると、利用することができます。たとえば下記のようになります:

 # 新しいディレクトリを作成する。 -pv は、ログファイル内でのデバッグを容易にするための冗長出力の指定と、
 # 親ディレクトリ存在しなかった場合の自動作成の指定です。
 mkdir -pv %{buildroot}%{_datadir}/vala-%{vala_version}/
 # ファイルの移動
 mv %{buildroot}%{_datadir}/vala/* %{buildroot}%{_datadir}/vala-%{vala_version}/
 # 元のフォルダの削除
 mv -Rf %{buildroot}%{_datadir}/vala/

%files セクション内でも、下記のようにして使用することができます:

 %dir %{_datadir}/vala-%{vala_version}/
 %{_datadir}/vala-%{vala_version}/*

これにより、 spec ファイルをシンプルにすることができます。

%{buildroot} ディレクトリ内の構造は最終的に出力される RPM 内の階層そのものを表しているため、パッケージのわかりやすさも +1 できることになります。