株式会社クリアコード > ククログ

ククログ


Ubuntuでdebパッケージのテストをするには

はじめに

以前、パッケージをクリーンルームでビルドする方法としてCowbuilderを使うやりかたについて記事を書きました。

今回は、ビルドしたパッケージのインストールやアップグレードが正しく行えるかのチェックを支援してくれるツールである、 piupartsの紹介をします。

なお、前回の記事ではUbuntuを普段常用していて、unstable(sid)向けにdebパッケージをビルドした、というのを前提にしています。 今回の紹介内容もそれを前提にしているので、未読の方は前回の記事を参照してください。

piupartsとは

debパッケージのインストール、アンインストール、アップグレードのチェックを行うために開発されたソフトウェアです。 実際に、様々なリリース向けに数多くのパッケージをシステマチックにチェックするのに使われています。 チェック結果のレポートはpiuparts.debian.orgで参照できます。

日本語による説明資料としては、第63回東京エリアDebian勉強会の資料が詳しいので参考になります。*1

piupartsをインストールしよう

piupartsはもちろんUbuntuでも使えます。apt-getを使ってインストールできます。

sudo apt-get install piuparts

ただし、Ubuntu 14.04 LTSでインストールできるpiupartsは0.56とバージョンが古いので、Ubuntu 15.04 (Vivid Vervet)から0.60のパッケージをもらってきてインストールして使ったほうがいいかも知れません。*2

既存のベースイメージを利用してpiupartsを実行しよう

前回の記事では、Ubuntu 14.04 LTSでDebian unstable(sid)向けのパッケージをビルドすると、/var/cache/pbuilder/unstable-amd64/result以下にdebが保存されるようにしていました。

また、Debian unstable(sid)のベースイメージを /var/cache/pbuilder/unstable-amd64-base.cow に保存していました。

piupartsには様々なオプションがあるのですが、必須といえるオプションは次の通りです。

  • -dで対象となるリリースを指定します。
  • -tで作業ディレクトリを指定します。tmpfsを指定すると速いのでおすすめです。
  • -mでミラーサイトを指定します。Ubuntu上でunstable(sid)のテストをするのでdebianのミラーを参照しないといけません。
  • -eで既存のcowbuilderのベースイメージを指定します。これがないとベースイメージをつくるところから始めるので時間がかかります。
  • -lでログファイルを指定します。

上記をまとめて実行するときの指定は次の通りです。

sudo piuparts -d sid -t /tmp/newtmp -m "http://ftp.jp.debian.org/debian main" -e /var/cache/pbuilder/unstable-amd64-base.cow -l piuparts.log /var/cache/pbuilder/unstable-amd64/result/groonga-normalizer-mysql_1.0.6-1_amd64.deb

あとは、テストが終了するまでしばらく待ちましょう。

0m40.6s DEBUG: Removed directory tree at /tmp/newtmp/tmpuVJGah
0m40.6s INFO: PASS: All tests.
0m40.6s INFO: piuparts run ends.

問題ない場合には、上記のように最後にPASS: All tests.と表示されます。 失敗した場合には、ログファイルを精査して問題がどこにあるのかを特定して修正しておきましょう。*3

まとめ

今回はdebパッケージのインストールやアップグレードのチェックを支援してくれるpiupartsの使い方を紹介しました。 まだ使ったことがないのであれば、パッケージの品質を向上させるために導入してみてはいかがでしょうか。*4

*1 2010年の資料ですが、依然として有益な情報源です。

*2 0.56だとエラーになるけど、0.60では問題なかったケースがあった。

*3 成功した場合には問題ないけど、piupartsのエラーログを読むのはややつらいかも知れない。

*4  といってもGroongaのITPをしていたときはまだpiupartsの存在を知らなかったのであんまりえらそうな事は言えない。

2014-12-01

12/9のリーダブルコードのWeb授業(生放送)用の資料

一ヶ月ほど前に告知した通り、12/9(来週の火曜日)の21:00からschoo(スクー)でリーダブルコードのWeb授業「名著『リーダブルコード - より良いコードを書くためのシンプルで実践的なテクニック』を解説者と一緒に読み解こう」の先生をします。

授業の内容は次のスライドの内容と質疑応答になる予定です。興味のある方は授業ページの「受けたい!」ボタンを押してください。リマインダーが届くようになります。

schooでの授業以外にもリーダブルコードに関するセミナーや開発支援をしているので、リーダブルコードに興味のある方はお問い合わせください。

2014-12-04

milterプロトコル

これはPostfix Advent Calendar 2014の10日目の記事です。

Postfixは2.3からmilterという仕組みをサポートしました。milterとは「mail filter」の略で、送信したメールまたは受信したメールになんらかの処理を行う仕組みです。もともとはSendmailが作った仕組みですが、Sendmail・Postfix以外のMTAでもサポートしているMTAがあります。

Postfix 2.3でのmilterサポートは限定的な機能のみのサポートでしたが、Postfix 2.6ではSendmailとほぼ同等の機能をサポートしています。SendmailとPostfixでマクロ(後述)名が違うなど一部非互換な部分もありますが、SendmailでもPostfixでも同様に使えます。

Postfixユーザーの人にとっては、milterでどのようなことができるか、content filterとの使い分けはどうすればよいか、という情報が有用かと思いますが、ここではそのような説明をしません。そのような説明ではなくmilterプロトコルについて説明します。なお、milterプロトコルについての情報は、Postfixユーザーの人にとっては有用ではなく、milter開発者にとってもそんなに有用ではなく、一部のmilterライブラリー開発者にとっては有用*1です。Postfix Advent Calendarっぽくなくてごめんなさい。

しかも、残念ながら2014/12/10中に完成しませんでした。。。続きはいつかどこかで。。。

はじめに

多くのmilterはlibmilterというSendmailが提供しているライブラリーを利用して実装します。libmilter内でmilterプロトコルを実装しているのでmilter開発者がプロトコルの詳細を気にする必要はありません。libmilterのAPIを知っていれば十分です。しかし、ここではlibmilterのAPIは説明しません。milterを作るための情報を探している人は次のページを参考にしてください。

  • CまたはC++でmilterを実装したい人向け
  • Rubyでmilterを実装したい人向け
  • Pythonでmilterを実装したい人向け
    • pymilter:libmilterのPythonバインディング。
    • ppymilter:Pythonでmilterプロトコルを実装したライブラリー。
  • Perlでmilterを実装したい人向け
  • Haskellでmilterを実装したい人向け
    • RPF:HaskellでのReceiver's Policy Frameworkの実装。この中にmilterプロトコルの実装がある。

普通の人向けの情報は提供したので、ここからは普通の人向けではない情報です。

実はmilterプロトコルの仕様書はありません。Sendmailでの挙動が事実的な仕様になっています。Postfixや各言語でのmilterプロトコルの実装は、Sendmail・libmilterのソースコード*2や、実際のSendmail・libmilterの動きを参考にmilterプロトコルを実装したものです*3

ただ、milterプロトコルについてまとめたメモはあります。Perlでmilterプロトコルを実装した人が残したもの(英語)です。まとめた時期が古い(milterプロトコルのバージョンでいうと2)ので新しいmilterプロトコル(最新はバージョン6)についての情報はないのですが、基本的なことからまとまっているので有用な情報です*4

ベースのやりとり

それではmilterプロトコルについて説明します。

まずはベースとなるやりとりの方法について説明します。

milterプロトコルはMTAとmilter間でやりとりします。基本的に、MTAからコマンドを送り、milterが応答する、という流れになります。

MTA ー コマンド → milter
    ←   応答   ー

コマンドと応答のフォーマットは同じです。フォーマットは次の通りです。

| データサイズ(4バイト)| データID(1バイト) | データ |

「データサイズ」はネットワークバイトオーダーで表現されています。データサイズはデータサイズ自体のサイズ(4バイト)を含みません。「データID」と「データ」を合わせたサイズ(バイト数)です。

「データID」はこのデータの種類を示す1文字のASCII文字です。例えば、「CONNECTというコマンド」を表すデータIDは「C」です。

「データ」はデータIDに関連するデータです。データIDによって中身が変わります。0バイトのときもあります。

milterプロトコルを実装するときは、まずは、このフォーマットをデコードする処理とこのフォーマットにエンコードする処理を実装します。リンク先はmitler managerでの実装です。

一連のやりとり

ベースのやりとりがわかったところで、次は一連のやりとりについて説明します。個々のやりとりはこのあと説明します。

milterプロトコルはSMTPと関連が深いです。SMTPの1つのセッションがmilterプロトコルでの1つのセッションに対応します。milterプロトコルでの1つのセッションは次のようになります。ただし、一部省略しています。

MTA                           milter
ネゴシエーションコマンド →
                          ← ネゴシエーション応答
接続コマンド              →
                          ← 応答
HELOコマンド              →
                          ← 応答
MAIL FROMコマンド         →
                          ← 応答
RCPT TOコマンド1          →
                          ← 応答
RCPT TOコマンド2          →
                          ← 応答
                          ...
RCPT TOコマンドn          →
                          ← 応答
DATAコマンド              →
                          ← 応答
ヘッダーコマンド1         →
                          ← 応答
ヘッダーコマンド2         →
                          ← 応答
                          ...
ヘッダーコマンドn         →
                          ← 応答
ヘッダー終了コマンド      →
                          ← 応答
本文チャンクコマンド1     →
                          ← 応答
本文チャンクコマンド2     →
                          ← 応答
                          ...
本文チャンクコマンドn     →
                          ← 応答
メッセージ終了コマンド    →
                         (← メッセージ変更コマンド)
                          ← 応答

なお、SMTPでメールトランザクションが複数ある場合は、それに対応して「MAIL FROMコマンド」から「メッセージ終了コマンド」の「応答」までを複数回繰り返します。

SMTPのセッションと似ていますね。「HELOコマンド」、「MAIL FROMコマンド」、「RCPT TOコマンド」、「DATAコマンド」はそれぞれSMTPの対応するコマンドを実行したときに実行されます。SMTPで「DATA」の後に指定したメッセージはパースされて個別にmilterに送られます。

コマンドの種類

次は個々のやりとりのうち「MTAから送るデータ」について説明します。

MTAから送るデータをコマンドと呼びます。

コマンドのID、名前、説明は次の通りです。最初の行が凡例です。

  • #{ID}: #{名前}: #{説明}
  • A: アボート: メールトランザクション中で終了するコマンド。
  • B: 本文チャンク: 本文のチャンク(一部)を送るコマンド。本文が大きい場合は複数のチャンクにわけて渡される。(複数の本文チャンクコマンドが送られる。)
  • C: 接続: 接続してきたSMTPクライアントに関する情報を送るコマンド。
  • D: マクロ定義: コマンドに対する付加情報を送るコマンド。詳細は後述。
  • E: メッセージ終了: メッセージ全体を送ったことを知らせるコマンド。このコマンドの応答でだけメールを変更できる。詳細は後述。
  • H: HELO: SMTPのHELO/EHLOに関する情報を送るコマンド。
  • L: ヘッダー: メッセージのヘッダーを1つ送るコマンド。複数のヘッダーがある場合は複数回送られる。
  • M: MAIL FROM: SMTPのMAIL FROMに関する情報を送るコマンド。
  • N: ヘッダー終了: すべてのヘッダーを送ったことを知らせるコマンド。
  • O: ネゴシエーション: milterセッション開始時に動作を調整するコマンド。特殊なコマンド。詳細は後述。
  • Q: 終了: このセッションを終了することを指示するコマンド。
  • R: RCPT TO: SMTPのRCPT TOに関する情報を送るコマンド。複数のRCPT TOを指定した場合は複数回送られる。
  • T: DATA: SMTPのDATAに関する情報を送るコマンド。
  • U: 未知: SMTPで未知のコマンドを指定されたときに送るコマンド。

IDにはコマンドの名前と関連がある文字が使われていることが多いので、データを生で見てもなんとなくわかることがあります。

重要なコマンドについて補足します。

マクロ定義コマンド

マクロ定義コマンドは特殊なコマンドです。

マクロ定義コマンドは次に送るコマンドの付加情報を送るコマンドです。付加情報はキーと値のペアのリストです。なお、マクロ定義コマンドに対してmilterは応答しません。

次の各コマンドの前にMTAが送ります。

  • 接続コマンド
  • HELOコマンド
  • MAIL FROMコマンド
  • RCPT TOコマンド(それぞれのRCPT TOコマンド毎)
  • DATAコマンド
  • ヘッダー終了コマンド
  • メッセージ終了コマンド

図にすると次のようになります。

MTA                           milter
ネゴシエーションコマンド →
                         ← ネゴシエーション応答
マクロ定義コマンド       →
接続コマンド             →
                         ← 応答
マクロ定義コマンド       →
HELOコマンド             →
                         ← 応答
マクロ定義コマンド       →
MAIL FROMコマンド        →
                         ← 応答
マクロ定義コマンド       →
RCPT TOコマンド1         →
                         ← 応答
マクロ定義コマンド       →
RCPT TOコマンド2         →
                         ← 応答
                         ...
マクロ定義コマンド       →
RCPT TOコマンドn         →
                         ← 応答
マクロ定義コマンド       →
DATAコマンド             →
                         ← 応答
ヘッダーコマンド1        →
                         ← 応答
ヘッダーコマンド2        →
                         ← 応答
                         ...
ヘッダーコマンドn        →
                         ← 応答
マクロ定義コマンド       →
ヘッダー終了コマンド     →
                         ← 応答
本文チャンクコマンド1    →
                         ← 応答
本文チャンクコマンド2    →
                         ← 応答
                         ...
本文チャンクコマンドn    →
                         ← 応答
マクロ定義コマンド       →
メッセージ終了コマンド   →
                         (← メッセージ変更コマンド)
                         ← 応答

ただし、これはSendmailの動作です。Postfixは「ヘッダーコマンド」と「本文チャンク」のときもマクロ定義コマンドを送ってきます。「ヘッダーコマンド」の場合は「ヘッダー終了コマンド」用のマクロ定義コマンドを送ってきて、「本文チャンクコマンド」の場合は「メッセージ終了コマンド」用のマクロ定義コマンドを送ってきます。

マクロ定義コマンドのフォーマットは次の通りです。

| データサイズ(4バイト)| M | コマンドID(1バイト) | マクロ定義リスト |

「コマンドID」はどのコマンド用のマクロ定義なのかを示すIDです。前述のコマンドIDと同じ値です。例えば、HELOコマンド用のマクロ定義コマンドなら「H」です。

「マクロ定義リスト」のフォーマットは次の通りです。

| キー1(NULL終端文字列) | 値1(NULL終端文字列) | キー2(NULL終端文字列) | 値2(NULL終端文字列) | ... |

キーは「{...}」というように「{」と「}」で囲まれている場合もあれば囲まれていない場合もあります。

Postfixが送るマクロ定義はmilter_connect_macrosなどで指定します。

ネゴシエーションコマンド

ネゴシエーションコマンドはかなり特殊なコマンドです。セッション開始時にMTAからmilterに1度だけ送ります。

ネゴシエーションコマンドでMTAとmilter間で次のことを決めます。

  • milterプロトコルのバージョン
  • アクション(milterがどんな操作をするか)
  • ステップ(MTAとmilter間のやりとりについて)

ネゴシエーションコマンドでは次のようにMTAとmilterで送りあうデータのフォーマットは同じです。

MTA ー ネゴシエーションコマンド → milter
    ← ネゴシエーションコマンド ー

MTAからmilterに送るときはMTAがサポートしている機能の情報を送り、milterがMTAに応答するときはmilterが要求する機能の情報を送ります。MTAがサポートしていない機能をmilterが要求するとネゴシエーションは失敗し、セッションは確立しません。

データのフォーマットは次の通りです。

| データサイズ(4バイト)| O | バージョン(4バイト) | アクション(4バイト) | ステップ(4バイト) | マクロリスト(0バイト以上) |

「バージョン」、「アクション」、「ステップ」はすべて4バイトの符号なし整数で、ネットワークバイトオーダーです。アクション、ステップは各ビットに意味を割り当てています。(フラグになっているということです。)

「バージョン」はMTAが提示したバージョンよりも低いバージョンをmilterが指定しても構いません。例えば、Postfixは「6」を提示し、milterが「2」を返してもよいです*5

「アクション」のフラグは次の通りです。最初の行が凡例です。

  • #{値}: #{説明}
  • 1 << 0: ヘッダーを追加できる
  • 1 << 1: 本文を変更できる
  • 1 << 2: 宛先を追加できる
  • 1 << 3: 宛先を削除できる
  • 1 << 4: ヘッダーを変更できる
  • 1 << 5: 隔離(配送せずにholdキューに入れる)できる
  • 1 << 6: 差出人を変更できる
  • 1 << 7: パラメーター付きで宛先を追加できる(RCPT TO:<forward-path> [ SP <rcpt-parameters> ] <CRLF>の<rcpt-parameters>を使うかどうか)
  • 1 << 8: milterがマクロ定義を上書きできるか(詳細は後述するマクロリストを参照)

「ステップ」のフラグは次の通りです。最初の行が凡例です。

  • #{値}: #{説明}
  • 1 << 0: MTAが接続コマンドを送らない
  • 1 << 1: MTAがHELOコマンドを送らない
  • 1 << 2: MTAがMAIL FROMコマンドを送らない
  • 1 << 3: MTAがRCPT TOコマンドを送らない
  • 1 << 4: MTAが本文チャンクコマンドを送らない
  • 1 << 5: MTAがヘッダーコマンドを送らない
  • 1 << 6: MTAがヘッダー終了コマンドを送らない
  • 1 << 7: milterがヘッダーコマンドに応答しない
  • 1 << 8: MTAが未知コマンドを送らない
  • 1 << 9: MTAがDATAコマンドを送らない
  • 1 << 10: MTAがスキップ応答(後述)をサポートしているかどうか
  • 1 << 11: MTAが拒否した宛先もmilterに送るかどうか
  • 1 << 12: milterが接続コマンドに応答しない
  • 1 << 13: milterがHELOコマンドに応答しない
  • 1 << 14: milterがMAIL FROMコマンドに応答しない
  • 1 << 15: milterがRCPT TOコマンドに応答しない
  • 1 << 16: milterがDATAコマンドに応答しない
  • 1 << 17: milterが未知コマンドに応答しない
  • 1 << 18: milterがヘッダー終了コマンドに応答しない
  • 1 << 19: milterが本文チャンクコマンドに応答しない
  • 1 << 20: MTAがヘッダーの値の先頭の空白を削除しない。「Subject: xxx」とあった場合、先頭の空白を削除しないで「 xxx」をmilterに送るということ。このフラグを落とすと先頭の空白を削除して「xxx」をmilterに送る。

「MTAが○○コマンドを送らない」について補足します。このフラグを使うとmilterが必要のないコマンドを送ってこないようにMTAに指示することができます。例えば、メール本文が必要ない場合は本文チャンクコマンドを送らないようにすることで、通信量が減りパフォーマンスがあがります。

同様に「milterが○○コマンドに応答しない」について補足します。後述しますが、応答時にはmilterは「このメールを拒否する」、「このメールは受け取る」などをMTAに伝えることができます。milterが特定のコマンドに対して必ず「次の処理にいってくれ」と応答することが事前にわかっている場合は、MTAはmilterの応答を待たずに次の処理にいきます。これもパフォーマンス向上につながります。

「マクロリスト」はユーザーがmilter_connect_macrosなどで指定した値をmilterが上書きするためにあります*6。指定した場合は次のようなフォーマットになります。

| マクロの種類(4バイト) | 空白またはコンマ区切りのマクロ名のリスト(NULL終端の文字列) |

「マクロの種類」は4バイトの符号なし整数で次の値のどれかです。最初の行が凡例です。

  • #{値}: #{説明}
  • 0: CONNECTコマンド用マクロ
  • 1: HELOコマンド用マクロ
  • 2: MAIL FROMコマンド用マクロ
  • 3: RCPT TOコマンド用マクロ
  • 4: DATAコマンド用マクロ
  • 5: メッセージ終了コマンド用マクロ
  • 6: ヘッダー終了コマンド用マクロ

マクロ定義コマンドでは「コマンドID」を使っていましたが、ここでは独自の値になることに注意してください。

他のコマンド

2014/12/10中ではまとめきれなかったので他のコマンドは省略します。ごめんなさい。

応答の種類

次は個々のやりとりのうち「milterから送るデータ」について説明します。

milterから送るデータを応答と呼びます。

応答のID、名前、説明は次の通りです。最初の行が凡例です。

  • #{ID}: #{名前}: #{説明}
  • +: 宛先追加: 宛先を追加する応答。複数の宛先を追加するときは複数回送る。
  • -: 宛先削除: 宛先を削除する応答。「何番目の宛先」という形式で指定する。
  • 2: パラメーター付きで宛先追加: <rcpt-parameters>付きで宛先を追加する。
  • a: 受理: このメールは受理するという応答。milterプロトコルでのメールトランザクションの処理はここで終了し、これ以降MTAからコマンドは送られてこない。
  • b: 本文置換: 本文を変更する応答。変更後の本文が大きい場合は複数のチャンクに分けて応答する。
  • c: 継続: 次のコマンドへいってくれという応答。基本はこれを返す。
  • d: 破棄: 処理中のメッセージを破棄する。SMTPでのメールトランザクションの処理はここで終了するので、milterプロトコルでのメールトランザクションも終了する。
  • e: 差出人変更: 差出人を変更する応答。
  • h: ヘッダー追加: ヘッダーを追加する応答。複数のヘッダーを追加するときは複数回送る。ヘッダーは末尾に追加される*7
  • i: ヘッダー挿入: 指定した位置にヘッダーを挿入する応答。
  • m: ヘッダー変更: 指定した位置のヘッダーを変更する。
  • p: 処理中: milterの処理に時間がかかっていることをMTAに知らせる応答。MTA側のタイムアウト時間を伸ばせる。
  • q: 隔離: このメールを隔離するという応答。
  • r: 拒否: このメールの受信を拒否するという応答。SMTPでは5XX系のレスポンスになる。
  • s: スキップ: このメールトランザクションの処理を途中でやめるという応答。
  • t: 一時拒否: このメールの受信を一時的に拒否するという応答。SMTPでは4XX系のレスポンスになる。
  • y: SMTPレスポンス設定: SMTPレスポンスのコードとメッセージを設定する。

「宛先追加」や「ヘッダー変更」などメッセージ本体を変更する応答は「メッセージ終了コマンド」の応答のときでないと返せないことに注意してください。

2014/12/10中ではまとめきれなかったので応答のデータフォーマットなどの説明は省略します。ごめんなさい。

まとめ

milterプロトコルについてまとめきれませんでした。もしかしたら後で追記するかもしれません。

文章にするのは時間がかかるのですが、口頭で説明するのは文章にするよりも時間がかからないので、興味のある人は、イベントなどでmilter managerの作者にばったり会ったときにでも聞いてください。

(途中ですが)milterプロトコルの説明を見るとmilterではいろんなことができることがわかります。milterを使うとPostfixの設定だけではできないようなことも実現できます。Postfixの設定だけでは実現できないなぁと思ったときは、milterも組み合わせることも検討してみてください。実際、クリアコードではRubyで小さなmilter(100行以内のもの)を書いて、Postfixの設定だけでは実現できない(実現しようとすると複雑になる)メールシステムの構築をお手伝いしていたりします*8。困ったときは、公開できる情報ならmilter managerのメーリングリスト、有償でもよいならお問い合わせフォームを使って相談してみてください。

*1 libmilterのバインディング開発者にはそんなに有用ではないが、自分でmilterプロトコルを実装する開発者には有用。

*2 オープンソースでよかったですね!

*3 たぶん。少なくともmilter managerのmilterプロトコル実装はそう。

*4 milter managerを実装しているときはこの情報を知らなかったので一から調べました。

*5 以前のPostfixはダメでしたが、Postfixにパッチを送って取り込んでもらったので、今はできるようになっています。

*6 こんな機能あったのか。。。milter managerでは実装していないな。。。

*7 MTAの実装依存かも。

*8 100行以上になるような込み入った機能を実現するmilterをRubyで開発することもあります。

2014-12-10

schooでリーダブルコードのWeb授業を開催

1週間ほど前になりますが、schoo(スクー)でリーダブルコードのWeb授業「名著『リーダブルコード - より良いコードを書くためのシンプルで実践的なテクニック』を解説者と一緒に読み解こう」の先生をしました。

事前に「受けたい」としていた人は1751人で、当日生授業に参加できたのは696人だったそうです。参加された方は有意義に学ぶことができたでしょうか。残念ながら当日参加できなかった方は、schooの生徒になると録画を観ることができます。気になる方は前述のschooの授業ページを確認してください。

内容

schooで授業をしたのははじめてだったのですが、みなさん積極的にコメントを書き込んでくれたので「Webを使った生授業での双方向のやりとり」を実感できました。コメントの流れが早く、リアルタイムでは確認できないコメントの方が多かったですが、後で確認しました。書き込んでくれていた中には見知った方もちらほら混ざっていてほっこりしました。ありがとうございます。

参加者の知識や経験にけっこう幅があったようでした。そのため、込み入った内容を扱う場合は進め方が難しそうです。(今回は本の一部をベースにしたのでそのあたりの難しさはあまりなかったです。)

当日使った資料は次の通りです。PDFはリンク先からダウンロードできます*1

資料を使った説明の後は質疑応答の時間がありました。1つだけ質問と回答を紹介します。この質問はリーダブルコード関連の講演をするとよく聞かれる質問です。

質問:おすすめのソース、勉強したソース等はありますか?

回答:自分が使っているオープンソースソフトウェアのソース*2がおすすめです。

講演では「リーダブルコード(読む人が理解しやすいコード)を書くためには読む人としての経験がとても役に立つ」ということを話しています。そのため、どのソースを読んで「読む人の経験」を養えばよいか気になるのは当然の流れです。

質問する人は、おそらく、「講師も読んでいる○○のソースを読んで自分も経験を養おう」と考えていて、「○○のソースを読むとよいですよ」という回答を期待していることでしょう。しかし、おすすめは前述の通り「自分が使っているオープンソースソフトウェアのソース」です。

どうして自分が使っているソフトウェアを読むとよいのかというと、使い方やどう動くかがわかっているからです。動きがわかっていると、動きを対応させてソースを読むことができるので理解しやすくなります。読む人の経験を養えるだけでなく、実はこんな使い方もできたのか、こんな機能もあったのかと新しい発見もあるかもしれません。自分が使っているソフトウェアのソースだと楽しくソースを読めるはずです*3

ぜひ、「他の人が読んでいるソースを読んでみよう」ではなく、「自分が使っているソフトウェアのソースを読んでみよう*4」という観点でソースを選んで読んでみてください。

続編

今回の授業は単発の企画なので続編の予定はありません。「解説」の著者とリーダブルコードについて考えてみたいという方は個別にご連絡ください。

*1 schooのサイトでは資料はダウンロードできないと書いていますが、リンク先からダウンロードするのはOKです。資料のライセンスをCreative Commonsにすることは調整済みです。

*2 残念ながら1つもオープンソースソフトウェアを使っていない場合は社内のみで使われているソフトウェアのソースでもよいです。

*3 ソースを読むことはとても楽しいことのはずです!

*4 使っているソフトウェアが、「ユーザーがプログラムがどのように動作しているか研究する自由」を保証しているソフトウェアならステキですね!

2014-12-15

Sphinx 1.3で使えるgettextとバージョン管理の相性の悪さを改善する仕組み

ドキュメントを作りたくなってしまう魔法のツールとしてSphinxがあります。 全文検索エンジンGroongaでは、Sphinxを英語と日本語のドキュメントの生成に利用しています。

しかし、バージョン管理する上でいくつか課題がありました。そのあたりの事情に触れた、gettextとバージョン管理システムの相性の悪さを解消する案という記事があります。*1

今回はそのときの案を引用しつつ、実際どのように解決したのかをGroongaの例で紹介します。*2

どう相性が悪いのか?

当時の記事では相性の悪さとして次の点をあげていました。

  • バージョン管理対象の.poファイルに出現位置情報が含まれているため、出現位置情報もバージョン管理せざるを得ない
  • 出現位置情報もバージョン管理するとノイズが大きい

上記を解決する案として以下を紹介しました。

  • バージョン管理しない作業用の.poファイルを導入する
  • 作業用の.poファイルには出現位置情報を入れるため翻訳時の利便性は維持
  • 作業用の.poファイルから出現位置情報を抜いた.poファイルを作り、それをバージョン管理対象とする

Groongaのドキュメントでpoファイルを更新するときには変更行が2000行くらいで、しかもその大部分がドキュメントにおける出現位置情報だったので、このやりかたで一見すべてうまくいくように思えました。

しかし、それでもまだノイズが残っていることに気づきました。

1
2
3
# 161fad58cef448c698df3d188d57135d
msgid "13.10 Saucy Salamander"
msgstr ""

上記の 161fad58cef448c698df3d188d57135d ですが、これはSphinxが生成する uuid です。

メッセージの出現位置情報については、GNU gettextでは --no-location オプションがあるので除去できます。しかし uuid についてはSphinxが独自に生成しているためどうすることもできません。

どう解決したのか?

生成されたpoから uuid を除去する仕組みをGroongaのドキュメント生成に組みこむことも考えましたが、やはりSphinx本体で uuid を生成しないという選択肢が選べるならそれにこしたことはありません。Groonga特有の問題ではなく、広く一般に使えるものだからです。

そこで Sphinx自体を修正し uuid の出力を制御可能にするオプションを追加しました。この変更はすでに Sphinx本体にとりこんで もらいました。

Sphinx 1.3b1以降で使えるようになります。

uuidを無効にするには

Sphinx 本体にとりこんでもらった時点では、従来との互換性から uuid の生成をデフォルトでは有効なままにしていました。

しかし、その後 デフォルトで出力しないように既定値がFalseへと変更されたようです。そのため、何もしなくても以下のように config.py に書いたのと同じことになります。

1
gettext_uuid=False

まとめ

今回はGNU gettextとバージョン管理の相性の悪さを、Sphinx本体を修正することで解決した事例を紹介しました。 使っているフリーソフトウェアの「こうだったらもっといいのに」があれば、開発元にフィードバックして、なおしてもらえるといいですよね。

*1 実は一年以上前の記事。

*2 解決してから結構経過しているが、そういえばククログに書いていなかったのを思いだしたので今年のうちに記事にした。

2014-12-16

Clangの静的解析を使ってコードの潜在的な問題点を探すには

はじめに

LLVMをベースにしたC/C++、Objective-C/C++のコンパイラフロントエンドとしてClangがあります。 Clangプロジェクトでは、コンパイラだけでなく、静的解析ツールも開発しています。

今回は、その静的解析ツールである、scan-buildについて紹介します。

scan-buildをインストールしよう

以下にUbuntu 14.04 LTS(Trusty)でのインストール手順を示します。Trustyでは、clang-3.3、clang-3.4もしくはclang-3.5をインストールできます。 scan-buildはclangパッケージに含まれているので、今回は新しめのバージョンであるclang-3.5をインストールすることにします。*1

% sudo apt-get install clang-3.5

scan-buildを使ってみよう

scan-buildをインストールできたので、実際に試してみましょう。例として使うのは、ククログで何度か登場しているのでおなじみカラムストア機能つき全文検索エンジンGroongaのコードです。

まずは、ソースコードをcloneして、configureファイルを生成します。

% git clone https://github.com/groonga/groonga.git
% cd groonga
% ./autogen.sh

configureファイルが生成されたら、次はconfigureでclangを使うようにします。

% ./configure CC=clang CXX=clang++

では、scan-buildにかけてみましょう。 configureを実行するとMakefileができるので、あとはscan-buildを単に実行するだけです。

% scan-build make

検出された問題点

scan-buildの実行が完了すると、次のようなサマリが最後に出力されます。

scan-build: 216 bugs found.
scan-build: Run 'scan-view /tmp/scan-build-2014-12-22-164614-28182-1' to examine bug reports.

どんな問題が実際に検出されたのか、確認してみましょう。 scan-buildが最後に出力したコマンドの例を実行します。

% scan-view /tmp/scan-build-2014-12-22-164614-28182-1

すると、自動的にブラウザが起動してレポートを表示します。

Bug Summaryのスクリーンショット

Bug Summaryをみると次のような問題が検出されていることがわかります。

  • NULLポインタの参照(の可能性)
  • 代入しているけど使っていない
  • NULLが期待されていない箇所でNULLが渡された

中には可能性があると指摘されているものの、実際には問題ないというケースもあります。そのあたりは確認が必要です。

Reports(問題の詳細)のスクリーンショット

検出された個別の問題については、Reportsのリンクをたどると該当箇所のコードも表示してくれます。

例えば、無駄な代入を行っている箇所として次のような箇所が検出されました。

検出された問題点に対応するコードのスクリーンショット

みるとわかるのですが、whileループの先頭で変数cに先頭の文字を毎回設定しています。しかし、case文の条件であるGRN_LOADER_BEGINでは、スペースを読みとばしたときには、変数cに先頭の文字を設定してからcontinueで抜けています。 つまり、その場合には

1
c = *str;

が重複して実行されています。これは明らかな無駄ですね。

まとめ

今回は、Clangの静的解析ツールを使ってコードの潜在的な問題点や使われていないコードを検出する方法を紹介しました。 scan-buildをうまく使って、既存のコードにつもった埃をキレイにしてみるといいかも知れませんね。

*1 sudo apt-get install clangで標準インストールされるのはclang-3.4です。

2014-12-22

Firefox OSのサポートサービス開始のお知らせ

クリアコードはFirefox OSのサポートサービスを開始しました。すでに2014年12月23日にMozilla JapanからFirefox OSとそのエコシステムに期待される企業のご紹介において、アプリの開発・最適化・Firefox OSの移植といったFirefox OSに関するサービスを提供する企業として、クリアコードを紹介して頂いています。

Mozilla製品のサポートの実績と特徴

クリアコードでは、創業以来Mozilla Japanのサポートパートナーとして、企業、官公庁へのFirefox、Thunderbirdの導入支援や、組み込みシステムへのFirefox導入支援を行ってきました。

クリアコードにおけるMozilla製品のサポートの特徴は次の2点にあります。

  • アドオンやドキュメントの公開
  • ソースコードレベルのサポート、Mozilla Japanとの連携
アドオンやドキュメントの公開

クリアコードではソフトウェア、ドキュメント、企業導入のノウハウを出来る限り公開することによって、より多くのユーザがFirefox/Thunderbirdを利用できるようにしています。Firefox/Thunderbirdの導入支援ツールやアドオンもフリーソフトウェアとして公開しており、無償で利用できます。

ソースコードレベルのサポート、Mozilla Japanとの連携

サポートでは問題の原因究明のため、ソースコードの中身まで調査することが可能です。また問題の原因がFirefox/Thunderbird本体あった場合、回避策としてアドオンを提供するとともに、Mozilla開発チームにそのバグを報告します。またMozilla Japanと連携し障害解析、問題解決する体制も確保しています。

Firefox OSのサポートサービス

クリアコードでは、2010年以降、組み込みシステムの開発に力を入れており、Webブラウザベースのデジタルサイネージ製品の開発にも携わっています。そのためFirefox OSはMozillaサポートや、組み込みシステム開発で培った技術力を発揮するのに適した環境にあります。

クリアコードでは、Firefox OS 向けアプリケーションの開発、Firefox OS の各種端末へのポーティング/チューニングに関する相談を承っています。お問い合わせフォームからお問い合わせください。ご不明な点などありましたら、お気軽にご連絡ください。

タグ: Mozilla
2014-12-25

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|