クリアコードはフリーソフトウェア開発で培った技術力を提供しています。特にMozilla製品(Mozilla FirefoxとMozilla Thunderbird)とRubyに関連した開発を得意としています。
注: FedoraやCentOSのRPMパッケージャーが書いた文章ではありません。FedoraやCentOSのRPMパッケージメンテナになりたい方はFedoraやCentOSが公式に配布している文書の方をお勧めします。例えば、How to create an RPM package - FedoraProjectという文書があります。
Debianパッケージの作り方と公開方法: groongaを例にしてのRPM版のような内容です。
ここでは、迷惑メール対策ソフトウェアmilter managerを例にしてDebian GNU/Linux上でCentOS 5.4向けのRPMパッケージを作成し、それを提供するYumリポジトリを作成・公開する方法を紹介します。RPMパッケージの作成では、1つのspecファイルから複数のパッケージを作成します。この方法は、ライブラリを提供するソフトウェアの場合に多く用いられます。
また、Yumリポジトリを登録するRPMも作成します。そのため、ユーザには以下のようにYumリポジトリを登録してもらうことになります。
Yumリポジトリを登録:
% sudo rpm -Uvh http://milter-manager.sourceforge.net/centos/5/milter-manager-repository-1.0.0-0.noarch.rpm
インストール:
% sudo yum install -y milter-manager
ここで紹介する方法を自動化するスクリプト群はmilter managerのリポジトリで公開されています。ここで紹介する方法でRPMパッケージ・Yumリポジトリを作成・公開する場合は参考にしてください。
それでは、まずはパッケージの作り方です。
RPMパッケージを作るためには.specを作ります。ここでは、複数のパッケージを生成する.specを作成するので、まず、どれをどのパッケージにいれるかを検討します。
milter managerはmilterプロトコルを実装したライブラリ、それを利用したmilter管理アプリケーション、ユーティリティツールで構成されています。この場合、以下のように複数のパッケージに分解することが多いようです。
milter managerの場合は以下のパッケージを作成することとします。
複数のパッケージを作成する.specは以下のような内容になります。簡単ですね、とは言えないくらいの長さです*1。
Summary: 簡単な説明
Name: (メイン)パッケージ名(「milter-manager」など)
Version: バージョン番号(「1.5.0」など)
Release: リリースバージョン(最初は「0{?dist}」にしておけばよい)
License: ライセンス(「GPLv3+」など)
URL: ソフトウェアのサイトのURL
(「http://milter-manager.sourceforge.net/」など)
Group: (メイン)パッケージが属するグループ
(「System Environment/Daemons」など。
「/usr/share/doc/rpm/GROUPS」に一覧がある。)
Source: ソースのアーカイブのURL
(「http://downloads.sourceforge.net/milter-manager/milter-manager-1.5.0.tar.gz」など)
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
(常に↑でOK)
BuildRequires: ビルドに必要なパッケージ
BuildRequires: 必要な分だけ書く
Requires: 動作に必要なパッケージ
Requires: 必要な分だけ書く
%description
詳細なパッケージの説明。複数行になってもOK。
% package -n サブパッケージ名
(「-n」をつけないと「『パッケージ名』-『サブパッケージ名』」
というパッケージ名になるので、「libXXX」というパッケージ名を
つけるときは「-n」をつけないといけない)
Summary: サブパッケージの簡単な説明
Group: サブパッケージの属するグループ
% description -n サブパッケージ名
(「-n」の意味は「%package」と同じ)
詳細なサブパッケージの説明。複数行になってもOK。
%prep
%setup -q
%build
%configure オプション
(「--with-default-effective-user=milter-manager」など)
make %{?_smp_mflags}
% install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-, root, root, -)
(メイン)パッケージに含めるファイルを指定。
%files -n サブパッケージ名
%defattr(-, root, root, -)
サブパッケージに含めるファイルを指定。
%changelog
* 日付 名前 <メールアドレス>
- (バージョン番号-リリース番号)
- new upstream release
1つの.specで1つのパッケージを作る場合は%package -n サブパッケージ名、%description -n サブパッケージ名、%files -n サブパッケージ名がなくなるだけで、基本的な記述は変わりません。
milter managerの場合は以下のような.specになっています。%preやpostなどが増えていますが、これらは、それぞれインストール前・インストール後に実行するシェルスクリプトを指定しているだけです。
そこそこ長いので、詳細に興味がない場合は読み飛ばしてください。
Summary: A milter to use milters effectively
Name: milter-manager
Version: 1.5.0
Release: 11%{?dist}
License: GPLv3+, LGPL3+, AGPL3+, GFDL, Public Domain
URL: http://milter-manager.sourceforge.net/
Group: System Environment/Daemons
Source: http://downloads.sourceforge.net/milter-manager/milter-manager-1.5.0.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
BuildRequires: intltool
BuildRequires: gettext
BuildRequires: gcc
BuildRequires: make
BuildRequires: glib2-devel
BuildRequires: ruby
BuildRequires: ruby-devel
Requires: glib2
Requires: ruby
Requires: libmilter-toolkit = %{version}-%{release}
Requires(pre): /usr/bin/getent, /usr/sbin/useradd
Requires(pre): /usr/bin/id, /usr/sbin/groupadd
Requires(post): /sbin/chkconfig
Requires(preun): /sbin/service, /sbin/chkconfig
Requires(postun): /sbin/service, /sbin/chkconfig, /usr/sbin/userdel
%description
milter manager administrates milters instead of MTA to reduce milter
administration cost and combine milters flexibly.
%package -n libmilter-toolkit
Summary: A milter protocol library
Group: System Environment/Libraries
%description -n libmilter-toolkit
Both of client-side and server-side milter protocol are implemented.
This package contains the library files required for running services
built using libmilter-toolkit.
%package -n libmilter-toolkit-devel
Summary: Development files for libmilter-toolkit
Group: Development/Libraries
Requires: libmilter-toolkit = %{version}-%{release}
%description -n libmilter-toolkit-devel
This package contains the headers, and other support files
required for developing applications against libmilter-toolkit.
%package -n libmilter-compatible
Summary: libmilter API and ABI compatible milter library
Group: System Environment/Libraries
Requires: libmilter-toolkit = %{version}-%{release}
%description -n libmilter-compatible
A libmilter API and ABI compatible library based on libmilter-toolkit.
This package contains the library files required for running services
built using Sendmail libmilter or libmilter-compatible.
%package -n libmilter-compatible-devel
Summary: Development files for libmilter-compatible
Group: Development/Libraries
Requires: libmilter-compatible = %{version}-%{release}
Requires: libmilter-toolkit-devel = %{version}-%{release}
%description -n libmilter-compatible-devel
This package contains the headers, and other support files
required for developing applications against libmilter-compatible.
%package -n milter-manager-munin-plugin
Summary: Munin plugin for milter manager
Group: System Environment/Libraries
Requires: milter-manager = %{version}-%{release}
Requires: munin-node
%description -n milter-manager-munin-plugin
This package contains the munin plugin for munin-node.
%prep
%setup -q
%build
%configure \
--with-default-effective-user=milter-manager \
--with-default-effective-group=milter-manager \
--with-default-socket-group=smmsp \
--with-default-pid-file=/var/run/milter-manager/milter-manager.pid \
--with-default-connection-spec=unix:/var/run/milter-manager/milter-manager.sock
make %{?_smp_mflags}
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
mkdir -p %{buildroot}%{_initrddir}
install -m 755 data/init.d/redhat/milter-manager %{buildroot}%{_initrddir}/milter-manager
mkdir -p %{buildroot}%{_sysconfdir}/sysconfig
install -m 644 data/init.d/redhat/sysconfig/milter-manager %{buildroot}%{_sysconfdir}/sysconfig/milter-manager
mkdir -p %{buildroot}%{_sysconfdir}/cron.d
install -m 600 data/cron.d/redhat/milter-manager-log %{buildroot}%{_sysconfdir}/cron.d/milter-manager-log
mkdir -p %{buildroot}%{_localstatedir}/run/milter-manager/
mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d/
cat <<EOC > %{buildroot}%{_sysconfdir}/httpd/conf.d/milter-manager-log.conf
Alias /milter-manager-log/ /var/lib/milter-manager/public_html/log/
EOC
mv %{buildroot}%{_datadir}/milter-manager/munin/ %{buildroot}%{_datadir}/
mkdir -p %{buildroot}%{_sysconfdir}/munin/plugin-conf.d/
cat <<EOC > %{buildroot}%{_sysconfdir}/munin/plugin-conf.d/milter-manager
[milter_manager_*]
user milter-manager
env.logdir /var/lib/milter-manager/public_html/log
EOC
%clean
rm -rf %{buildroot}
%pre
if ! /usr/bin/getent group milter-manager &>/dev/null; then
/usr/sbin/groupadd -r milter-manager || \
%logmsg "Unexpected error adding group \"milter-manager\". Aborting installation."
fi
if ! /usr/bin/id milter-manager &>/dev/null; then
/usr/sbin/useradd -r -s /sbin/nologin -c 'milter manager' \
-d %{_localstatedir}/lib/milter-manager --create-home \
-g milter-manager milter-manager || \
%logmsg "Unexpected error adding user \"milter-manager\". Aborting installation."
fi
%post
/sbin/chkconfig --add milter-manager
/bin/mkdir -p /var/run/milter-manager
/bin/chown -R milter-manager:milter-manager /var/run/milter-manager
%post -n milter-manager-munin-plugin
/usr/sbin/munin-node-configure --shell | \
grep -e '\(milter_manager_\|\(postfix\|sendmail\)_processes\)' | \
sh
[ -f /var/lock/subsys/munin-node ] && \
/sbin/service munin-node restart > /dev/null 2>&1
:
%preun
if [ $1 -eq 0 ] ; then
/sbin/service milter-manager stop > /dev/null 2>&1
/sbin/chkconfig --del milter-manager
fi
%postun
if [ $1 -ge 1 ] ; then
/sbin/service milter-manager condrestart > /dev/null 2>&1
fi
if [ $1 -eq 0 ]; then
/usr/sbin/userdel -r milter-manager || \
%logmsg "User \"milter-manager\" could not be deleted."
fi
%postun -n milter-manager-munin-plugin
if [ $1 -eq 0 ]; then
rm %{_sysconfdir}/munin/plugins/milter_manager_* > /dev/null 2>&1
rm %{_sysconfdir}/munin/plugins/postfix_processes > /dev/null 2>&1
rm %{_sysconfdir}/munin/plugins/sendmail_processes > /dev/null 2>&1
[ -f /var/lock/subsys/munin-node ] && \
/sbin/service munin-node restart > /dev/null 2>&1
:
fi
%files
%defattr(-, root, root, -)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%doc %{_datadir}/gtk-doc/html/milter-manager/
%{_bindir}/milter-manager-log-analyzer
%{_sbindir}/milter-manager
%{_includedir}/milter-manager/milter/manager.h
%{_includedir}/milter-manager/milter/manager/
%{_libdir}/libmilter-manager.*
%{_libdir}/milter-manager/binding/
%{_libdir}/milter-manager/module/
%{_libdir}/pkgconfig/milter-manager.pc
%{_mandir}/man1/milter-manager.*
%{_mandir}/man1/milter-manager-log-analyzer.*
%{_mandir}/ja/man1/milter-manager.*
%{_mandir}/ja/man1/milter-manager-log-analyzer.*
%{_initrddir}/milter-manager
%{_datadir}/milter-manager/admin/
%{_sysconfdir}/milter-manager/cron.d/
%{_sysconfdir}/milter-manager/init.d/
%{_sysconfdir}/milter-manager/rc.d/
%{_sysconfdir}/cron.d/
%config %{_sysconfdir}/sysconfig/milter-manager
%config %{_sysconfdir}/milter-manager/milter-manager.conf
%config %{_sysconfdir}/milter-manager/defaults/
%config %{_sysconfdir}/milter-manager/applicable-conditions/
%config %{_sysconfdir}/httpd/conf.d/milter-manager-log.conf
%defattr(-, milter-manager, milter-manager, 0755)
%dir %{_localstatedir}/run/milter-manager/
%files -n libmilter-toolkit
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%{_bindir}/milter-test-client
%{_bindir}/milter-test-server
%{_bindir}/milter-performance-check
%{_libdir}/libmilter-core.so.*
%{_libdir}/libmilter-client.so.*
%{_libdir}/libmilter-server.so.*
%{_mandir}/man1/milter-test-client.*
%{_mandir}/man1/milter-test-server.*
%{_mandir}/man1/milter-performance-check.*
%{_mandir}/ja/man1/milter-test-client.*
%{_mandir}/ja/man1/milter-test-server.*
%{_mandir}/ja/man1/milter-performance-check.*
%files -n libmilter-toolkit-devel
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%doc %{_datadir}/gtk-doc/html/milter-manager/
%{_includedir}/milter-manager/milter/core.h
%{_includedir}/milter-manager/milter/core/
%{_includedir}/milter-manager/milter/client.h
%{_includedir}/milter-manager/milter/client/
%{_includedir}/milter-manager/milter/server.h
%{_includedir}/milter-manager/milter/server/
%{_libdir}/libmilter-core.so
%{_libdir}/libmilter-core.la
%{_libdir}/libmilter-client.so
%{_libdir}/libmilter-client.la
%{_libdir}/libmilter-server.so
%{_libdir}/libmilter-server.la
%{_libdir}/pkgconfig/milter-core.pc
%{_libdir}/pkgconfig/milter-client.pc
%{_libdir}/pkgconfig/milter-server.pc
%files -n libmilter-compatible
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%{_libdir}/milter-manager/libmilter.so.*
%files -n libmilter-compatible-devel
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%doc %{_datadir}/gtk-doc/html/milter-manager/
%{_includedir}/milter-manager/libmilter/
%{_libdir}/milter-manager/libmilter.so
%{_libdir}/milter-manager/libmilter.la
%{_libdir}/pkgconfig/libmilter.pc
%files -n milter-manager-munin-plugin
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%{_datadir}/munin/
%config %{_sysconfdir}/munin/plugin-conf.d/
%changelog
* Thu Feb 17 2010 Kouhei Sutou <kou@clear-code.com>
- (1.5.0-11)
- new upstream release
* Thu Oct 29 2009 Kouhei Sutou <kou@clear-code.com>
- (1.4.1-0)
- new upstream release
* Thu Oct 13 2009 Kouhei Sutou <kou@clear-code.com>
- (1.4.0-0)
- new upstream release
* Wed Sep 16 2009 Kouhei Sutou <kou@clear-code.com>
- (1.3.1-0)
- new upstream release
* Wed Aug 12 2009 Kouhei Sutou <kou@clear-code.com>
- (1.3.0-0)
- new upstream release
* Fri Jul 17 2009 Kouhei Sutou <kou@clear-code.com>
- (1.2.0-0)
- new upstream release
* Fri Jul 03 2009 Kouhei Sutou <kou@clear-code.com>
- (1.1.1-0)
- new upstream release
* Tue Jun 02 2009 Kouhei Sutou <kou@clear-code.com>
- (1.1.0-0)
- initial 1.1.x development seriese release
* Thu Apr 16 2009 Kouhei Sutou <kou@clear-code.com>
- (1.0.0-1)
- initial stable release
Debian GNU/Linux上でCentOS用のRPMパッケージをビルドすることは茨の道です。そこで、CentOS用のchroot環境を作って、そこでビルドすることにします。こうすることで、CentOSの実機がなくてもビルドできる上に、きれいな環境でビルドすることもできます。
Debian GNU/LinuxやUbuntuのchroot環境を作るにはdebootstrapが便利です。CentOSやFedoraのchroot環境を作るにはrinseが便利です。
/var/lib/chroot/centos-i386/以下にCentOS 5.4 i386用のchroot環境を構築するには以下のようにします。
% sudo aptitude install -y rinse % sudo mkdir -p /var/lib/chroot/centos-i386/etc/rpm/ % sudo sh -c "echo i386-centos-linux > /var/lib/chroot/centos-i386/etc/rpm/platform" % sudo rinse --arch i386 --distribution centos-5 --directory /var/lib/chroot/centos-i386/
/etc/fstabに以下を追記:
/dev /var/lib/chroot/centos-i386/dev none bind 0 0 devpts-chroot /var/lib/chroot/centos-i386/dev/pts devpts defaults 0 0 proc-chroot /var/lib/chroot/centos-i386/proc proc defaults 0 0
本質的な部分はsudo rinse ...だけです。sudo rinse ...の前にあるRPMパッケージで利用するプラットフォームを指定している箇所はrinseのバグを回避するためです*2。これがないとamd64のDebian GNU/Linux環境でi386のCentOS環境を作成することができません。
/etc/fstabへの追記は、再起動する度にmountしなおすのが面倒だからです。
この作業はbuild-in-chroot.shの中で自動化されています。
CentOSのchroot環境ができたら、その環境にビルド専用アカウントを作成し、そのユーザでビルドします。ここで紹介する作業はbuild-rpm.shの中で自動化されています。ここでは、その中の一部を説明します。
まず、ビルド用ユーザが存在しない場合はユーザを作成します。
USER_NAME=milter-manager-build if ! id $USER_NAME >/dev/null 2>&1; then useradd -m $USER_NAME fi
次に、そのビルド用ユーザが実行するビルドスクリプトを作成します。.tar.gzや.specなどのビルドに必要なファイルはchroot環境の/tmp/以下(/var/lib/chroot/centos-i386/tmp/以下)に事前にコピーしておきます。それぞれの処理内容はコメントとして説明しています。
BUILD_SCRIPT=/tmp/build-milter-manager.sh VERSION=`cat /tmp/milter-manager-version` cat <<EOF > $BUILD_SCRIPT #!/bin/sh # RPMパッケージのビルドは~/rpm/以下で行う。 if [ ! -f ~/.rpmmacros ]; then cat <<EOM > ~/.rpmmacros %_topdir \$HOME/rpm EOM fi # RPMパッケージ作成に必要なディレクトリを作成。 # rpmdevtoolsパッケージに含まれているrpmdev-setuptreeコマンド # でも同様のことができるよう。 mkdir -p rpm/SOURCES mkdir -p rpm/SPECS mkdir -p rpm/BUILD mkdir -p rpm/RPMS mkdir -p rpm/SRPMS # ソースと.specを配置。 cp /tmp/milter-manager-$VERSION.tar.gz rpm/SOURCES/ cp /tmp/milter-manager.spec rpm/SPECS/ # RPMパッケージ作成。 rpmbuild -ba rpm/SPECS/milter-manager.spec EOF
このビルドスクリプトをビルド用ユーザで実行します。
chmod +x $BUILD_SCRIPT su - $USER_NAME $BUILD_SCRIPT
ビルドが成功するとビルド用ユーザの~/rpm/RPMS/i386/以下にRPMパッケージができます*3。
RPMパッケージができたら、それらに署名をします。
RPMでは、パッケージに署名することによりパッケージ作成者のなりすましを防止することができます。署名の検証を無効にすることもできるので、署名なしのRPMパッケージでもYumリポジトリで公開・インストールすることはできますが、よほどの理由がない場合は署名をするべきでしょう。
パッケージへの署名はパッケージ作成時にも作成後にも行うことができます。パッケージ作成後に行う場合はrpmコマンドの--resignオプションを使います。署名する鍵は_gpg_nameで指定します。
% rpm -D "_gpg_name Kouhei Sutou <kou@clear-code.com>" --resign XXX.rpm
これをそれぞれの.rpmに対して行います。
RPMパッケージに署名したら、署名済みRPM使ってYumリポジトリを作ります。
milter managerは安定版と開発版の2つのリリースラインがあります。そのため、以下のようなディレクトリ構成でYumリポジトリも2つ作ります。下の図のdevelopmentとstableがそれぞれYumリポジトリになります。各YumリポジトリはSRPMS, i386, x86_64ディレクトリを持ち、その下にビルドしたRPMパッケージを配置します。
.
+--- centos/
+--- 5/
+--- development
| +--- SRPMS/
| | +--- milter-manager-1.5.0-0.src.rpm
| | +--- ...
| +--- i386/
| | +--- CentOS/
| | +--- milter-manager-1.5.0-0.i386.rpm
| | +--- ...
| +--- x86_64/
| +--- CentOS/
| +--- milter-manager-1.5.0-0.x86_64.rpm
| +--- ...
+--- stable
+--- SRPMS/
| +--- milter-manager-1.4.1-0.src.rpm
| +--- ...
+--- i386/
| +--- CentOS/
| +--- milter-manager-1.4.1-0.i386.rpm
| +--- ...
+--- x86_64/
+--- CentOS/
+--- milter-manager-1.4.1-0.x86_64.rpm
+--- ...
このように.rpmを配置したらSRPMS, i386, x86_64のそれぞれのディレクトリに対してcreaterepoコマンドを実行します。この作業はupdate-repository.shで自動化されています。
for dir in centos/5/*/*; do createrepo $dir done
createrepoコマンドを実行するとそれぞれのディレクトリの下にrepodataというディレクトリが作成され、パッケージの情報が格納されます。
これでYumリポジトリは完成です。HTTPでアクセスできるところにアップロードしてください。
http://milter-manager.sourceforge.net/centos/以下でアクセスできるところにアップロードしたとすると、Yumのリポジトリ指定は以下のようになります。
/etc/yum.repos.d/milter-manager.repo:
[milter-manager] name=milter manager for CentOS-$releasever baseurl=http://milter-manager.sourceforge.net/centos/$releasever/stable/$basearch/ gpgcheck=1 enabled=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-milter-manager
gpgkeyはRPMに署名した時に使ったキーの公開鍵が入ったファイルを指定します。公開鍵は以下のコマンドで出力できます。
% gpg --export --armor "Kouhei Sutou <kou@clear-code.com>"
これを/etc/pki/rpm-gpg/RPM-GPG-KEY-milter-managerに置くことになります。
つまり、Yumリポジトリを登録するためには以下の作業が必要になります。
2段階にわかれているので面倒ですね。Yumリポジトリ登録の手間を軽減するために、「Yumリポジトリを登録するRPMパッケージ」を作りましょう。
ここで作成する「Yumリポジトリを登録するRPMパッケージ」はbuild-repository-rpm.shで自動化されています。
Yumリポジトリ登録に必要なものは「リポジトリを指定するファイル」と「RPMパッケージを署名している公開鍵」の2つです。それらをアーカイブした.tar.gzがソースのパッケージを作成します。
% tar cvzf milter-manager-repository.tar.gz milter-manager.repo RPM-GPG-KEY-milter-manager
.specはこのようになります。
Summary: milter manager RPM repository configuration
Name: milter-manager-repository
Version: 1.0.0
Release: 0
License: GPLv3+
URL: http://milter-manager.sourceforge.net/
Source: milter-manager-repository.tar.gz
Group: System Environment/Base
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
BuildArchitectures: noarch
%description
milter manager RPM repository configuration.
%prep
%setup -c
%build
%install
%{__rm} -rf %{buildroot}
%{__install} -Dp -m0644 RPM-GPG-KEY-milter-manager %{buildroot}%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager
%{__install} -Dp -m0644 milter-manager.repo %{buildroot}%{_sysconfdir}/yum.repos.d/milter-manager.repo
%clean
%{__rm} -rf %{buildroot}
%post
rpm -q gpg-pubkey-1c837f31-4a2b9c3f &>/dev/null || \
rpm --import %{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager
%files
%defattr(-, root, root, 0755)
%doc *
%pubkey RPM-GPG-KEY-milter-manager
%dir %{_sysconfdir}/yum.repos.d/
%config(noreplace) %{_sysconfdir}/yum.repos.d/milter-manager.repo
%dir %{_sysconfdir}/pki/rpm-gpg/
%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager
%changelog
* Sat Feb 06 2010 Kouhei Sutou <kou@clear-code.com>
- (1.0.0-0)
- Initial package.
大事な部分はBuildArchitecturesと%postの部分です。
このYumリポジトリ登録RPMはプラットフォームに関係なく使えるのでnoarchを指定しています。
%postではRPMにも公開鍵を登録しています。
%post
rpm -q gpg-pubkey-1c837f31-4a2b9c3f &>/dev/null || \
rpm --import %{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager
このYumリポジトリ登録RPMはDebian GNU/Linux上でも作成できます。作成方法はCentOS上での方法と同じです。
% echo "%_topdir $HOME/rpm" > ~/.rpmmacros
% mkdir -p ~/rpm/{SOURCES,SPECS,BUILD,RPMS,SRPMS}
% cp milter-manager-repository.tar.gz ~/rpm/SOURCES/
% cp milter-manager-repository.spec ~/rpm/SPECS/
% rpmbuild -ba ~/rpm/SPECS/milter-manager-repository.spec
これで、~/rpm/RPMS/noarch/以下に.rpmができ、~/rpm/SRPMS/以下に.src.rpmができます。
ここで作成したRPMが冒頭で紹介したRPMです。このRPMを使うことで以下のようにパッケージをインストールできるのでしたね。
% sudo rpm -Uvh http://milter-manager.sourceforge.net/centos/5/milter-manager-repository-1.0.0-0.noarch.rpm % sudo yum install -y milter-manager
ここまできたら、後はアクセスできる場所にアップロードするだけです。SourceForge.netではrsyncでアップロードできます。今回はhttp://milter-manager.sourceforge.net/centos/以下で公開したいので以下のようなコマンドになります。
% rsync -avz --exclude .gitignore centos/ \
ktou,milter-manager@web.sourceforge.net:/home/groups/m/mi/milter-manager/htdocs/centos
rsyncではパスの最後の「/」の有無に注意してください。
以上がRPMパッケージの作成と作成したRPMパッケージをYumリポジトリで公開する手順です。これら一連の手順を自動化したものはmilter managerのリポジトリで公開しています。読んでみてわかる通り、手順が多く、バージョンが上がる毎に手動で作業するのは大変です。簡単にバージョンアップに対応できるよう、自動化しておきましょう。
先日、LOCAL DEVELOPER DAY '10 WinterでRubyでmilterを作る方法について話してきました。どのタイミングでどのmilterプロトコルのコマンドが発行されるかについても説明しているので、Rubyではなく(libmilterを使って)Cでmilterを実装する場合にも参考になる部分があるはずです。むしろ、Rubyとmilterの組み合わせについて話している部分は薄めです。これは、Rubyそのものとmilterの仕組みを理解していればRubyとmilterを組み合わせることは容易だからです。
少しgroongaについてもふれています。
それでは、ダイジェストで資料の内容を紹介します。完全版はリンク先を見てください。資料のPDF・ソースもリンク先にあります。
具体的にRubyでmilterを作る話に入る前に、まず、前提となる知識を確認します。
はじめにメールフィルタ、次にメールフィルタの仕組みの1つであるmilterについて簡単に説明します。その後、一度milterから離れてSMTPについて説明します。これはmilterの動作を理解するためにはSMTPの動作も知っておく必要があるからです。SMTPの動作を確認したらそれをふまえてmilterの具体的な動作を説明します。
ここまできたらRubyでmilterを作るための下準備は整っているはずです。実際に1つRubyでmilterを作ってみます。
説明の途中にいくつか確認ポイントがあります。それぞれの技術は他の技術をベースになりたっているので、ベースとなっている技術をおさえていくことが、理解してしっくりくるためのコツです。
それぞれの確認ポイントをゴールとして最終的な「Rubyでmilterを作れる」ようになるゴールまでたどりついてください。
メールシステムとは外部とユーザ間でメールを配信するシステムです。すべてのメールシステムではそのままメールをやりとりするのではなく、メールを配信するまでのあいだに、メールに対してなんらかの処理を実行します。つまり、すべてのメールシステムにはメールフィルタ機能が備わっています。
メールシステムでメールフィルタを実現する方法はいくつかありますが、その1つがMTA(メールサーバ)のプラグインとして実現する方法です。この方法のメリットはMTAを変更せずにメールフィルタ機能を変更できることです。milterはこのタイプで動作するメールフィルタです。
メールフィルタはメールシステムが持っている必須機能の1つです。その実現方法としてMTAのプラグインとして実現する方法があり、milterもその方法で実現されているメールフィルタです。
それでは、milterの概要について説明します。
milterの名前の由来は「mail filter」です。milterは汎用的なメールフィルタの仕組みのため、同じメールフィルタを異なるMTAと一緒に使うことができます。
Sendmailを用いているメールシステムではmilterを利用していることが多く、milterをサポートした商用のメールフィルタも多く存在します。最近ではPostfixのmilterサポートがリリース毎に改善されていっているため、Postfixを用いたメールシステムでもmilterを利用するケースが徐々に増えています。
「milter」は文脈によって異なるものを指すことがあります。そこで、ここでは混乱を避けるために異なる名前で呼ぶことにします。
まず、メールフィルタそのものを「milter」と呼びます。
メールフィルタとMTAは別プロセスで動作するため、プロセス間通信でフィルタ対象のメールやフィルタ結果などをやりとりする必要があります。そのやりとりのきまりを「milterプロトコル」と呼びます。
そして、「milter」と「milterプロトコル」をサポートしたMTAを含んだメールフィルタの仕組み全体を「milterシステム」と呼びます。
「milter」といった場合は「メールフィルタそのもの(ここでいうmilter)」という意味で使う場合と、「メールフィルタの仕組み(ここでいうmilterシステム)」という意味で使われる場合が多いです。「milter」という単語が使われている場合はどちらの意味かを判断できるようになってください。
milterプロトコルはSMTPと密接に関連したプロトコルです。そのため、milterプロトコルについて説明する前に、SMTPについて確認します。
SMTPは以下の4つのコマンドが基本となるシンプルなプロトコルです。
まず、「HELO」で接続したSMTPクライアントの情報を伝えます。以下の例ではSMTPサーバ(MTA)からのメッセージは先頭に「<」をつけて示します。SMTPクライアントのメッセージは先頭に「>」をつけて示します。
% telnet localhost smtp < 220 note-pc.example.com ESMTP Postfix (Ubuntu) > HELO localhost.example.com < 250 note-pc.example.com
挨拶が済んだらSMTPセッションのスタートです。1つのSMTPのセッションで複数のメールを送ることができます。「MAIL FROM」、「RCPT TO」、「DATA」で1つのメールを送ります。
まず、「MAIL FROM」で送信者を伝えます。
> MAIL FROM: <kou@example.com> < 250 2.1.0 Ok
次に、「RCPT TO」で宛先を伝えます。
> RCPT TO: <info@example.com> < 250 2.1.5 Ok
同じメールを複数の宛先に送ることもできます。その場合は「RCPT TO」を複数回実行します。
最後に「DATA」でメールの内容を伝えます。メールの最後は「.」だけの行になります。
> DATA < 354 End data with <CR><LF>.<CR><LF> > Subject: Hello > From: <kou@example.com> > To: <info@example.com> > > This is a test mail! > . < 250 2.0.0 Ok: queued as 054C624FB
これで、1通のメールを送信できました。続けてメールを送信する場合はまた「MAIL FROM」から始めます。
SMTPセッションを終了する場合は「QUIT」です。
> QUIT < 221 2.0.0 Bye
これで1つのSMTPセッションが終了しました。
milterプロトコルはSMTPと密接に関わっています。それでは、milterプロトコルの詳細を説明します。
milterプロトコルにもSMTPと同じようにコマンドがあります。そして、そのコマンドはSMTPのコマンドと対応したものになっています。まずSMTPのコマンドを説明したのはそのためです。
例えば、SMTPで「HELO」というコマンドが実行された場合、「HELO」に対応する「helo」というmilterプロトコルのコマンドが発行されます。このとき、SMTPクライアントが指定したHELOコマンドの引数がmilterに渡されます。
MTAはmilterにコマンドを送った後、milterからの返答があるまでSMTPクライアントには返答しません。つまり、milterが「helo」でrejectを返すことで、SMTPクライアントの「HELO」コマンドへの返答をrejectとすることができます。これにより、MTAがSMTPレベルでできることとほとんど同じことをmilterで実現できます。
mitlerプロトコルのコマンドはほとんどSMTPのコマンドに対応していますが、milterプロトコルのコマンドの方がより細かくなっています。例と一緒にコマンドの対応を説明します。
SMTPでの最初のコマンドは「HELO」ですが、milterプロトコルでは「helo」よりも前にコマンドが発行されます。それが、SMTPクライアントがSMTPサーバに接続したときに発行される「connect」コマンドです。
「connect」コマンド以外はSMTPのコマンドとmilterプロトコルのコマンドは1対1で対応します。「envfrom」の「env」は「envelope」の略で、「封筒」という意味です。「envfrom」で「差出人」という意味、「envrcpt」で「宛先」という意味です。「rcpt」は「recipient」の略で「受信者」という意味です。
SMTPでは1つのメールを複数の宛先に送信できます。この場合、複数回「RCPT TO」を指定します。STMPで複数回「RCPT TO」が指定されるので、milterプロトコルでも「envrcpt」コマンドが複数回発行されます。
SMTPの「DATA」コマンドはmilterプロトコルではより細かいコマンドに分解されています。
まず、「DATA」コマンド時にはmilterプロトコルの「data」コマンドがすぐに発行されます*1。その後、SMTPクライアントはメール本体を送信しますが、「header」などのイベントはすぐには発生しません。SMTPクライアントがデータの終了を示す「.」のみの行を入力するまでは何も起きません。「.」のみの行が入力されると、MTA側でメール本文をパースして「header」、「eoh」(end of header: ヘッダーの終わり)、「body」、「eom」(end of message: メッセージの終わり)コマンドを発行します。もちろん、ヘッダーもパースしてあるので、MTAは「ヘッダー名」と「ヘッダー値」と分解した状態で情報を渡します。
このようにmilterプロトコルはSMTPと密接に関わっています。milterプロトコルのコマンドがわかれば、自分が必要な機能を持つmilterを実現するためにはどのコマンドを利用すればよいかを考えることができるでしょう。
説明用のサンプルとしてメール検索を実現するmilterを作成します。今回はSubject、From、Toと本文のみを扱うことにします。
メール検索を実現するために、全文検索エンジンとしてgroongaを、milterライブラリとしてmilter managerのRubyバインディングを使います。
groongaは全文検索のためのインデックス作成機能だけではなく、データストアの機能も持っています。groongaのデータストアはカラム指向で、リレーショナルデータベースとは違い、レコード(行)毎にデータをまとめて持つのではなく、カラム(列)毎にデータをまとめて持っています。
このようにデータを持つと、同じカラムの複数の値へのアクセスを高速に行うことができます。このため、カラムの値を使った集計処理を高速に実行できます。集計処理とは、例えば、SQLでいうGROUP BYのような処理です。
集計処理を用いると絞り込み検索をしやすいユーザーインターフェイスを提供することができます。例えば、ショッピングサイトで商品に複数のタグがついているとします。このとき、同じタグがついている商品が何項目あるかを表示してリンクにします。1つも商品が属していないタグは表示しないようにすれば、ユーザは無駄な絞り込み操作を行わずにすみます。
全商品(123件) タグ スポーツ(58件)← リンクにする 映画(45件) ← リンクにする 食べ物(36件) ← リンクにする 旅行(0件) ← 表示しない
この状態で「スポーツ」をクリックしたとします。
全商品(123件) > スポーツ(58件) タグ スポーツ ← 選択済みなので表示しない 映画(26件) ← リンクにする 食べ物(0件) ← 表示しない 旅行(0件) ← 表示しない
このように、絞り込んだ後にがっかりするような操作を示さないことにより、絞り込み検索をしやすいユーザインターフェイスを作ることができます。がっかりするような操作かどうかを判断するために、同じ値を持つレコードの個数を数える、といった集計処理をしています。
groongaはキー管理のためのデータ構造としてハッシュテーブルとバイナリパトリシアトライを採用しています。バイナリパトリシアトライはパトリシアトライの一種です。
ここにB+木とパトリシアトライの説明を書く予定でしたが、もう、だいぶ長くなっているので省略します。また別の機会があれば紹介します。
パトリシアトライを利用すると効率よく最長一致検索を実現できます。これを試してみるためのサンプルアプリケーションを用意しました。
リンク先ではキーワードを変えて試すことができます。
最長一致機能を利用してキーワード検出している部分のソースは以下の通りです。
target_text = "..." keywords = request["keywords"].split words = Groonga::PatriciaTrie.create(:key_type => "ShortText", :key_normalize => true) keywords.each do |keyword| words.add(keyword) end tagged_text = words.tag_keys(target_text) do |record, word| "<span class='keyword'>#{word}</span>" end
まず、パトリシアトライを作り、キーワードを登録します。Groonga::PatriciaTrieにはtag_keysという便利メソッドがあり、これを使うと「最長一致検索」→「キーワードにタグ付け」をより簡潔に記述することができます。
全体のソースはリンク先にあるソース一式の中に含まれています。
groongaのRubyバインディングであるRuby/groongaはスキーマ定義のためのDSLを提供しています。
メールを保存するMessagesテーブルにはsubject、from、to、bodyカラムを定義しています。今回は簡単のため、宛先は1つのみ扱うことにしています。
次に、高速に全文検索を行うために索引を作成します。Termsテーブルのキーに単語(ここではbigramを利用しているので1文字か2文字の文字列)、カラムにその単語が出現するMessagesレコードのID(とN-gramなので単語の出現位置)を保持します。
subjectカラムとbodyカラムでそれぞれに対して索引を作成しています。こうすることにより、「どこかに○○が含まれているメールを検索」といった検索だけではなく、「Subjectに○○が含まれているメールを検索」、「本文に○○が含まれているメールを検索」というような細かい検索ができるようになります。細かい検索が必要ない場合はMessagesテーブルに検索対象をすべて入れたカラムを1つ作り、そのカラムに対して索引を作成してもよいでしょう。
Groonga::Schema.define do |schema| schema.create_table("Messages") do |table| ... table.text("text") end schema.create_table("Terms", :type => :patricia_trie, :default_tokenizer => "TokenBigram", :key_normalize => true) do |table| table.index("text") end end messages = Groonga["Messages"] from = "kou@clear-code.com" to = "info@clear-code.com" body = "Hello Ruby and milter!" text = "#{from} #{to} #{body}" # <- textに検索対象をまとめる messages.add(:from => from :to => to, :body => body, :text => text) query = "Ruby" messages.select do |record| record["text"].match(query) # <- textカラムで全文検索 end
データの保存・検索の仕組みはできたので、あとは、groongaのデータベースにメールを登録するだけです。
milter managerのRubyバインディングのAPIでは、ユーザがmilterプロトコルのコマンドに対応するメソッドを定義し、ライブラリ側がそのメソッドを呼び出します。今回必要な情報はヘッダーと本文にあります。そのため、今回のmilterは以下のようになります*2。
class ArchiveMilter < Milter::ClientSession def initialize @messages = Groonga["Messages"] @values = {} @encoding = nil @body = "" end def header(context, name, value) case name when /\A(Subject|From|To)\z/i key = $1.to_s.downcase utf8_value = NKF.nkf("-w", value) @values[key] = utf8_value when /\AContent-Transfer-Encoding\z/i @encoding = value end end def body(context, chunk) @body << chunk end def end_of_message(context) nkf_option = "-w" nkf_option << " -MB" if @encoding == "base64" @values["body"] = NKF.nkf(nkf_option, @body) @messages.add(@values) end end
このように、Rubyでmilterを作るときは必要な処理の部分だけを記述するだけですみます。つまり、やりたいことを実現するためにどういうデータが必要で、どのタイミングでそのデータを手に入れられるかがわかれば、Rubyでmilterを作ることは簡単だということです。
登録したメールは以下のように検索・表示することができます。
query = "Ruby" # <- 検索キーワード messages = Groonga["Messages"] result = messages.select do |record| record["subject"].match(query) | record["body"].match(query) end result.sort([["_score", :desc]]).each do |message| puts "-" * 78 puts "score: #{message.score}" puts "Subject: #{message.subject}" puts "From: #{message.from}" puts "To: #{message.to}" puts puts message.body puts "-" * 78 end
Rubyでmilterを作る方法について説明しました。そのために必要な技術として、milterプロトコルの具体的な動作も説明しました。ここで説明されている内容を理解していれば、より詳細なmilter関連情報も理解しやすくなるでしょう。英語ですが、milterに関する情報はmilter.orgにまとまっています。より詳しい情報を知りたい場合はチェックするとよいでしょう。
札幌はやはりやさいい雰囲気に包まれていました。札幌Ruby会議02とは少し違う雰囲気でしたが、似ているとは感じました。
一度、札幌の人たちに会いに行ってみてはいかがでしょうか。
2010-02-14 - iakioの日記 - postgresqlグループ
「C言語でPostgreSQLを拡張する」というタイトルで石田さんが淡々とライブコーディングされていました。会場とやりとりをしながらコーディングする様子を見ていると、札幌っぽい雰囲気を感じることができるでしょう。
今年も肉の日がきましたね。
Ruby/groongaの0.9.0がリリースされました。リリース後すぐにgroongaの新しいバージョン0.1.6がリリースされたため、0.1.6に対応した0.9.1がすぐにリリースされました。gemで自動インストールされるgroongaのバージョンが0.1.6になっているだけで、Ruby/groongaの機能は変わっていません。
いつも通り、以下のコマンドでインストールできます。システムにgroongaがインストールされていない場合は自動でダウンロードしてインストールします。
% sudo gem install groonga
全文検索エンジンgroongaの特徴はgroongaのドキュメントを参照してください。
前のリリースでは0.0.7だったバージョン番号が一気に0.9.1まであがっています。バージョン番号から想像できる通り、初のメジャーリリース1.0.0を意識しはじめたということです。
0.9.x系列では1.0.0に向けて以下の2点を重点的に開発していきます。
APIを改良するため、以前のバージョンとは互換性が壊れることがありますが、今後より便利にRuby/groongaを使うために、今のうちに積極的に改良していく予定です。使ってみて、「ここがこうなっていたらもっと使いやすい」、「こういうAPIがあると便利」などという意見があったらぜひお知らせください。
Groonga::Context.default#[]のショートカットとしてGroonga.[]を導入するなど、もうすでに便利なAPIの追加は始まっています。
現在のRuby/groongaにはまだ高速化の余地があります。あまり意識せずにスクリプトを書いてもなるべく高速に動作するようにライブラリ側でできることはなるべくライブラリ側で頑張る方向で開発していく予定です。ユーザが使いやすくなるように処理系が頑張るというのはRuby本体と同じ方向です。
今年も年に一度の肉の日がきました。
Ruby/groonga 0.9.1がリリースされています。Rubyで全文検索システムを構築したい場合はRuby/groongaも検討してみてはいかがでしょうか。今のうちに改善案をだしておけばメジャーリリース時にはそれが反映されてより便利に高速な全文検索システムを構築できるようになるかもしれません。
先日、milter managerを用いたメールフィルタリングについて話してきました。milter managerというより、milter managerがベースにしているmilterという技術についての説明の方に重点をおいています。これは、日本でのmilter情報が不足しているのを少しでも解消したいということからです。
milterはPostfixも積極的にサポートを強化している有用な技術の1つです。メールに関わっている方はその仕組みや動作について把握しておいて損はないはずです。
それでは、コメント付きでとばしとばし資料の内容を紹介します。完全版はリンク先を見てください。資料のPDF・ソースもリンク先にあります。
milterについて説明する前に、まず、より一般的なメールフィルタについてまとめておきます。その後、メールフィルタの仕組みの1つであるmilterについて説明し、最後にmilter managerも利用したmilterの使い方について説明します。
外部から届いたメールをユーザに配信するのがメールシステムです。メールシステム内部では単純に届いたメールをそのままユーザに配信するのではなく、なんらかの処理をしてから配信します。そのため、ユーザには加工されたメールが配信されます。例えば、Receivedというメールヘッダを追加したりします。
つまり、メールシステムの中にはメールフィルタの機能が内臓されています。それでは、メールフィルタについてもう少し詳しくみていきます。
メールフィルタの実現方法にはいくつかありますが、大きく以下の3種類に分けられます。
それぞれの方法について図で説明します。
MTAに組み込む方法では、MTA内にメールフィルタの機能を内蔵させます。MTA単体で実現できます。
例えば、Postfixではaccess(5)やcidr_table(5)を用いてフィルタを適用することができます。
MTAのプラグインとしてフィルタを実現する方法では、MTAと別のプロセスにメールを通すことによりフィルタリングします。この別のプロセスがフィルタプラグインです。milterもこのタイプのメールフィルタの仕組みです。
MTAとプラグインのやりとり方法はMTA独自のことがほとんどです。SendmailやPostfixなど複数のMTAで動くmilterの方が珍しいといえます。milterが複数のMTAでサポートされているのは、既に多くの有用なmilterが存在しているからと思われます。
複数のMTAでメールシステムを構築することもできます。フィルタMTAを挿入する方法では、外部からのメールをユーザに配信するまでの間に、フィルタリングをするMTAにメールをリレーすることで、メールフィルタリングを実現します。MTA間のやりとりにはSMTPを使うので、どのMTAでも実現できる方法です。
アプライアンス製品などはこの方法でメールフィルタ機能を提供することが多いです。
それでは、それぞれのメールフィルタの実現方法による違いです。それぞれ特徴があるので、必要とされている機能や導入した後のメンテナンスなども考慮して、メールシステム毎に適した方法を選択することが重要です。
MTAに組み込む方法の利点はお手軽であるということです。組み込まれている機能はMTA毎に違いますが、組み込まれた機能で十分な場合はこの方法を利用するとよいでしょう。機能を追加する場合は、パッチを当てたり、バージョンアップをしたりすることになります。機能を追加する場合はメンテナンスのことをよく考えてください。
MTAのプラグインとしてメールフィルタを利用する方法の利点は、柔軟性です。多くの場合、プラグインではMTA本体が実現していることのほとんどすべてを実現できます。そのため、必要な機能をMTA本体を変更せずに追加することができます。MTA組み込み方法よりは導入が面倒ですが、システムや世間の変化にあわせたメールシステムを構築する場合は、機能追加や複数の機能を組み合わせられるこの方法を利用するとよいでしょう。
フィルタMTAを挿入する方法の利点は、どのMTAでも利用できることです。フィルタMTAを挿入する場合は、MTAが数珠つなぎになります。そのため、MTAを挿入・削除するたびに複数のMTAのリレー設定を変更する必要があり、面倒です。もし、必要な機能をすべて満たしたフィルタMTAを1つ導入するのであれば、この方法がよいでしょう。複数のフィルタMTAを導入する場合は導入・メンテナンスのことをよく考えてください。
以上のことをふまえると、以下のポイントをメールフィルタの実現方法を選択するために使えます。
メールフィルタは迷惑メール対策に利用されることが多いです。その観点から考えるとどの方法がよいでしょうか。
迷惑メールの配信方法は多様化しているため、1つの対策で完璧に防ぐことはできません。複数の手法を組み合わせて対応する必要があります。
また、これからも新しい手法で迷惑メールは送られ続けるでしょう。そうなった場合でも新しい対策を適用できることが求められます。
よって、迷惑メール対策には、複数の機能を組み合わせることが容易で、新しく機能を追加できるプラグインタイプの方法が適しているといえます。プラグインタイプの中でも、複数のMTAで利用でき、すでに多くの実装が存在するmilterがオススメです。ということで、milterの説明に入ります。
「milter」といっても文脈によっては微妙に異なるものを指すことがあります。「広義の○○」「狭義の○○」といわれたりするものと同じようなものです。
「milter」はメールフィルタの仕組み全体のことを指す場合と、フィルタプログラムのことを指す場合が多いです。「milterに対応したMTA」という場合は「メールフィルタの仕組み」を指していますし、「このmilterを使う」という場合は「フィルタプログラム」のことを指しています。
milterの説明に入る前に、混乱しないように別々の名前をつけておきます。ただし、正式な名前ではないことに注意してください。あくまで、わかりやすさのためにつけた名前です。
それぞれの関係を図示するとこうなります。
MTAとmilter間でやりとりするmilterプロトコルはSMTPと密接に関わっています。milterプロトコルでのコマンドのほとんどはSMTPでのコマンドに由来しています。
例えば、MTAがSMTPで「EHLO」コマンドを受け取ったらmilterにも対応する「helo」コマンドが送られます。その時、EHLOで受け取ったFQDNも一緒に送られます。
また、milterプロトコルはSMTPと平行して動作するのも重要なポイントです。MTAがメッセージすべてを受信してからmilterにコマンドを送るのではないので、「helo」コマンドのときにmilter側でrejectすれば、MTAも「EHLO」コマンドのときにrejectし、「MAIL FROM」や「RCPT TO」などそれ以降のコマンドを受けとりません。
milterシステムでは複数のmilterを同時に利用することができます。複数のmilterを利用する場合もSMTPと平行して動作する点は変わりません。
複数のmilterを利用する場合に注意することは、それぞれのmilterはコマンド毎に順番に動くということです。1つめのmilterの処理が終わってからはじめて2つめのmilterにコマンドが送られます。
1つめのmilterの処理結果が2つめのmilterに影響を与えます。例えば、1つめのmilterで本文を変更したら、2つめのmilterには元の本文ではなく1つめのmilterが変更した本文が渡されます。DKIMなどメッセージが変更されると問題があるような処理をする場合はmilterの順序に気をつけてください。
例えば、DKIMの場合は、送信するメールの署名も受信したメールの署名の検証も一番外部に近い部分で行います。署名を行うmilterは、他のすべてのmilterが処理を終わった後に適用しなければ署名が壊れてしまうため、一番最後のmilterにします。署名を検証するmilterは、他のmilterがメッセージを変更する前に適用しなければ検証に失敗してしまうため、一番最初のmilterにします。
以上がmilterの動作です。それでは、milterの使い方について進みます。
milterシステムを使い込んでいくといくつか気になるところがでてきます。milterシステムはプラグインタイプなので他の方法に比べて機能の追加は容易ですが、さすがにmilterの数が増えてくると面倒になります。
milterはすべてのセッションに対して適用されます。ホワイトリストの管理は各milterの役割になります。しかし、複数のmilterを利用している場合は、複数の形式でホワイトリストを管理することになりメンテナンスが面倒になります。
これらの問題点を解決するソフトウェアがmilter managerです。
milter managerはMTA側に足りない機能を補って、milterをより使いやすくするソフトウェアです。MTA・milterどちらにも変更を加えずに導入することができるため、既存の環境を壊すことなく導入できます。
クリアコードの他のソフトウェアと同様、milter managerもオープンソースソフトウェアとして公開しているので自由に利用することができます。
milter managerはMTAとmilterの間で動作します。MTAからのmilterプロトコルのコマンドをmilterに転送し、milterからのレスポンスをMTAに転送します。MTAとmilterの間でやりとりを制御することができます。
milter managerはmilterとして実装されているため、MTAには1つのmilterとしてみえます。また、milter managerはMTA側のmilterプロトコルも実装していて、その機能を使ってmilterへコマンドを転送しています。よって、milterからはmilter managerがMTAのようにみえます。このため、MTAもmilterも変更なしでそのまま動作することができるのです。
milterシステムの設定・管理にいくつか作業が必要になります。
milterを追加する場合は、milter本体を設定した後にMTAにmilterを登録する必要があります。複数のmilterを同時に利用している場合はmilter本体の設定のみをして、MTA側に登録することを忘れてしまうこともあります。
milter managerはそのような部分を自動化し、単純なミスを防ぎます。/etc/以下を走査し、インストールされているmilterを検出したら自動で登録します。また、インストールはされているが無効にされている場合もそれを検出し、自動でそのmilterを使用しないようにします。
milterシステムは複数のMTAでサポートされていますが、完全に同じサポート状況ではなく、MTA毎に異なる部分があります。そのため、milterによってはSendmailでは動くが、Postfixでは動かないというのもありました。(最近はそのようなmilterは減ってきています。)
milter managerはMTAとmilterの間で動作します。その位置でSendmailとPostfixのmilterシステムのサポートの違いを吸収するため、同じmilterを変更せずにそのまま動作させることができます。
他にも、Sendmailはmilter毎にタイムアウトなどのパラメータを設定できるが、Postfixはmilter全体の設定なってしまうなどといった違いがあります。このような違いも、milter managerがmilter毎にパラメータを管理することにより、PostfixでもSendmailと同じようなmilterシステムサポートを利用することができます。
このようにmilter managerを導入することにより、milterシステムの管理が負担を減らすことができます。milter managerはMTAとmilterの間に入って動作するため、MTA・milterのどちらも変更せずにmilterシステムのサポートをより充実させることができます。このため、メールシステム全体の管理の負担も減ります。
前述の通り、milterはすべてのメールに対して適用されるため、milter全体でホワイトリストを共有したり、ユーザ毎に適用するmilterを選択することが面倒になります。それぞれのmilterにまたがって設定をする必要があります。
milter managerを使うとこの問題を解決することができます。MTAはすべてのmilterを適用しますが、milter managerはSMTPセッションの情報を使って動的に適用するmilterを選択する機能を提供しています。また、LDAPやRDBなどからもデータを取得することができるため、メールシステム外にあるアカウントシステムと連携した制御をすることもできます。
この機能を使うことにより、milter全体で共有するホワイトリストやユーザ毎にどのmilterを適用するかということをmilter managerレベルで一括管理できます。これにより、ユーザからの要望や利用状況に合わせた柔軟なメールシステムをメンテナンスしやすい構成で運用できます。
前述の通り、milter managerはmilterシステムをより使いやすくする機能を提供します。しかし、それだけにとどまらず、さらにメールシステムをより使いやすくする機能も提供します。その1つがtarpit問題の解決方法の提供です。
tarpitとはSMTPクライアントへのレスポンスを(RFCの範囲内で)わざと遅らせることにより、すぐに切断してくる迷惑メール送信SMTPクライアントからのメールを受信しないようにする迷惑メール対策手法です。
迷惑メール送信側はタイムアウト時間をRFCで示されている時間よりも短めに設定していることが多いというデータがあります。これは、できるだけ短時間で多くの迷惑メールを送信したいというのが理由ではないかと考えられています。この特徴にあわせた迷惑メール対策手法がtarpitです。
tarpitを使うことにより多くの迷惑メールを受信せずにすみますが、同時SMTPセッション数が増加し、リソース消費量が増えるという問題があります。1〜2分程度のレスポンス遅延を導入することにより、2倍程度セッション数が増えるというデータもあります。
この問題は、SMTPクライアントが切断したらすぐにSMTPセッションを終了することにより緩和することができます。Postfixなどでtarpitを実現すると、SMTPクライアントの切断を検出せずに、指定した時間までSMTPセッションを維持してしまいます。そのため、必要以上に同時SMTPセッション数が増加してしまうのです。
milter managerにはSMTPクライアントが切断したらすぐにSMTPセッションを終了する機能がついています。
milter managerはmitlerプロトコルでMTAからSMTPクライアントのソケット情報を受け取っています。それとnetstatコマンドの結果*1を照合することによってMTA外からSMTPクライアントの切断を検出します。切断を検出したらMTAとmilter双方に処理の中止を通知することですぐにSMTPセッションを終了することができます。
milterをベースとした柔軟なメールフィルタリングを実現するための仕組みを説明しました。
メールフィルタリングには以下のような仕組みがあり、それぞれ特徴があります。
新しい迷惑メール対策にも柔軟に対応していくメールシステムにはプラグインタイプがオススメです。プラグインタイプの仕組みの1つとしてmilterを説明しました。
milterはSMTPと密接に連携している仕組みです。milterプロトコルについても説明し、milterがどのような流れでフィルタリングを行うかも示しました。すでに多くの迷惑メール対策手法がmilterとして実現されているため、今すぐmilterを使って迷惑メール対策を組み込んだメールシステムを構築することができます。
最後に、milterシステムをより使いやすくするソフトウェアとしてmilter managerを紹介しました。milter managerを導入すると、設定の自動化や一元管理によりメンテナンスが容易なメールシステムが構築できることを説明しました。
組織に合わせたメールシステムを構築する場合があれば参考にしてみてください。
*1 Linux上では/proc/以下の情報を利用することも考えられます。
2010年1月29日付で、テスティングフレームワークUxUのバージョン0.7.6をリリースしました。
utils.wait()について今回のアップデートでの目玉となる新機能は、非同期な機能のテストをより簡単に記述できるようにするヘルパーメソッドであるutils.wait()です。これは、以下のように利用します。
function testSendRequest() { myFeature.sendRequest(); utils.wait(1000); // 1000ミリ秒=1秒待つ assert.equals('OK', myFeature.response); } function testLoad() { var loaded = { value : false }; content.addEventListener('load', function() { content.removeEventListener('load', arguments.callee, false); loaded.valeu = true; }, false); myFeature.load(); utils.wait(loaded); // valueがtrueになるまで待つ assert.equals('OK', content.document.body.textContent); }
また、utils.wait()は関数の中でも利用できます。
function testSendRequest() { function assertSend(aExpected, aURI) { myFeature.sendRequest(aURI); utils.wait(1000); assert.equals(aExpected, myFeature.response); } assertSend('OK', '...'); assertSend('NG', '...'); assertSend('?', '...'); }
utils.wait()が受け取れる値は、これまでの処理待ち機能で yieldに渡されていた値と同じです。詳しくは処理待ち機能の利用方法をご覧下さい。大抵の場合、yield Do(...);と書かれていた箇所は、utils.wait(...);へ書き換えることができます。
ただし、このヘルパーメソッドはFirefox 3以降やThunderbird 3以降など、Gecko 1.9系の環境でしか利用できません。Thunderbird 2などのGecko 1.8系の環境ではエラーとなりますのでご注意下さい。(それらの環境でもテストの中で処理待ちを行いたい場合は、従来通りyieldを使用して下さい。)
これまでUxUでは、「機能を実行した後、N秒間待ってから、機能が期待通りに働いたかどうかを検証する」「初期化処理で、ページの読み込みの完了を待ってから次に進む」といった処理待ちを実現する際は、JavaScript 1.7以降で導入されたyieldを使う仕様となっていました。
yieldを含む関数はジェネレータとなり、関数の戻り値をイテレータとして利用できるようになります。この時ジェネレータの内側から見ると、「yieldが出現する度に処理が一時停止する」という風に考えることができます。
function gen() { alert('step1'); yield 'step1 done'; alert('step2'); yield 'step2 done'; alert('step3'); } var iterator = gen(); // この時点ではまだ関数の内容は評価されない var state; state = iterator.next(); // 'step1' が表示される alert(state); // 'step1 done' が表示される state = iterator.next(); // 'step2' が表示される alert(state); // 'step2 done' が表示される try { state = iterator.next(); // alert('step3'); が実行される } catch(e if e instanceof StopIteration) { // 次のyieldが見つからないので、StopIteration例外が投げられる }
UxUに従来からある処理待ち機能は、この考え方を推し進めて作られています。テスト関数の中にyieldがある場合(つまり、関数の戻り値がイテレータとなる場合)は、フレームワーク側で自動的にイテレーションを行い、yieldに渡された値をその都度受け取って、次にイテレーションを行うまでの待ち条件として利用しています。例えば、数値が渡された場合はその値の分の時間だけ待った後で次にイテレーションを行う、といった具合です。
このやり方の欠点は、yieldを含む関数から任意の戻り値を返すことができないという点です。
function gen() { alert('step1'); yield 'step1 done'; alert('step2'); return 'complete'; } try { var iterator = gen(); } catch(e) { alert(e); // TypeError: generator function gen returns a value }
returnを書くと、関数の実行時にエラーになってしまいます。どうしても何らかの値を取り出したい場合は、値を取り出すためのスロットとなるオブジェクトを引数として渡すなどの工夫が必要になります。
function gen(aResult) { alert('step1'); yield 'step1 done'; alert('step2'); aResult.value = 'complete'; } var result = {}; var iterator = gen(result); var state; state = iterator.next(); // 'step1' が表示される alert(state); // 'step1 done' が表示される try { state = iterator.next(); // 'step2' が表示される } catch(e if e instanceof StopIteration) { alert(result.value); // 'complete' が表示される }
また、ジェネレータは実行してもその段階では関数の内容が評価されないという点にも注意が必要です。例えば以下のようなテストは、期待通りには動いてくれません。
function testSendRequest() { function assertSend(aExpected, aURI) { myFeature.sendRequest(aURI); yield 1000; assert.equals(aExpected, myFeature.response); } assertSend('OK', '...'); assertSend('NG', '...'); assertSend('?', '...'); }
この例では、assertSend()を実行したことで戻り値としてイテレータが返されているものの、そのイテレータに対するイテレーションが一切行われていないため、リクエストも行われなければアサーションも行われないということになってしまっています。これは以下のように、返されたイテレータをそのままフレームワークに引き渡して、フレームワーク側でイテレーションを行わせる必要があります。
function testSendRequest() { function assertSend(aExpected, aURI) { myFeature.sendRequest(aURI); yield 1000; assert.equals(aExpected, myFeature.response); } yield assertSend('OK', '...'); yield assertSend('NG', '...'); yield assertSend('?', '...'); }
また、このままではジェネレータの中で発生した例外のスタックトレースを辿れないという問題もあります。スタックを繋げるためには、ヘルパーメソッドのDo()を使ってyield Do( assertSend('OK', '...') )のように書かなければなりません。
utils.wait()を使う場合、これらのことを考慮する必要はありません。冒頭のサンプルコードのように、素直に書けば素直に動作してくれます。
utils.wait()がどのように実装されているかについても解説しておきます。
このメソッドの内部では、Gecko 1.9から実装されたスレッド関連の機能を利用しています。
window.setTimeout(function() { alert('before'); }, 0); alert('after');
JavaScriptは基本的にシングルスレッドで動作するため、このようにタイマーを設定すると、その処理はキューに溜められた状態となります。その上で、メインの処理が最後まで終わった後でやっとキューの内容が処理され始めるため、この例であれば「after」「before」の順でメッセージが表示されることになります。
var finished = false; window.setTimeout(function() { alert('before'); finished = true; }, 0); var thread = Cc['@mozilla.org/thread-manager;1'] .getService() .mainThread; while (!finished) { thread.processNextEvent(true); } alert('after');
Gecko 1.9以降のスレッド機能を使うと、メインの処理を一旦中断して先にキューに溜められた処理の方を実行し、その後改めてメインの処理に戻るということができます。実際に、こちらの例では「before」「after」の順でメッセージが表示されます。UxU 0.7.6ではこれを応用して、任意の条件が満たされるまでthread.processNextEvent(true);でメインの処理を停止し続けることによって、処理待ちを実現しています。
なお、HTML5にもWeb Workersというスレッド関係の機能がありますが、こちらは別スレッドでスクリプトを動作させる機能しか持っていないため、上記のようなことは残念ながらできません。
UxU 0.7.6からは、utils.wait()を使ってより簡単に処理待ちを行えるようになりました。Firefox 3以降やThunderbird 3以降専用にアドオンを開発する際には、是非利用してみて下さい。
来月2/13(土)に札幌で開催されるLOCAL DEVELOPER DAY '10/WinterでRubyでメールフィルターを作る方法について話します。
Rubyでメールフィルターを開発する方法について話します。以下、背景などをまじえてもう少し詳しく説明します。
SendmailやPostfixといったよく使われているメールサーバにはmilterというメールフィルターを追加する仕組みが実装されています。milterを使うことにより、メールサーバに迷惑メール対策機能やウィルスチェック機能、メールアーカイブ機能、添付ファイル自動暗号化機能などを追加することができます。つまり、メールサーバ本体を変更せずに組織のポリシーに合わせたメールシステムを構築することができるということです。
milterという仕組みを使ったメールフィルター*1はすでにたくさん開発されているので、既存のものを組み合わせてメールシステムを構築できることも多いです。しかし、組織特有の事情などがある場合は既存のメールフィルターでは対応できないこともあるでしょう。そういった場合、新しくメールフィルターを開発したり既存のメールフィルターを改造して対応できます。
通常、メールフィルターはC言語で開発する必要がありますが、milter maangerが提供する機能を利用することによってRubyを使って素早くメールフィルターを開発することができます。
今回は、milter managerの機能を使ってRubyでメールフィルターを開発する方法やデバッグの方法などを紹介します。milterという仕組みを知らない方でもわかるように、milterという仕組みから順を追って説明します。ただし、Rubyについて詳しく説明しないので、Rubyがまったくわからない方には少し厳しいかもしれません。札幌でRubyについて詳しくなりたい方はRuby札幌に参加することをオススメします。
来月開催されるLOCAL DEVELOPER DAY '10/Winterで、Rubyを用いてメールフィルターを作る方法について話すので、それを告知しました。
JavaScript(Ext JS)やWebアプリケーションのテスト(Selenium)、ドキュメント指向データベース(MongoDB)、リレーショナルデータベース(PostgreSQL)の話などもあるようです。参加登録も必要ないので、興味のある方はお気軽に参加してみてはいかがでしょうか。
*1 混乱するかもしれませんが、「milterという仕組みを使ったメールフィルター」もmilterと呼びます。milterといった場合は仕組みよりメールフィルターのことを指すことが多いです。
注: Debianデベロッパーが書いた文章ではありません。Debianデベロッパーになりたい方はDebianが公式に配布している文書の方をお勧めします。
Web上にはいくつかDebianパッケージの作り方を説明しているページがありますが、はじめてDebianパッケージを作る場合には情報不足のものが多いです。例えば、古めの文書でCDBSを使っていなかったり、「あとは適当に修正して…」などと手順の一部が省略されている文書が多いです。
ここでは、全文検索エンジン兼カラムストアのgroongaを例にしてDebianパッケージの作り方を説明します。ここで説明するのは、1つのソースから1つのパッケージを作成するのではなく、1つのソースから複数のパッケージを作成する方法です。この方法は、ライブラリの場合に多く用いられます。
また、aptitudeでインストールできる形で公開する方法もざっくりと紹介します。ここで作成するDebianパッケージは以下のようにインストールすることができます。
以下の内容の/etc/apt/sources.list.d/groonga.listを作成
deb http://packages.clear-code.com/debian/ lenny main deb-src http://packages.clear-code.com/debian/ lenny main
インストール:
% sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 1C837F31 % sudo aptitude update % sudo aptitude -V -D -y install groonga libgroonga-dev
以下の内容の/etc/apt/sources.list.d/groonga.listを作成
deb http://packages.clear-code.com/debian/ unstable main deb-src http://packages.clear-code.com/debian/ unstable main
インストール:
% sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 1C837F31 % sudo aptitude update % sudo aptitude -V -D -y install groonga libgroonga-dev
注: Ubuntu本家のuniverseセクションもインストール対象としておくこと
以下の内容の/etc/apt/sources.list.d/groonga.listを作成
deb http://packages.clear-code.com/ubuntu/ hardy universe deb-src http://packages.clear-code.com/ubuntu/ hardy universe
インストール:
% sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 1C837F31 % sudo aptitude update % sudo aptitude -V -D -y install groonga libgroonga-dev
注: Ubuntu本家のuniverseセクションもインストール対象としておくこと
以下の内容の/etc/apt/sources.list.d/groonga.listを作成
deb http://packages.clear-code.com/ubuntu/ karmic universe deb-src http://packages.clear-code.com/ubuntu/ karmic universe
インストール:
% sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 1C837F31 % sudo aptitude update % sudo aptitude -V -D -y install groonga libgroonga-dev
それでは、まずはパッケージの作り方です。
dh_makeを利用する方法が紹介されていることが多いですが、dh_makeではたくさんのファイルが生成されるため、はじめてパッケージを作成する場合はとまどってしまいます。そのため、ここでは手動でパッケージを作成する方法を紹介します。
groongaはライブラリとgroongaのデータベースを管理するコマンドで構成されています。この場合、Debianでは以下のように複数のパッケージに分解します。
groongaの場合は以下のようなパッケージを作成することとします。
Debianパッケージを作成する場合にはじめにつまずくポイントがファイルやディレクトリの名前付けの規則です。今回は現時点での最新リリースgroonga 0.1.5を利用するので、それを例にして説明します。
まず、元のソースをダウンロードし、展開します。
% cd /tmp % wget http://groonga.org/files/groonga/groonga-0.1.5.tar.gz % tar xvzf groonga-0.1.5.tar.gz groonga-0.1.5/ ...
この状態ではDebianの名前付け規則から外れています。規則に合わせるためには以下のようにします。
% mv groonga-0.1.5.tar.gz groonga_0.1.5.orig.tar.gz
つまり、以下のような構成にする必要があります。
. |--- groonga-0.1.5/ | |... | ... +--- groonga_0.1.5.orig.tar.gz
一般化するとこうです*1。
ソースの準備ができたら、ソースを展開したディレクトリの直下にdebian/ディレクトリを作成します。Debianパッケージ用のファイルはこのディレクトリの下に置きます。
% cd groonga-0.1.5 % mkdir debian/
このような構成になります。
. |--- groonga-0.1.5/ | |--- debian/ | | |... | |... | ... +--- groonga_0.1.5.orig.tar.gz
今回のパッケージ作成に必要なファイルは以下の通りです。それぞれ順番に説明します。
debian/controlにはパッケージ全体の情報と個々のパッケージの情報を書きます。
まず、パッケージ全体の情報です。
Source: groonga Priority: optional Maintainer: Kouhei Sutou <kou@clear-code.com> Build-Depends: debhelper (>= 5), cdbs, autotools-dev, libmecab-dev Standards-Version: 3.7.3 Homepage: http://groonga.org/
それぞれ以下のような意味です。
パッケージが準拠しているDebianポリシーマニュアルのバージョン。現在の最新バージョンは3.8.3。
3.7.3を指定しているのはUbuntu 8.04 LTS Hardy Heron用にもパッケージを作成するため。Hardyの時点ではバージョンが3.7.3だった。
次に、個々のパッケージの情報を書きます。ここでは、libgroonga-devパッケージを例にして説明します。他のlibgroonga, groongaパッケージは後で示します。
Package: libgroonga-dev
Section: libdevel
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, libgroonga (= ${binary:Version})
Description: Development files to use groonga as a library
Groonga is an open-source fulltext search engine and column store.
It lets you write high-performance applications that requires fulltext search.
.
This package provides header files to use groonga as a library.
それぞれ以下のような意味です。
依存しているパッケージ。「${misc:Depends}」、「${shlibs:Depends}」はビルドされたファイルから自動的に検出された依存パッケージに置換される。/usr/bin/dh_*を見ると、他にも「${python:Depends}」などがあるよう。
「libgroonga (= ${binary:Version})」とバージョン指定付きで明示的にlibgroongaを指定しているのは、異なるバージョンのgroongaパッケージを使用しないようにするため。同じソースから生成したパッケージではバージョンまで指定しておく方が無難。
libgroonga, groongaパッケージも含んだdebian/controlは以下のようになります。それぞれのパッケージについての記述は空行で区切ります。
Source: groonga
Priority: optional
Maintainer: Kouhei Sutou <kou@clear-code.com>
Build-Depends: debhelper (>= 5), cdbs, autotools-dev, libmecab-dev
Standards-Version: 3.7.3
Homepage: http://groonga.org/
Package: libgroonga-dev
Section: libdevel
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, libgroonga (= ${binary:Version})
Description: Development files to use groonga as a library
Groonga is an open-source fulltext search engine and column store.
It lets you write high-performance applications that requires fulltext search.
.
This package provides header files to use groonga as a library.
Package: libgroonga
Section: libs
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Library files for groonga.
Groonga is an open-source fulltext search engine and column store.
It lets you write high-performance applications that requires fulltext search.
.
This package provides library files.
Package: groonga
Section: database
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, libgroonga (= ${binary:Version})
Description: An open-source fulltext search engine and column store.
It lets you write high-performance applications that requires fulltext search.
.
This package provides 'groonga' command.
debian/rulesにはパッケージの作り方を書きます。CDBS(Common Debian Build System)を使うとよくある処理をより簡潔に書くことができます。
groongaはAutotoolsを使っているのでCDBSのAutotools用のファイルを読み込みます。
#!/usr/bin/make -f include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/autotools.mk
最初の行をみるとわかる通り、debian/rulesはMakefileと同じ書式を使います。
続いて、各パッケージで作成するディレクトリを指定します。変数の名前は「DEB_INSTALL_DIRS_#{パッケージ名}」となります。
DEB_INSTALL_DIRS_groonga = \ /usr/bin \ /usr/share/groonga DEB_INSTALL_DIRS_libgroonga = \ /usr/lib DEB_INSTALL_DIRS_libgroonga-dev = \ /usr/include/groonga \ /usr/lib/pkgconfig
groongaパッケージでは/usr/bin/groongaをインストールするので、/usr/binを指定する、libgroonga-devパッケージでは/usr/include/groonga/groonga.hをインストールするので/usr/include/groongaを指定するといった具合です。
最後にそれぞれのパッケージにどのファイルを含めるかを指定します。ターゲットの名前は「install/#{パッケージ名}」となります。ターゲットの後の「:」がふたつなのは実行する処理を追加しているためです。GNU makeの「::」の挙動について詳しく知りたい場合はGNU makeのinfoのDouble-Colon Rulesのところを読んでください。
install/groonga::
cp -ar debian/tmp/usr/bin/* debian/groonga/usr/bin/
cp -ar debian/tmp/usr/share/groonga/* \
debian/groonga/usr/share/groonga/
install/libgroonga::
cp -ar debian/tmp/usr/lib/libgroonga* debian/libgroonga/usr/lib/
install/libgroonga-dev::
cp -ar debian/tmp/usr/include/groonga/* \
debian/libgroonga-dev/usr/include/groonga/
cp -ar debian/tmp/usr/lib/pkgconfig/* \
debian/libgroonga-dev/usr/lib/pkgconfig/
ビルドしたファイルはdebian/tmp/以下にインストールされます。インストールされたファイルをcpでそれぞれのパッケージに振り分けています。通常はdh_movefilesを使うようなのですがinstall/libgroongaで使っているようなワイルドカードが使えないようなのでcpを使っています。
debian/rules全体はこうなります。
#!/usr/bin/make -f include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/autotools.mk DEB_INSTALL_DIRS_groonga = \ /usr/bin \ /usr/share/groonga DEB_INSTALL_DIRS_libgroonga = \ /usr/lib DEB_INSTALL_DIRS_libgroonga-dev = \ /usr/include/groonga \ /usr/lib/pkgconfig install/groonga:: cp -ar debian/tmp/usr/bin/* debian/groonga/usr/bin/ cp -ar debian/tmp/usr/share/groonga/* \ debian/groonga/usr/share/groonga/ install/libgroonga:: cp -ar debian/tmp/usr/lib/libgroonga* debian/libgroonga/usr/lib/ install/libgroonga-dev:: cp -ar debian/tmp/usr/include/groonga/* \ debian/libgroonga-dev/usr/include/groonga/ cp -ar debian/tmp/usr/lib/pkgconfig/* \ debian/libgroonga-dev/usr/lib/pkgconfig/
debian/copyrightにはソースの作者・ライセンスとパッケージの作者・ライセンス情報を書きます。ソースの作者・ライセンス情報はソースの中にある情報を利用します。groongaの場合はAUTHORSファイルに作者がリストされていました。著作権者の情報はソースのヘッダーファイルに、ライセンスの情報はCOPYINGにありました。
パッケージのライセンスはGPLv3+にしました。
This package was debianized by Kouhei Sutou <kou@clear-code.com> on
Thu, 15 Jan 2010 14:52:04 +0000.
It was downloaded from <http://groonga.org/>
Upstream Author(s):
Daijiro MORI <morita at razil. jp>
Tasuku SUENAGA <a at razil. jp>
Yutaro Shimamura <yu at razil. jp>
Kouhei Sutou <kou at cozmixng. org>
Kazuho Oku <kazuhooku at gmail. com>
Moriyoshi Koizumi <moriyoshi at gmail. com>
Copyright:
Copyright(C) 2009-2010 Brazil
License:
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
The Debian packaging is (C) 2010, Kouhei Sutou <kou@clear-code.com> and
is licensed under the GPL, see `/usr/share/common-licenses/GPL'.
# Please also look if there are files or directories which have a
# different copyright/license attached and list them here.
debian/changelogには変更履歴を書きます。パッケージのバージョンはこのファイルから抽出されるのでパッケージのバージョンを上げた場合はこのファイルに追記することを忘れてはいけません。
debian/changelogに変更履歴を追記するための専用のコマンドdchがあるので、それを利用します。-vオプションで新しいバージョンを指定します。
% dch -v 0.1.5
例えば、以下のように書きます。
groonga (0.1.5) unstable; urgency=low * New upstream release -- Kouhei Sutou <kou@clear-code.com> Mon, 18 Jan 2010 17:58:31 +0900
これでパッケージに必要な情報はできあがりました。
パッケージをビルドするにはソースディレクトリで以下のコマンドを実行します。
[groonga-0.1.5]% debuild -us -uc
-usは.dscファイルにGPGでサインしないオプションで、-ucは.changesファイルにGPGでサインしないオプションです。
パッケージ作成を試行錯誤しているときは毎回ソースをビルドするのは面倒なものです。そのときは以下のように-ncオプションを指定すると、ビルド前にmake cleanを実行しなくなるのでビルドしなおさなくなります。
[groonga-0.1.5]% debuild -us -uc -nc
パッケージの作成が成功するとトップディレクトリにパッケージが作成されます。
. |--- groonga-0.1.5/ | |--- debian/ | | |... | |... | ... |--- groonga_0.1.5.orig.tar.gz |--- groonga_0.1.5.diff.gz |--- groonga_0.1.5.dsc |--- groonga_0.1.5_amd64.build |--- groonga_0.1.5_amd64.changes |--- groonga_0.1.5_amd64.deb |--- libgroonga-dev_0.1.5_amd64.deb +--- libgroonga_0.1.5_amd64.deb
生成したパッケージはdpkgでインストールできます。
[groonga-0.1.5]% sudo dpkg -i ../*groonga*.deb
Debianパッケージを作るとインストールが楽になりますが、dpkgでインストールしている場合は依存関係を自動的に解決してくれません。依存関係を自動的に解決するためにはAPTリポジトリを作る必要があります。
ただし、用意するのはわりと面倒です。1つのDebian/Ubuntuのバージョン・アーキテクチャ向けであればまだ頑張ろうかという気にはなれますが、複数のバージョン・アーキテクチャをサポートする場合は嫌になることでしょう。
ここでは、Cutterやmilter managerで使用しているDebianパッケージ作成からAPTリポジトリの更新を自動化しているスクリプトを紹介します。これらのスクリプトはCutterのリポジトリ などで公開されています。環境依存の部分があるので、利用する場合は自分の環境用に変更して利用してください。ライセンスはGPLv3+としますので、ライセンスの範囲内で自由に利用してください。
ただし、以下の説明はかなりざっくりとしているので気になる方は自分でスクリプトをみてください。
複数のバージョン・アーキテクチャをサポートする場合、それぞれの環境ごとのDebianパッケージを作成することが大変です。ここで紹介するスクリプトではDebian GNU/Linux sid (x86_64)の環境にchroot環境を作って、すべて同じマシン上で作成します。複数のマシンで作成してもよいのですが、そうするとOSのインストールやsshの設定などが面倒になります。
pbuilderを使うことも多いようですが、その環境の中にシェルで入って確認したいこともあるので、常にchroot環境を持っていた方が便利です*2。
Cutterの場合はchroot環境内で以下のビルドスクリプトを実行しています。単純なコマンドの羅列なので説明は省略します。
#!/bin/sh VERSION=1.1.0 sudo aptitude -V -D update && sudo aptitude -V -D -y safe-upgrade sudo aptitude install -y subversion devscripts debhelper cdbs autotools-dev \ intltool gtk-doc-tools libgtk2.0-dev libgoffice-0-{6,8}-dev \ libgstreamer0.10-dev libsoup2.4-dev mkdir -p ~/work/c if [ -d ~/work/c/cutter ]; then cd ~/work/c/cutter svn up else cd ~/work/c svn co https://cutter.svn.sourceforge.net/svnroot/cutter/cutter/trunk cutter fi cd ~/work/c rm -rf cutter-${VERSION} tar xfz cutter_${VERSION}.orig.tar.gz cd cutter-${VERSION} mkdir debian cp -rp ../cutter/debian/* debian/ if dpkg -l libgoffice-0-8-dev > /dev/null 2>&1; then : else sed -i'' -e 's/libgoffice-0-8/libgoffice-0-6/g' debian/control fi debuild -us -uc
そして、このビルドスクリプトを各chroot環境内で実行するために以下のようなMakefileを使っています。Debian GNU/Linuxのlennyとsid、UbuntuのHardyとKarmicのパッケージを作成します。また、それぞれi386用とamd64用を生成するので8種類のパッケージを作成することになります。
VERSION = 1.1.0 SERVER_PATH = ktou,cutter@web.sourceforge.net:/home/groups/c/cu/cutter/htdocs DISTRIBUTIONS = debian ubuntu CHROOT_BASE = /var/lib/chroot ARCHITECTURES = i386 amd64 CODES = lenny unstable hardy karmic update: for code_name in $(CODES); do \ target=$${code_name}-$${architecture}; \ case $${code_name} in \ lenny|unstable) \ distribution=debian; \ section=main; \ ;; \ *) \ distribution=ubuntu; \ section=main; \ ;; \ esac; \ (cd $${distribution}; \ mkdir -p dists/$${code_name}/$${section}/source; \ for architecture in $(ARCHITECTURES); do \ mkdir -p dists/$${code_name}/$${section}/binary-$${architecture}; \ done; \ apt-ftparchive generate generate-$${code_name}.conf; \ rm -f dists/$${code_name}/Release*; \ apt-ftparchive -c release-$${code_name}.conf \ release dists/$${code_name} > /tmp/Release; \ mv /tmp/Release dists/$${code_name}; \ gpg --sign -ba -o dists/$${code_name}/Release{.gpg,}; \ ); \ done upload: update for distribution in $(DISTRIBUTIONS); do \ (cd $${distribution}; \ rsync -avz --exclude .svn --delete \ dists pool $(SERVER_PATH)/$${distribution}; \ ); \ done download: for distribution in $(DISTRIBUTIONS); do \ (cd $${distribution}; \ rsync -avz $(SERVER_PATH)/$${distribution}/pool/ pool; \ ); \ done build: for architecture in $(ARCHITECTURES); do \ for code_name in $(CODES); do \ target=$${code_name}-$${architecture}; \ case $${code_name} in \ lenny|unstable) \ distribution=debian; \ section=main; \ ;; \ *) \ distribution=ubuntu; \ section=main; \ ;; \ esac; \ build_dir=$(CHROOT_BASE)/$$target/home/$$USER/work/c; \ pool_dir=$$distribution/pool/$$code_name/$$section/c/cutter; \ mkdir -p $$build_dir; \ cp ../cutter-$(VERSION).tar.gz \ $$build_dir/cutter_$(VERSION).orig.tar.gz && \ sudo su -c \ "chroot $(CHROOT_BASE)/$$target su - $$USER" < build-deb.sh; \ mkdir -p $$pool_dir; \ cp -p $$build_dir/*cutter*_$(VERSION)* $$pool_dir; \ done; \ done
細かい内容は省略しますが、以下のように使用します。準備が大変です。
% sudo mkdir -p /var/lib/chroot/ % sudo su -c 'debootstrap --arch i386 lenny /var/lib/chroot/lenny-i386' % sudo su -c 'debootstrap --arch amd64 lenny /var/lib/chroot/lenny-amd64' % sudo su -c 'debootstrap --arch i386 sid /var/lib/chroot/sid-i386' % sudo su -c 'debootstrap --arch amd64 sid /var/lib/chroot/sid-amd64' % sudo su -c 'debootstrap --arch i386 hardy /var/lib/chroot/hardy-i386' % sudo su -c 'debootstrap --arch amd64 hardy /var/lib/chroot/hardy-amd64' % sudo su -c 'debootstrap --arch i386 karmic /var/lib/chroot/karmic-i386' % sudo su -c 'debootstrap --arch amd64 karmic /var/lib/chroot/karmic-amd64' % sudo vim /etc/fstab proc /var/lib/chroot/lenny-i386/proc proc defaults 0 0 devpts /var/lib/chroot/lenny-i386/dev/pts devpts defaults 0 0 sysfs /var/lib/chroot/lenny-i386/sys sysfs defaults 0 0 ... % sudo mount -a ...それぞれのchroot環境に入って作業用ユーザを作成し、sudo可能にする...
準備ができたら以下のコマンドでパッケージがビルドできます。
% cd /tmp % svn export https://cutter.svn.sourceforge.net/svnroot/cutter/cutter/trunk/apt % wget http://downloads.sourceforge.net/cutter/cutter-1.1.0.tar.gz % cd apt % make build
成功すると*/pool/*/main/c/cutter/以下に.debが生成されています。
% ls */pool/*/main/c/cutter/*.deb debian/pool/lenny/main/c/cutter/cutter-bin_1.1.0-1_i386.deb debian/pool/lenny/main/c/cutter/cutter-doc_1.1.0-1_i386.deb ...
この状態でAPTリポジトリを作成できます。
% make update
GPGでサインしようとするのであらかじめGPGの設定を行っておいてください。APTリポジトリの作成に関する設定はdebian/*.confとubuntu/*.confです。環境に合わせて変更してください。
Makefileの中にはuploadというターゲットがあります。このターゲットを使うとupdateで作成したAPTリポジトリをsf.netのWebサーバにrsyncでアップロードできます。
groongaを例にしてDebianパッケージの作成方法を説明しました。ついでに、APTリポジトリの公開方法もざっくりと紹介しました。
Debianパッケージの作り方やAPTリポジトリの公開方法は、必要な人はあまりいないかもしれませんが、groongaのDebianパッケージを必要としている人はそれよりもいそうな気がします。もしよかったら使ってみてください。
先日、あしたのオープンソース研究所の第6回でオープンソースのマルチメディアフレームワークであるGStreamerを紹介してきました。
あしたのオープンソース研究所では、海外のオープンソースソフトウェアのドキュメントを翻訳されていて、翻訳対象の文書も募集されています。GStreamerなどいくつか応募したのですが、そのうちの1つとしてGStreamerを採用してもらえたのでGStreamerの概要を紹介をしてきました。
スライドを見ただけでは伝わらないはずなので少し説明も加えておきます。いくつか省略しているページもあるので、完全版が見たい場合は画像のリンク先を見てください。
GStreamerはマルチメディアのフレームワークです。音声・動画の再生、フォーマットの変換、録音・録画など基本的なことはもちろん、RTSPなどを用いたネットワーク通信を行うこともできます。
GStreamerにはgst-launchというコマンドラインツールが付属していて、gst-launchを使うことによりプログラムを作らなくてもGStreamerの機能を利用することができます。
GStreamerにはplaybinという機能があり、再生するときはこの機能が便利です。URIを指定するだけで、内容からフォーマットを自動で判別し、フォーマットに合わせた再生処理をします。音声・動画どちらでも再生することができます。
GStreamerではshのパイプのように機能をつなぎ合わせて目的を実現します。Ogg VorbisからAACへフォーマットを変換する場合は、以下のように機能を組み合わせます。
GStreamerはVideo4Linuxにも対応しているので、Linux環境ではVideo4Linuxを利用してPCに接続されたWebカメラで録画することもできます。ここでは以下のように機能を組み合わせて、Webカメラで撮影した動画を画面に出力しています。
撮影した動画を画面に出力する前にテキストを挿入することもできます。GStreamerでは機能をつなぎ合わせて目的を実現するので、間に追加の機能を挿入することが簡単にできます。
動画と合わせて音声も録音することができます。動画用のパイプライン(機能のつなぎ合わせ)と音声用のパイプラインが別々になっていることがポイントです。
GStreamerでは機能を付け替えることが簡単にできるため、データの入力元・出力先をファイルからネットワーク通信に変えてやることで別のホストに動画を転送することもできます。この例ではRTPなどを使わず、直接TCPでOggデータをやりとりしています。まず、データを受信して画面に表示するクライアント側を動かします。
別の端末で録画したものをOggにエンコードしてクライアント側にTCPでデータを送信します。Theoraにエンコードしている部分で「video/x-raw-yuv,framerate=10/1」としているのは、フレームレートをおとしてデータ量を減らすためです。
サーバ側・クライアント側で何もデータを変換しなければネットワーク経由でのファイルコピーも実現できます。GStreamerは機能をつなぎ合わせるための汎用的な環境を提供しているので、こんなこともできるよ、という話です。
GStreamerでどういうことができるのかというイメージをつかめたでしょうか。
GStreamerはGNOMEアプリケーションで利用されています。メディアプレイヤーのTotem、音楽プレイヤーのRhythmbox、CDリッパーのSound Juicer、VoIP・ビデオ会議アプリケーションのEkigaなどはGStreamerを利用しています。GStreamerはGNOMEアプリケーション以外でも利用されています。FlashプレイヤーのGnashや動画編集ソフトのPiTiVi、Mozillaテクノロジーをベースとした音楽プレイヤーであるSongbirdなどもGStreamerを利用しています。
GStreamerと類似しているソフトウェアにはFFmpeg、Phonon、QuickTime、DirectShowなどがあります。どれもマルチメディアを扱うソフトウェアなのでGStreamerと似ているのですが、GStreamerはこれらのソフトウェアと競合するものではありません。
これらのソフトウェアの関係を図示したものです。ここでは、「API」、「フレームワーク」、「ライブラリ」という層に分けていますが、一般的な分け方ではないので注意してください。実際、複数の層にまたがるソフトウェアが多く、この分類にすっきり当てはまるわけではありません。ただ、このようにざっくりと分類した方がイメージがつかみやすいのではないかということでこのような分類を導入しました。
上の層ほど高レベルのソフトウェアで下の層のソフトウェアを利用したりして実現されています。それぞれの層は以下のように分類しています。
ライブラリ層はコーデックなどマルチメディアデータのフォーマットを扱う機能などを提供します。GUIがないことが多く、プログラムや付属のコマンドラインツールなどからライブラリの機能を利用することになります。1つのライブラリで必要な機能がすべて満たされる場合はこの層を直接利用するとよいでしょう。複数のライブラリが必要になる場合は、フレームワーク層やAPI層を利用した方が開発効率がよくなることが多いです。この層にあるソフトウェアは、サーバ上でも広く利用されているFFmpegやTheoraをエンコード・デコードする機能を提供するlibtheoraなどの各種コーデック、などです。
フレームワーク層はメディアフォーマットのエンコード・デコード機能だけではなく、複数のフォーマットを統一的に扱う機能や、マルチメディア再生時の制御機能など、メディアプレイヤーで必要になるようなマルチメディア関連の機能を包括的に提供します。フレームワークに後から機能(コーデックなど)を追加する仕組みがあることが多く、この仕組みにより、プログラムの変更を最小限に抑えながらアプリケーションを新しいフォーマットに対応させたりすることができます。より汎用的なアプリケーションを開発する場合はこの層を使って開発するとよいでしょう。この層にあるソフトウェアは、GStreamerやMac OS XのQuickTime、WindowsのDirectShowなどです。
API層は実際の処理を行わず、フレームワークやライブラリを利用してプログラマが安心してマルチメディア機能を使うための安定したAPIを提供する層です。プログラマがAPI層を利用する利点は、環境(やフレームワークやライブラリ)に依存せずに同じコードでマルチメディアの機能を利用できることです。この層にあるソフトウェアはQtに含まれているPhononやMac OS XでのQTKitです。QtプログラマはPhononが提供するAPIを用いてプログラムを開発することで、クロスプラットフォームで動作するマルチメディア機能を実現することができます*1。
GStreamerが他のソフトウェアと競合しないのは、GStreamerが他のソフトウェアの機能を利用できるからです。GStreamerは、後からGStreamerに機能を追加できるプラグインシステムを実装しています。プラグインシステムを用いて、FFmpegの機能を利用したり、Mac OS XではQuickTimeの機能を利用したり、WindowsではDirectShowの機能を利用したりできます。つまり、GStreamerは他のソフトウェアと協調して動作することができます。このため、他のソフトウェアと競合しないのです。
GStreamerの概要を理解するために大事な概念は以下の4つです*2。
エレメントが個々の機能に対応します。エレメントにはデータの出入り口となるパッドがあります。エレメントから別のエレメントにデータを渡す場合は、エレメント同士を接続しなければいけません。これをリンクといいます。エレメント同士を接続するときは、パッドとパッドを接続します。
エレメントをつなぎ合わせて目的とする機能を実現したら、エレメントをパイプラインに入れます。パイプラインに入れると、エレメントの処理の開始・停止などを一括で指示できるようになります。それぞれのエレメントに対して指示する必要はありません。
エレメントはパッドの持ち方で以下の3種類に分類できます。
ソースエレメントはデータ出力用のパッド(ソースパッド)のみを持つエレメントです。データ生成用のエレメントで、エレメントのつなぎ合わせの先頭におきます。ファイルからデータを読み込むエレメントなどがソースエレメントです。
フィルタエレメントはデータの入力用パッド(シンクパッド)と出力用パッド(ソースパッド)を持つエレメントです。入力用と出力用のパッドを1つずつ持つエンコーダーやデコーダーのようにデータを変換するエレメントがあります。エレメントはパッドを複数持つことができます。マルチプレクサーは複数の入力から1つのコンテナフォーマットのデータを生成し、デマルチプレクサーは1つのコンテナフォーマットのデータを分解し、複数の出力データを生成します。
シンクエレメントはデータ入力用のパッド(シンクパッド)のみを持つエレメントです。データ受信用のエレメントで、エレメントのつなぎ合わせの最後におきます。ファイルにデータを出力するエレメントなどがシンクエレメントです。
目的の機能を実現するためにはソースエレメント→フィルタエレメント→…→フィルタエレメント→シンクエレメントというようにリンクします。
リンクしたエレメントはビン(パイプライン。パイプラインはビンの1種)に入れて利用します。
例えば、Ogg Vorbisを再生する場合はこのようにエレメントをリンクします。
どのエレメント同士もリンクできるわけではありません。パッドは受け付けられるMIME-typeを複数持っています。パッド同士が同じMIME-typeを利用する場合のみエレメントをリンクできます。
Ogg Vorbisプレイヤーの場合はこのようなMIME-typeのパッドでリンクしています。
Ogg Vorbis/Theoraプレイヤーはこのようになります。Oggデマルチプレクサーからの出力を両方とも利用しています。
GStreamerはマルチメディアフレームワークで、マルチメディアを扱う場合に必要な機能が一通り揃っていて、GNOMEアプリケーションなど多くのアプリケーションで利用されていています。PhononやQuickTimeなど類似のソフトウェアがありますが、GStreamerはそれらと競合するソフトウェアではなく、それらと協調して動作します。
GStreamerにはエレメントとパッドとビンという概念があります。
エレメントはデータを処理するもので、複数のエレメントをつなぎ合わせて目的の機能を実現します。エレメント同士をつなぎ合わせることをリンクといい、エレメントをリンクするときはエレメントのパッドとパッドをつなぎ合わせます。リンクしたエレメントをビン(パイプライン)に入れて目的の機能を利用します。
ここではGStreamerの概要のみを扱ったので、省略したことがたくさんあります。
あしたのオープンソース研究所ではGStreamerのチュートリアルを翻訳してくれるそうです。チュートリアルにはここで説明したことより多くのことが書かれているので、GStreamerに興味を持った方はチュートリアルも読んでみてください。翻訳は1,2ヶ月後には公開されているようなので、2月か3月になると日本語でチュートリアルが読めるのではないでしょうか。楽しみですね。
解説付きで資料を公開してみました。スライド中で使っているSVGの画像やRabbitのソースもあわせて公開しました*3。もし利用する場合ははじめにCOPYINGに書かれたライセンスを確認してください。
昨年は多くの方々にお世話になりました。ありがとうございます。 今年もどうぞよろしくお願いします。
早いもので、クリアコード設立から約3年半、ククログ開始から約1年半が経過しました。うまくいくことばかりではありませんでしたがこれまでやってこれました。
クリアコードは小さな受託開発の会社で、1人を除いて全員開発者です。すべての開発者はフリーソフトウェアの開発に関わっており、そこでの経験を活かして受託開発をしています。
フリーソフトウェアの開発で学んだことの1つに「いきなりとてもよくすることは難しい」ということがあります。細かいバグ修正の積み重ねがより安定したソフトウェアにつながり、細かい機能追加・改良がより使いやすいソフトウェアにつながる、ということを実際に開発に参加しながら実感してきました。
大きな機能追加も小さなコミットの積み重ねです。最初は「とりあえず動く」だったものが、コミットの積み重ねで便利で安定した機能になっていく過程を何度も体験しました。新機能・改良の提案もいきなり大きなパッチを提出するのではなく、1つずつ小さな提案を積み重ねて、最終的によりよいソフトウェアとなることを目指します。その過程で、当初想定していた方法とは違う方法で実現されることも珍しくありません。それは、他の開発者とやりとりをし、よりよい実現方法を見つけることがあるからです。
今年のクリアコードも、ソフトウェア開発や多くの方々とのやりとりなどからいろいろなことを学び、少しずつよりよくなっていく予定です。意識しないと気づかない程度になってしまうかもしれませんが、少しずつよくなっていくクリアコードを今年もお楽しみください。
早いもので今年も残り10日になりました。今年のクリアコードは積極的に社外でも活動してきました。(おそらく)そのおかげで、はじめてお会いするときに「あぁ、名前を聞いたことがある」と言ってもらえることが増えてきました。ありがとうございます。
今回はククログ上で公開した今年の活動をまとめてみます。
仙台Ruby会議01にスピーカーとして参加し、地方(たとえば東北)でプログラミングが好きな人がスキルアップするにはどうすればよいかということを自分の経験を基に話しました。
必ずしも東京などに出る必要はありません。場所よりも、こつこつと続けて少しずつよくなっていくことの方が重要です。そして、続けるためには「好きなこと」であることが大きな助けになります。タイトルが「まず好きなこと、そしてそれを続けること」となっているのはこのような理由からです。
オープンソースカンファレンス2009 Tokyo/Springに参加しました。クリアコードがイベントに協賛したのはこれがはじめてでした。OSCでは展示スペースでライブ開発をしながら、迷惑メール対策ソフトウェア milter managerやMozilla関連サービスの紹介をしました。(ライトニングトークでもmilter managerを紹介)
インターンシップの紹介もしていて、ブースに寄ってくれたかくたにさんも紹介してくれました。ありがとうございます。
ククログに書かれる技術情報は、CutterやUxUなど開発している・開発に参加しているプロジェクトのリリース情報がわりと多いのですが、この月はユーザ側から見た技術情報が2つありました。
どちらもたまにリンクされているので、役にたった方が少しはいるようです。嬉しいことです。
このあたりからgroongaの話がでてきます。
どちらもすでに情報が古くなっていることが残念です。いつか最新の状況にあわせて更新したいですね。
Fennec(モバイル端末向けのFirefox)の話は1月からいくつかありましたが、Fennecで日本語を表示するための設定を公開したのは4月でした。この情報は今でも有効な情報です。
クリアコードのSubversionリポジトリを公開したのも4月でした。
迷惑メール対策ソフトウェアmilter manager関連で2回話しました。
共有ライブラリから関数名を抜き出す方法を紹介しはじめたのは5月からです。
6月もmilter managerについて2回話しました。
Mach-O形式の共有ライブラリから関数名を抜き出す方法も紹介しました。
日本Ruby会議2009にスポンサー・スピーカーとして参加しました。ブースも出展しました。
4月から毎月groonga情報を公開していましたが、7月はRuby/groongaを使ったサンプルを公開しました。
Windows上で便利にgitを使うためのソフトウェアTurtoiseGitのtipsを紹介したのも7月です。
8月はクリアコードではじめてのインターンシップを行いました。インターンシップでの成果も公開しました。
8月もmilter managerについて話しました。
共有ライブラリから関数名を抜き出すシリーズの最後としてPE形式の共有ライブラリから関数名を抜き出す方法も紹介しました。
ここでは扱えきれていない部分があるので、いつかその部分にも対応したバージョンを紹介したいですね。
milterでtaRgreyを実現できるようになりました。
開発に役立つこととして、Emacsのこと、デバッグのこともありました。
ハードウェアと連携するWebアプリケーションを実現するための1つのサンプルとして、(Webページ内の)JavaScriptからCPUの情報にアクセスするためのAPIを提供するFirefoxアドオンをリリースしました。
10月はとちぎRuby会議02にスポンサー・スピーカーとして参加しました。
テスト開発に役立つこととして、データ駆動テストを紹介したのも10月です。
10月は久しぶりに日本語のGTK+の書籍が出版された月でもありました。GTK+が得意なクリアコードとして、最近のGTK+/GNOMEまわりの情報も交えながら少し紹介しました。
Firefox Developers Conference 2009にパネリスト・発表者として参加しました。
11月もテスト、もう少しいうと、テスティングフレームワークについての話題がありました。
Ruby関連で2回話しました。
1つは札幌Ruby会議02へのスポンサー・スピーカーでの参加です。札幌Ruby会議はとてもグッとくるステキなRuby会議でした。札幌Ruby会議は動画まわりが強力なので札幌に行かなくても様子を観ることができますが、札幌でないとわからないこともあるのではないでしょうか。(もちろん、その逆もあるでしょう。)
2つめはコンテンツワンさん主催のRuby on Railsセミナーです。今までは「Rails」というキーワードで集まる方の前で話す機会がなかったので、今回のような機会があってとてもよかったです。ありがとうございます。やはり、ActiveLdapなどRailsと一緒に使うことを意識しているライブラリは、このような場で話す方があっています。
このRuby on Railsセミナーがクリアコードの今年最後のトークでした。
月ごとに今年のクリアコードの活動をまとめました。
今年は2009/1/24の仙台Ruby会議01にはじまり、3月・4月以外は毎月どこかで話していました。また、OSCや日本Ruby会議などでは発表者としてだけではなく、スポンサーとしても参加しました。イベント運営にも多少は貢献できていたのであれば嬉しいことです。
イベント以外でも、20回以上のフリーソフトウェアリリースやソフトウェアの使い方の紹介など、技術面でも役に立つような情報を発信できたつもりです。お役に立てたでしょうか。
今年もありがとうございました。来年もクリアコードをよろしくお願いします。