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

ククログ


代表取締役の須藤がApache Arrowの開発でGoogle Open Source Peer Bonusを受賞

Apache Arrowの開発に参加している代表取締役の須藤です。

もう2ヶ月前の話なのですが、2019年のGoogle Open Source Peer Bonusの1人に選ばれました。選ばれるまでこんな取り組みがあることを知らなかったのですが、2011年からやっているそうです。オープンソースの開発に関わっている人を支援する取り組みだそうです。賞金(?)として250ドルもらいました。ありがとうございます。

Apache Arrowの開発をがんばっているということで選んでもらいました。現時点で私は2番目にコミット数が多いのでそのあたりを評価してくれたのかもしれません。ちなみに、1番コミット数が多いWesさんはpandasの開発で選ばれていました。

私の自由なソフトウェアの開発を支援したいけどどう支援したらよいかわからないという方はご相談ください。

2019-06-17

Firefox 67以降のユーザープロファイルの仕様の詳細

Firefox 67の新機能の1つに、インストール先ごとにプロファイルが分けられるようになったという物があります。

この記事では、Firefoxのユーザープロファイルの仕組みの歴史的な経緯や、プロファイル切り替えの仕組み自体がどのように使われてきたかという点を踏まえながら、Firefox 67での新機能の性質を解説します。

Firefoxの「ユーザープロファイル」

Firefoxのユーザープロファイルの仕組みは、元を辿ると、その前身となったNetscape Communicatorのユーザープロファイルの仕組みに行き着きます。

Netscape Communicatorが現役だった頃に広く使われていたWindows 95やWindows 98は、原則としてユーザーは1人だけであるという前提で設計されていたため、複数人で同じPCを共用する場合にユーザーを使い分けるという事ができませんでした。そのためNetscape Communicator独自の概念として「使用者ごとにユーザープロファイルを作り分けて、設定値やブックマークなどはその中で管理する」という仕組みが用意されました。

(図:Netscape Communicatorのユーザープロファイルをユーザーごとに使い分ける様子)

その一方で、Windows XP以降のバージョン*1ではOS自体が複数ユーザーを想定した設計になっています。アプリケーション側で個別にユーザープロファイルを管理する必要は無く、アプリケーションの設定を使用者ごとに使い分けたい場合、単純にWindowsのユーザーを使い分ければよいという事になっています。

(図:Windowsのプロファイルをユーザーごとに使い分ける様子)

以上のような歴史的な経緯があり、Firefoxのユーザープロファイルは「Windowsのユーザープロファイルごとに、複数のFirefoxのプロファイルが存在しうる」という少々複雑な状況となっています。

(図:Windowsのプロファイル上に複数のFirefoxのプロファイルが存在する様子)

実際には、Windowsのユーザーを使い分ければよい現状においては、使用者ごとにFirefoxのプロファイルを使い分けないといけないという状況はまず発生しません。よって、Firefoxのユーザープロファイルは今では1人のユーザーが、「仕事用」「開発用」「趣味用」といった何らかの用途ごとに使い分けるという用途が主流になっています。

(図:Windowsのプロファイル上で、用途ごとにFirefoxのプロファイルが使い分けられている様子)

「ユーザープロファイルの使い分け」から「コンテキストやコンテナーの使い分け」への変化

ただ、一般的なユーザーが普段使いにおいてFirefoxのユーザープロファイルを使い分けるということは、実際にはあまり無いものと思われます。その最大の理由は、運用の面倒さです。

Firefoxのユーザープロファイルを使い分けるには、

  • Firefoxの起動オプションに-pオプションを指定してプロファイルマネージャを起動し、そこでプロファイルを管理する。
  • about:profilesでプロファイルを管理する。

このいずれかの方法でプロファイルを作成し、さらに、

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p private(個人的な使用)
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p work(仕事での使用)
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p test(検証用)

のように起動オプションを変えたショートカットを複数用意する必要があります。

また、これだけでは「個人用のFirefox」「仕事用のFirefox」を並行して起動することができません。というのも、Firefoxのプロセスが既に起動している場合、別のプロセスで新たにFirefoxを起動しようとすると、既に起動している方のプロセスの方にプロセスが吸収されてしまう(そちらでウィンドウやタブが開かれる)という結果になるからです。これを回避するためには、

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p private
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p work -no-remote
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p test -no-remote

のように、普段使いの(URLなどの関連付けからFirefoxが起動された時に使う)プロファイルを除いて他のショートカットには-no-remoteオプションを指定し、前述の「既存プロセスに吸収される」挙動を無効化する必要があります。

また別の方法として、プロファイルマネージャを使用せずに以下のようにする方法もあります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "C:\Users\username\private-profile" -no-remote

-profileオプションで任意のフォルダをフルパスで指定すると、プロファイルマネージャの管理下にないフォルダであっても、それをプロファイルとしてFirefoxを起動できます。実験や検証のためのプロファイルをいくつも併用したり使い捨てにしたりという使い方をしたい場合、プロファイルマネージャを使わずともフォルダを用意するだけで済むため、この方法は非常に便利です。ただしこの場合も、-no-remoteを付けないと「既に起動している方のプロセスの方にプロセスが吸収される」という動作になってしまう点には注意が必要です。

これだけ面倒な手順が必要なわりに、一般的なユーザーが普段使いで得られるユーザー体験はあまり良い物ではありません。というのも、実際の利用局面では「仕事中に見つけた面白そうな記事を個人用にブックマークしておく」「個人的な使用中に見つけた仕事に関係する記事を、仕事中に履歴から探す」といった場面が生じやすいのに対し、履歴やブックマークなどがプロファイルごとに別々に保存されていると、そういった「用途をまたいだ情報の共有」ができないからです。

この問題の解決策として現在のFirefoxには、「1つのユーザープロファイルで、閲覧履歴やブックマークなどは共有しつつ、起動中の1つのプロセスの中でログイン状態やCookieなどの認証情報は分離する」という事を可能にする、以下の機能が備わっています。

  • プライベートウィンドウ(そのウィンドウの中で閲覧したページには、通常モードで閲覧した時のCookieなどが通知されない)
  • コンテナータブ(用途ごとに認証情報を切り替える)

これらの機能は、「諸々の不便を被ってでも厳密にプロファイルを分けたい程ではない」「しかし、ログイン状態やCookieなどの認証情報は用途ごとに切り替えたい」という、実際の使用局面でよく見られるニーズに応えるものとなっています。

(図:コンテナーを使い分けている様子)

ユーザープロファイルの使い分けよりもはるかに手軽に使えるため、これらの機能で充分に用を果たせる場面では、ユーザープロファイルの使い分けのニーズはほぼ無くなったと言ってよいでしょう。

それでも残った「ユーザープロファイルを使い分ける必要がある場面」

以上のような経緯から、現在のFirefoxで「ユーザープロファイルを使い分けなければその目的を達成できない」場面は非常に限られています。その代表的な例が、複数バージョンのFirefoxの並行起動です。

(図:複数バージョンのFirefoxを専用プロファイルで使い分けている様子)

例えば、前述の手順を用いて複数バージョンのFirefoxを並行起動する場合、以下のように起動オプションでプロファイルを指定する事になります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p default(64bit版Firefox)
  • "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" -p 32bit -no-remote(32bit版Firefox)
  • "C:\Program Files\Mozilla Firefox Beta\firefox.exe" -p beta -no-remote(ベータ版)

Firefox 67からの変更は、まさにこの用途のためのものです。言い換えると、Firefox 67以降のバージョンでは、「インストール先が異なるFirefoxの実行ファイルごとに、専用のユーザープロファイルを使用する」使い方が、特にユーザー側での工夫無しに自動的に行われるようになっているという事になります。

Firefoxが自身のインストール先を識別する処理の流れ

では、このような挙動は一体どのようにして実現されているのでしょうか。特に、「特定のインストール先のFirefox」と「特定のユーザープロファイル」とはどのように紐付けられているのでしょうか。鍵になるのは、インストール先のパスに基づくCity Hash値です。

Firefox 67以降のバージョンでは、Firefoxはプロセスの起動時に、自身の実行ファイルが置かれたフォルダの位置からCity Hashのアルゴリズムに基づいてハッシュ値を求めます。例えばFirefoxがC:\Program Files\Mozilla Firefoxにインストールされていた場合、値は308046B0AF4A39CBのようになります。内部的にはこれは「install hash」と名付けられている模様です。

次にFirefoxは、ユーザープロファイルが置かれている%AppData%\Mozilla\Firefox(具体的には C:\Users\ユーザー名\Appdata\Roaming\Mozilla\Firefox)の位置にあるprofiles.iniから[Install(install hash)]という名前のセクションを探します。これは通常、以下のような内容になっています*2

; こちらは従来からあるセクション
[Profile0]
Name=default
IsRelative=1
Path=Profiles/xxxxx.default

; 自動的に追加されたセクション
[Install308046B0AF4A39CB]
Default=Profiles/xxxxx.default
Locked=1

このDefaultの値がprofiles.iniから見た相対パスとなっており、このパスを解決することで、特定のインストール先のFirefoxに対して紐付けられたプロファイルフォルダを特定できるというわけです。この仕組みがあるお陰で、関連付け等からFirefoxが起動された場合などで起動オプションでプロファイルが明示されていなくても、適切なプロファイルが自動的に選択されるようになっています。

ただし、同じインストール先であっても、起動時のパスの指定の仕方によってはプロファイルが意図せず切り替わってしまう場合があります(別記事にて詳細な解説)。Firefoxのパスを明示的に指定する場面では、必ず正式なパス表記を使うように注意して下さい。

なお、この機能について「Firefoxのバージョンごとにプロファイルが別れる」というような説明がなされている場合がありますが、以上の仕組みを見て分かる通り、プロファイルはあくまでFirefoxのインストール先のパスに紐付いています。インストール済みのFirefoxに別のバージョンのFirefoxを上書きインストールしたり、Firefox自身の自動更新機能で新しいバージョンに更新したりした場合であっても、Firefoxのインストール先が変わらなければ、プロファイルは以前の物が引き続き使われます。「Firefoxを更新したらその度に新しいプロファイルが作られる」という事はありません。(逆に言うと、この機能を使ってプロファイルを使い分けるためには、必ずそれぞれのバージョンのFirefoxを別々の位置にインストールする必要があります。)

Windowsのレジストリ上での取り扱い

上記のinstall hashはWindowsのレジストリ上でも使われています。

従来は、Windowsのレジストリ上はFirefoxはあくまで「Firefox」という名前の1つのアプリケーションとして扱われており、複数のバージョンを異なるフォルダにインストールした場合は最後にインストールした物(最後にインストーラがレジストリに書き込みを行ったバージョン)がその代表として扱われるようになっていました。例えばシステム標準のブラウザの選択肢の項目は

  • HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet\Firefox
  • HKEY_CURRENT_USER\Software\Clients\StartMenuInternet\Firefox

とその配下に記録された情報に基づいて表示されていました。

Firefox 67以降のバージョンでは、この情報がインストール先ごとに書き込まれるようになっています。

  • HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet\Firefox-(install hash)
  • HKEY_CURRENT_USER\Software\Clients\StartMenuInternet\Firefox-(install hash)

同様に、関連付け対象のファイルやデータの型も

  • HKEY_CLASSES_ROOT\FirefoxHTML-(install hash)
  • HKEY_CLASSES_ROOT\FirefoxURL-(install hash)

のようにそれぞれインストール先ごとに登録されるようになりました。このように、Windowsのレジストリ上はインストール先ごとのFirefoxはそれぞれ別々のアプリケーションとして認識されています

アプリケーションの一覧上でそれぞれのインストール先を識別できない問題の回避策

以上の通り、Windowsのシステム上は各インストール先のFirefoxは別々の物として認識されているわけですが、ここで1つ問題があります。内部的にはそれぞれ別々のinstall hashを伴って認識されているものの、表示されるアプリケーション名としてはどれも「Firefox」となるため、標準のブラウザや関連付けの対象を選択する場面で同じ名前の項目がいくつも並んでしまうという事が起こってしまうのです。

(複数のバージョンのFirefoxが同じ名前で並んでいる様子)

現在の所、この問題を解決するには手動でのレジストリの編集が必要です。この一覧に表示されるアプリケーション名は HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\Shell\MuiCache というキーの値が参照されており、

  • C:\Program Files\Mozilla Firefox\firefox.exe.FriendlyAppName のデータ→Firefox Release
  • C:\Program Files\Mozilla Firefox ESR\firefox.exe.FriendlyAppName のデータ→Firefox ESR

のように実行ファイルのパス.FriendlyAppNameという名前の値のデータをそれぞれ編集しておくと、インストール先ごとの項目が分かりやすい名前で表示されるようになります。

(複数のバージョンのFirefoxにそれぞれ別の名前が付いている様子)

起動オプションの作用はどう変わったか

上記の通り、Firefox 67以降のバージョンは自分自身のインストール先に基づいて適切なプロファイルを自動的に選択するようになったわけですが、では、今まで使われていた「起動オプションでプロファイルを明示する」機能はどのような影響を受けたのでしょうか。

-p プロファイル名

前述した通り、Firefox 67以降のバージョンでは%AppData%\Mozilla\Firefox\profiles.iniに以下のように[Install(install hash)]という名前のセクションが追加されます。

; 従来からあるセクション
[Profile0]
Name=default
IsRelative=1
Path=Profiles/xxxxx.default

; 自動的に追加されたセクション
[Install308046B0AF4A39CB]
Default=Profiles/xxxxx.default
Locked=1

この時、2つのセクションで同じパスが参照されている点に注目して下さい。Firefoxは参照先のパスが一致する2つのセクションを関連付けて認識しています。そのため、以下の2つのコマンド列は同等として扱われます

  • "C:\Program Files\Mozilla Firefox\firefox.exe" (install hashに基づいて自動的にプロファイルを選択)
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p default-pオプションでプロファイルを明示)

引数無しで起動したFirefoxのプロセスがある状態でプロファイルを明示してFirefoxを起動しても、逆に、プロファイルを明示して起動したプロセスがある状態で引数無しでFirefoxを起動しても、既に起動している方のプロセスに処理が吸収される(既存プロセスの方でウィンドウまたはタブが開かれる)という、従来の-no-remoteオプション無しの動作と似た結果になります。

同じバージョン・同じインストール先のFirefoxを、別プロセス・別プロファイルで起動する

別のプロファイルを指定して別のプロセスを並行動作させたい場合、従来は-p 別のプロファイル名と指定するだけでは不十分で、-p 別のプロファイル名 -no-remoteと指定する必要がありました。Firefox 67以降のバージョンではこの点が改善され、-p 別のプロファイル名と指定するだけで別プロセスのFirefoxを並行動作させられるようになりました

従来は-no-remoteオプションが必須だったため、必然的に、これらのオプションを指定したショートカットからFirefoxを多重起動しようとすると、「既存プロセスに処理が吸収されない」「しかし、新しいプロセスで使おうとしているプロファイルは、別のプロセスによって既に使用中」ということで、「Firefoxは起動していますが応答しません。新しいウィンドウを開くには既存のFirefoxをプロセスを終了させなければなりません。」というメッセージが表示されてしまうという結果になっていました。Firefox 67以降では、この制限に煩わされる事はもうありません。

-profile プロファイルのフルパス

プロファイルをフルパスで指定して起動する場合も同様に、-no-remoteオプション無しでの別プロセス起動が可能となりました。以下のように起動オプションを指定すると、それぞれのプロファイルでFirefoxのプロセスが並行して動作する状態になります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "C:\Users\username\profile1"
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "C:\Users\username\profile2"

この場合、同じ起動オプションでFirefoxを多重起動すると、既に起動していてそのプロファイルを使用しているプロセスに処理が吸収されます。

また、条件を整えると、「起動オプションでプロファイル名を明示する場合」と「フルパスで明示する場合」を等価に扱うことや、あるいは、「プロファイル名を明示」「フルパスで明示」「プロファイルを明示しない」の3つを等価に扱うという事も可能になっています。

重要なのは以下の点です。

  • 当該プロファイルがプロファイルマネージャの管理下に入っている(profiles.iniにそのフォルダの情報が記載されている)。
  • 当該プロファイルのプロファイルマネージャ上の名前と、フォルダ名とが一致している。

以下は、profiles.iniを編集して上記の条件を満たすように設定したプロファイルの例です。

[Profile0]
;Name=default
Name=xxxxx.default
IsRelative=1
Path=Profiles/xxxxx.default

Firefoxのプロファイルマネージャはプロファイルを作成する際、表示用の名前の前にランダムな文字列を付与してフォルダを作成します*3この表示名が記載されているName=の行を編集し、表示名がプロファイルのフォルダ名と一致する状態にします。この状態であれば、以下の2つのコマンド列は等価に扱われるようになります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "%AppData%\Mozilla\Firefox\Profiles\xxxxx.default"
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p xxxxx.default

また、install hashとの紐付けが

[Profile0]
Name=xxxxx.default
IsRelative=1
Path=Profiles/xxxxx.default

[Install308046B0AF4A39CB]
Default=Profiles/xxxxx.default
Locked=1

のように行われていれば、以下の3パターンがすべて等価に扱われるようになります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "%AppData%\Mozilla\Firefox\Profiles\xxxxx.default"
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p xxxxx.default
  • "C:\Program Files\Mozilla Firefox\firefox.exe"

ただ、前述した通り、Firefox 67以降での新機能の恩恵を受けるためには、プロファイルをプロファイルマネージャの管理下に置く必要がある、という点に注意が必要です。「フルパスでプロファイルを明示したい場面」と「プロファイル指定無しでプロファイルを自動認識させたい場面」が重なる事はあまりありませんが、この両方のニーズを同時に満たしたいという場合*4には、見落としが生じないようにくれぐれも気をつけて下さい。

まとめ

以上、Firefox 67以降での「Firefoxのインストール先ごとのユーザープロファイルを使用する挙動」の詳細について解説しました。

Google Chromeが覇権を取った現在においては、Firefoxのニーズは一般ユーザーよりも(おそらくは検証用として)Web制作・Webアプリ開発を業としている人の方に偏っている模様です。「複数バージョンを並行して起動して検証する」という場面で特に有用なこの機能が導入されたのも、そのような背景があったからという事ではないかと考えられます。筆者ほど多くのバージョンを使い分ける事はそうそう無いと思われますが、通常リリース版・ベータ版・ESR版の3つを並行起動できるだけでも検証の負担は下がります。皆さんもこれを機に試してみてはいかがでしょうか。

また、今回の調査は現在Firefox ESR60を運用中のお客様にFirefox ESR68をご案内するにあたって、変更点の詳細を把握するために行いました。当社の法人向けFirefoxサポートでは、詳細な技術情報が提供されていない新機能の詳細の調査も有償にて承っております。「SIの業務において、お客さまの承認を得るために詳細なエビデンスの提出が求められているが、どこを対象に調査すればよいのか分からない」といった問題でお困りの方は、メールフォームよりぜひお問い合わせ下さい。

*1 正確には、Windows 2000やそれ以前のWindows NTなども含みます。

*2 Firefox 67以降のバージョンを初めて起動した時に、このような情報が追記されます。

*3 これは、ユーザープロファイルの位置を決め打ちで特定しにくいようにして、悪意の攻撃者の被害を受けにくくするための工夫です。

*4 例えば、web-ext経由でFirefoxを起動して常用しているというような場合。

タグ: Mozilla
2019-06-14

MXEを使ってUNIX系フリーソフトウェアをGNU/Linux上でWindows向けにクロスビルドする

はじめに

UNIX系のフリーソフトウェアMinGW-w64でビルドすることでWindows上で使えるものも多いですが、Windows上でのMinGW-w64およびMSYS2を使ったビルドは、UNIX系OS上でのセルフビルドと比べて非常に遅いという問題があります。こういった問題はUNIX系OS上でWindows向けにクロスビルドすることで改善することができますが、様々な環境変数やオプションを指定する必要があり、ビルド方法が煩雑になります。また、どちらでビルドするにしても、依存するライブラリについて一つ一つWindows用SDKを用意する必要があり、それらを手動で用意するのも骨が折れます。特に全ての依存ライブラリを自分でビルドしたい場合、ソースコードに手を入れなければならないことも多く、非常に手間がかかります。GNOMEプロジェクトで開発されているJHBuildというビルドシステムを使うことである程度は解決することができますが、Windowsについてはサポートが十分ではなく、素直にビルドが通らないことも多いようです。

これまでは真面目に調べたことがなかったこともあり、なかなか定番と言えるWindows向けクロスビルドシステムが存在しないなと思っていたのですが、最近になってMXE(M cross environment)というクロスビルドシステムの存在を知ったので、紹介致します。

MXEの概要

MXE(M cross environment)はUNIX系OS上からWindowsへのクロスビルドに特化したビルドシステムのようです。このようなビルド環境に特化しているため、サポートされているホストおよびターゲットであれば、ほぼノートラブルでビルドを完了できます。

ビルドシステムはGNU Makefile形式のみで記述されているためシンプルで理解しやすく、新たなパッケージを追加するのも比較的容易です。標準で収録されているソフトウェアはGTKやQt、SDLといったGUI系のものがメインで、非GUI系のものは手薄いという印象です。詳細はMXE: List of Packagesに記載されています。

ビルド環境の準備

MXEを動かすのに必要なソフトウェア一式はMXE: Requirementsに記載されています。

例えばDebianやUbuntuの場合には以下のコマンドで必要なソフトウェア一式をインストールできます。

$ sudo apt install \
    autoconf \
    automake \
    autopoint \
    bash \
    bison \
    bzip2 \
    flex \
    g++ \
    g++-multilib \
    gettext \
    git \
    gperf \
    intltool \
    libc6-dev-i386 \
    libgdk-pixbuf2.0-dev \
    libltdl-dev \
    libssl-dev \
    libtool-bin \
    libxml-parser-perl \
    lzip \
    make \
    openssl \
    p7zip-full \
    patch \
    perl \
    pkg-config \
    python \
    ruby \
    sed \
    unzip \
    wget \
    xz-utils

DebianやUbuntuではMinGW-w64もaptコマンドでインストールできますが、MXEの場合はMXEでビルドしたMinGW-w64が使用されるため、インストールする必要は無いようです。

次に、MXEをcloneします。

$ git clone https://github.com/mxe/mxe.git

cloneしたディレクトリに移動し、以下のコマンドを実行することで、要求されるソフトウェアが揃っているか否かをチェックすることができます。

$ make check-requirements

この際、トップディレクトリにビルド設定が記載された settings.mk というファイルが生成されます。
必要に応じて、ビルド前に編集しておくと良いでしょう。

# This is a template of configuration file for MXE. See
# docs/index.html for more extensive documentations.

# This variable controls the number of compilation processes
# within one package ("intra-package parallelism").
#JOBS := 

# This variable controls where intermediate files are created
# this is necessary when compiling inside a virtualbox shared
# directory. Some commands like strip fail in there with Protocol error
# default is the current directory
#MXE_TMP := /tmp

# This variable controls the targets that will build.
#MXE_TARGETS := i686-w64-mingw32.static i686-w64-mingw32.shared x86_64-w64-mingw32.static x86_64-w64-mingw32.shared

# This variable controls which plugins are in use.
# See plugins/README.md for further information.
#override MXE_PLUGIN_DIRS += plugins/apps plugins/native

# This variable controls the download mirror for SourceForge,
# when it is used. Enabling the value below means auto.
#SOURCEFORGE_MIRROR := downloads.sourceforge.net

# The three lines below makes `make` build these "local
# packages" instead of all packages.
#LOCAL_PKG_LIST := boost curl file flac lzo pthreads vorbis wxwidgets
#.DEFAULT_GOAL  := local-pkg-list
#local-pkg-list: $(LOCAL_PKG_LIST)

例えば、MXE_TARGETSを省略すると、デフォルトはi686-w64-mingw32.staticになるようですので、ターゲットをx86_64にしたい場合や、各種ライブラリを共有ライブラリとしてビルドしたい場合などは事前の設定が必要です。

例:

MXE_TARGETS := x86_64-w64-mingw32.shared

ビルド方法

ビルドは単純で、

$ make レシピ名

でビルドできます。例えばGTK3をビルドした場合は

$ make gtk3

とするだけで、依存ライブラリを含めたソースパッケージのダウンロードやビルドが自動的に実行されます。ビルドされたソフトウェアはデフォルトでは./usr/[ターゲット名]以下にインストールされるようです。

新たなソフトウェアの追加方法

MXEの各パッケージ向けMakefileはsrcディレクトリ下に[パッケージ名].mkの名前で用意されています。
拡張子が.mkのファイルは自動的に認識されるので、新たなソフトウェアは同様の方法で追加することができます。

また、前述のsetting.mkに以下のような設定があることからもわかるように

# This variable controls which plugins are in use.
# See plugins/README.md for further information.
#override MXE_PLUGIN_DIRS += plugins/apps plugins/native

MEXにはpluginという仕組みがあり、この仕組みを使って新たなパッケージを追加することもできます。プラグインとは言っても、単に.mkファイルを読み込むディレクトリを追加するだけですので、src以下の.mkファイルと同様に変数やマクロを追加(あるいは上書き)するだけです。詳細は、上記のコメントにもある通りplugins/README.mdに記載されています。

まとめ

UNIX系OS上からWindowsへのクロスビルドに特化したビルドシステムMXE(M cross environment)を紹介しました。

クリアコードが過去に関わった案件で、GTKを使用してマルチプラットフォームアプリケーションを開発されているお客様がおられましたが、ビルドシステムは独自に構築されており、そのメンテナンスに多大なコストがかかっていました。当時MXEのことを知っていれば、開発コスト低減の提案が出来ていたかもしれないな、ということが悔やまれてなりません。

2019-06-06

株式会社アカツキさまでの新人研修の一環としてOSS Gateワークショップを実施しました

結城です。

当社のOSS開発支援サービスではこれまで、SpeeeさんでのOSS開発支援やご相談窓口の設置、Supershipさまでの研修の実施などに携わって参りました。この記事ではその新たな事例として、株式会社アカツキさまで昨年および本年の新人研修の一環として開催した OSS Gate のワークショップを社内にて複数回開催させていただいた件をご紹介します。

アカツキさまへのクリアコードの関わり方

株式会社アカツキさまの事業の柱の1つはゲームの開発・運営です。お話を伺ったところ、ゲーム業界では(少なくとも、ファミコンに端を発するコンソールゲームの業界では)伝統的に、会社ごとに自社開発のライブラリやノウハウを抱え込む事が多かった事から、開発の成果を公開したり社外に開発の中心があるOSSの開発に参加したりという事はそれほど積極的には行われない傾向があるとのことでした。

しかし、現代のゲーム開発では技術が1社だけに閉じないケースが多くなっています。オンラインゲームではサーバー側の技術基盤は一般的なWebサービスと変わりませんし、クライアント側においてもUnityのスクリプトエンジンが.NET FrameworkのOSS実装のMonoであったりします。また、ゲーム本体から周囲に目を向ければ、そもそも開発に使用するGitやVisualStudio CodeなどのツールもOSSです。このように、サービス提供も開発も今やOSS無しではままならないというのが実情です。こういった背景があり、アカツキさまでは「会社としてOSS活動を推進していきたい」という意気が高まっていたそうです。

これを踏まえて、アカツキさまの技術アドバイザーSpeeeさまの技術顧問を兼任されている井原さんから当社をご紹介頂き、アカツキさまのOSS活動推進の取り組みをお手伝いさせて頂くこととなりました。その際、具体的な取り組み方について相談した結果、まだOSS活動に関わったことがない人が多い・(今後の進め方を考えるために)当社が現状を把握したいという理由から、まずは当社メンバーが案内役を務める形で OSS Gate ワークショップをアカツキさま社内で開催することとしました。

OSS Gate ワークショップ in アカツキ

OSS Gate ワークショップは、参加者を「まだOSS開発に関わった事が無い人=ビギナー」と「OSS開発に関わった経験がある人=サポーター」に分けて、ビギナーが主体的に作業を進めるのをサポーターが手助けする*1という形で、バグ報告やパッチの提供などの「フィードバック」をする体験をして頂くイベントです。今回は、ビギナーとしては主に新入社員の方や中途採用されて日が浅い方に多くご参加頂く結果となりました。また、通常の公開で行う OSS Gate ワークショップではサポーターは広くボランティアを募っていますが、今回はサポーターも皆アカツキさま社内の方に務めて頂きたいという事で、こちらも社内で希望者を募ったり、アカツキ内でこの取り組みの主担当をされているシニアエンジニアの島村さんから指名して頂いたりしました。これは、サポートする側を経験して頂く事により、普段から業務の中で他の方のOSS開発への参加を手助けして頂きやすくなればという目論見があったためです。

開催形態は東京で開催している OSS Gate ワークショップの形態を踏襲し、1人のサポーターが1~2人のビギナーと組む形としました*2。参加希望者のスケジュールが合う日がバラバラだったため複数の日に分けて開催することとし、2018年には4回、2019年には3回に分けて実施しました。

ワークショップは通常は1日がかりで行いますが、今回はアカツキさまの業務の都合もあり、内容を一部省略して所要時間を2/3~半分程度に短縮しての実施としました*3

(写真:2019年開催分の様子)

各回の参加者アンケートは OSS Gate ワークショップのリポジトリで公開されており、以下から閲覧可能です。

OSS Gate ワークショップはフィードバックの過程を体験して頂くという趣旨のイベントなので、バグ報告やパッチ提出などの形に残る結果を出すことを必須には設定していません。そのため、通常の OSS Gate ワークショップはフィードバックに至る手前で時間切れになる方も多いです*4。ただ、アカツキさまでの開催分については研修で前提知識が共有されている部分が多いためか躓きはあまり発生せず、ほとんどの方が時間内に実際にフィードバックを行う段階まで到達されていました。

筆者が印象的だった出来事としては、ビギナーの方が選択を検討されていたソフトウェアが実はOSSではなかった、という事例がありました。ゲーム業界ということでUnityを使われている方が多く、その方は「ユニティちゃんライセンス」が設定されたプロジェクト(unity3d-jp/unitychan-crsなど)へのフィードバックを検討されていたのですが、調べてみると、当該ライセンスはOSIが認定済みのOSSライセンス一覧には掲載されていませんでした*5。この事例のように、GitHubにリポジトリがある・ソースコードが閲覧可能な状態になっているからといってOSSであるとは限らない、という点は意外と見落としてしまいがちなので、皆さんもくれぐれもご注意下さい*6

なお、この一連のワークショップが契機となってか、後に参加者の方が新たにOSSを公開されたという事を後からお知らせ頂きました*7。OSS Gate ワークショップではフィードバックをする際の注意点として「作者(プロジェクト運営者)にとって分かりやすくなるように報告内容を整理する」という事を度々伝えていますが、自分自身が作者としてOSSを公開する側になると、実際にフィードバックを受ける際にどのようなフィードバックであれば助かるかという事を、より実感を持って考えられるようになるはずです。得るものは多いと思われますので、開発したソフトウェアをOSSとして公開する事にも、皆さんには是非チャレンジして頂きたいです。

継続的なOSS活動の推進のためのふりかえり

アカツキさま社内での OSS Gate ワークショップ開催は、ゴールではなく最初の一歩という位置付けです。そのため、毎回のワークショップ終了後にはアンケートの回答を見ながら参加者全員で「どうすれば、社内にOSS活動が根付くか?」という事を考えるふりかえりの時間を設けました。

ワークショップ後も継続してフィードバックしていく事が根付くかどうかが課題であるという点は、公開で開催されている OSS Gate ワークショップと共通している様子でした。アカツキさまのケースでは特に、「業務と並行してフィードバックも行う」という事を全体でどう習慣化していくかが課題という事になります。この点について、各回のアンケートの回答からは、現在は「普段の業務」と「OSS活動」を全く別の物として捉えている方が多いようで、「普段の業務をしていると、OSS活動をする時間がない」と認識されている様子がうかがえました。

業務上使用しているツールやライブラリの不具合を見つけた場合、仮にそれが自社開発の物であれば、不具合を伝えて修正してもらうという事は、業務の中の出来事として受け入れやすいでしょう。その際にはもちろん、修正する側にとってわかりやすい形の障害報告にするという事も求められるはずです。業務で使用するOSSへのフィードバックも、報告先が公開のイシュートラッカーであったり、報告に使う言語が英語であったり*8という差異はあるものの、基本的にはそれと同じ事と言えます。よって、心理的な障壁をどのようにして取り除くか、いかにして「業務の活動をしていたら、それが自然とOSS活動になっていた」と言える状況を作るか、という事が重要なのではないかと思われます。

ただ、業務での開発では「ライブラリやツールのバージョンは開発時点の物で固定し、最新版へは更新しない」といった事も行われがちです。その場合、フィードバックの結果が業務に反映されることが無いという事になってしまうので、開発元へのフィードバックに時間を使う事に対して「業務と無関係な事をしている」感覚が強くなってしまう、という側面はありそうです。この種の問題については個人でどうにもなりませんので、むしろ、古いバージョンのソフトウェアを使い続ける事にはセキュリティなどの面でリスクがあるという認識を全員で持って、適宜ソフトウェアのバージョンを上げていく健全な開発・メンテナンスのスタイルを取り入れていくという、普段の開発そのものの見直しも必要になってくるでしょう。

こういった事を全体で一気に推し進めるのは困難が伴います。アカツキさまでは元々、日常的にフィードバックをされている方の周囲ではスポット的にそのような動きが見られるとのことですので、全体を一気に改革するという事には拘らず、周囲の人に影響を与えるリーダーとなる人を増やした上で部分部分のできる所から進めていく、というのが無理のない進め方なのかもしれません。

また、この取り組み全体を担当されているシニアエンジニアの島村さんと一緒に行った、全体を通してのふりかえりの中では、OSSへのフィードバックで困った人が表れた時の気軽な相談先として当社をお使い頂く、というような方向でのOSS推進もご検討頂きました。

まとめ

以上、アカツキさまでのOSS推進の取り組みの一環として実施した OSS Gate ワークショップの様子をご紹介しました。

当社では「自社内にOSS開発者を育てたい」「OSSへのコントリビュートを行いたい」といった企業さまのご依頼を受けて、人材育成や社内文化の改善といった観点からご協力させて頂くというOSS開発支援サービスを行っています。今回のアカツキさまでの事例のように、社内事情に合わせて規模や内容をカスタマイズしての OSS Gate ワークショップ開催のお手伝いも承っております。社内で旗振り役を務められる人が少ないためにOSS活動をうまく推進できていない、などのお悩みをお持ちの方がいらっしゃいましたら、是非メールフォームよりお気軽にお問い合わせ下さい。

なお、近い日程では2019年6月8日にOSS Gate 東京ワークショップが開催される予定です。公開のOSS Gateワークショップはサポーター側が不足しがちなため、ビギナー側での参加は枠がすぐ一杯になってキャンセル待ちになりやすいのですが、「バグ報告をした事がある」という方はサポーターとして登録して頂ければ、多くの場合はそのまま参加できます。会社としてOSS活動を奨励していきたいという方は、実際にワークショップに参加して頂くと雰囲気や要領を参考にして頂けると思いますので、是非ご検討下さい。

*1 詰まった時にヒントを出す、迷ったときにアドバイスするなど。

*2 東京開催分は、現在は基本的に1人のビギナーに1人以上のサポーターが付く形態とし、サポーターの人数以上のビギナーを集めない方針を取っています。アカツキさま社内での開催に関してはビギナー参加者を可能な限り多く受け入れる必要があったため、進行役・サポートメンターを加えればマンツーマンに相当する人数比は維持できそうという事から、この人数に設定しました。

*3 過去には、PHPカンファレンスの中で一室を借りて実施した際にもこの形態を取りました。

*4 Gitの使い方に手間取ったり、課題選びに悩んだり、選んだ課題の規模が大きすぎて準備に時間がかかったり、という例が多いようです。

*5 OSIでは、オープンソースの定義に合致していて、且つOSIで認定されているライセンスに基づくソフトウェアのみをOSSと呼ぶ事を推奨しています。また、よくある質問の回答として、オープンソースの定義に合致していても一覧に掲載されていない物をOSSライセンスと称する事は混乱を招くため、(承認され一覧に掲載されるまでは)「独自のOSSライセンス」を無闇に策定・自称しない事が求められています。なお、筆者がユニティちゃんライセンスの文面を確認した限りでは、営利目的での利用の禁止などの条件がオープンソースの定義に合致しないようでした。

*6 OSSでなくとも、ライセンスの条件に従って利用する限りにおいては何ら問題ありません。また、OSSライセンスでないライセンスを設定する事もここでは否定していません。ただ、OSSライセンス同士であっても条件が互いに矛盾するために成果物を組み合わせられない場合はあり、OSSライセンスとOSSでないライセンスとの組み合わせでは余計にそのような状況が発生しやすい、という事は言えます。

*7 s-luna/AndroidReplayRecorder

*8 作者が日本語話者のOSSでは、日本語でフィードバックするという事もありますが、多くの場合は英語が使われています。

2019-05-29

Thunderbirdアドオン「CardBook(連絡先)」でローカルに保存されるデータの暗号化に対応しました

CardBookと企業利用

皆さんはCardDAVという仕様をご存じでしょうか? CardDAVはWebDAVのプロトコルを使ってLDIF形式のアドレス帳をやり取りするという物で、これを用いると「読み書き両方を行えて、内容が複数PC間で同期される」という種類のリモートアドレス帳を汎用の物として実現することができます。CardDAVサーバーとして振る舞える製品にはownCloudやDAViCalなどがあり、読み取り専用に設定したリモートアドレス帳を複数人で共有するという事もできますので、企業利用では重宝する場面がありそうです。

このように便利なCardDAVですが、残念ながらThunderbirdは本体の機能としては対応していません。CardDAVベースのリモートアドレス帳を使うにはアドオンをインストールする必要があります。CardBookは、そのようなCardDAV対応のためのアドオンの一例です。

ところで、企業によっては個人情報の取り扱い方について、「顧客や取引先の個人情報をローカルに保存する際は必ず暗号化する」といったプライバシーポリシーを定めている場合があります。前出のCardBookはリモートアドレス帳のデータをIndexedDBを使用してローカルにキャッシュする設計で、この時のデータは暗号化されないため、そのままでは前述のポリシーに抵触するので採用できないという事になります。

そのような背景から、「CardBookでローカルに保存されるデータを暗号化したい」というご相談を頂き、成果を開発元に還元する前提で先行して作業を進めていたのですが、残念ながら受注には至らず、手元には実現可能性の調査のために行った試験的な実装が残るという結果になりました。しかしせっかく実装した物をそのまま放置しておくのも勿体なかったので、CardBookプロジェクトに還元したところ、標準機能の1つとして取り込まれるに至りました。現在リリース済みのCardBook 33.9以降のバージョンでは、設定画面でチェックボックスをONにすればローカルデータの暗号化が有効になるようになっています。

以下、CardBookのローカルデータベースの暗号化を実現するにあたって行った具体的な内容をご紹介します。

IndexedDBに格納するデータの暗号化と復号

幸い、Thunderbirdの基盤であるGeckoには、暗号化のための汎用APIであるWeb Crypto APIが実装されています。あるのなら使わない理由はありませんので、CardBookでもデータの暗号化はWeb Crypto APIによる共通鍵暗号で行う事にしました。何故公開鍵暗号ではなく共通鍵暗号なのかについては別項で詳しく述べていますので、そちらも併せてご覧下さい。

実装は、まず暗号化・復号を行う専用のモジュールを追加した上で、IndexedDBの読み書きを行うモジュールの書き込み用のデータを用意する箇所に暗号化処理を読み込んだデータを検証する箇所に復号処理を仕掛けることで、他のモジュールに影響を与えず透過的に動作するような組み込み方としました。

この時気をつけなくてはならないポイントとして、暗号化をどのタイミングで行うかという点が挙げられます。以下、暗号化を行っている実際の箇所を抜粋しながら説明します。

元々の設計では、IndexedDBへのデータ書き込みは以下の要領で行われていました。

addCard: unction (aDirPrefName, aCard, aMode) {
  var db = cardbookRepository.cardbookDatabase.db;
  // トランザクション開始
  var transaction = db.transaction(["cards"], "readwrite");
  var store = transaction.objectStore("cards");
  var storedCard = aCard;
  // データの書き込み
  var cursorRequest = store.put(storedCard);

  // 以下、成功時・エラー時の処理
}

ここに暗号化処理を組み込むのですが、Web Crypto APIは暗号化したデータがPromiseで返されるため、値を使うには.then()のコールバック関数で受け取るか、awaitで値の解決を待つ必要があります。コールバック関数を使うスタイルで実装するにはこのメソッドの書き方を大幅に変えなくてはなりませんが、asyncキーワードとawaitを使うと、この同期処理の関数を容易に非同期処理に対応させることができます。

addCard: async function (aDirPrefName, aCard, aMode) { // asyncキーワードを追加
  var db = cardbookRepository.cardbookDatabase.db;
  // トランザクション開始
  var transaction = db.transaction(["cards"], "readwrite");
  var store = transaction.objectStore("cards");
  // 暗号化処理を追加
  var storedCard = cardbookIndexedDB.encryptionEnabled ? (await cardbookEncryptor.encryptCard(aCard)) : aCard;
  // データの書き込み
  var cursorRequest = store.put(storedCard);

  // 以下、成功時・エラー時の処理
}

当初はこの例のように、書き込みを行う直前で暗号化を行うようにしていました。しかし実際に動作させてみると、これではIndexedDBでのデータ書き込みに失敗するという結果になりました。何故でしょうか?

実は、IndexedDBでのデータ書き込みはトランザクション開始から書き込みまでを同期的に(同じイベントループ内で)行う必要があります。この例ではトランザクション開始後にawaitを使ってしまっているせいで、store.put(storedCard)が次のイベントループでの実行となってしまい、そのせいで書き込みに失敗してしまうという訳です。

そのため最終的な実装では、以下の例のようにトランザクション開始前に暗号化を終えておくようにしました。

addCard: async function (aDirPrefName, aCard, aMode) {
  // 暗号化
  var storedCard = cardbookIndexedDB.encryptionEnabled ? (await cardbookEncryptor.encryptCard(aCard)) : aCard;
  var db = cardbookRepository.cardbookDatabase.db;
  // トランザクション開始
  var transaction = db.transaction(["cards"], "readwrite");
  var store = transaction.objectStore("cards");
  // データの書き込み
  var cursorRequest = store.put(storedCard);

  // 以下、成功時・エラー時の処理
}

これなら、トランザクション開始から書き込みまでが同期的に行われるため問題ありません。

復号時には、特にこのような注意は必要ありません。また、元々IndexedDBからのデータ読み取りは結果が非同期で返されるので、CardBookのデータ読み込み処理もその前提で設計されていました。そのため、IndexedDBから返ってきたデータを非同期で復号した上で返却するという処理を挟み込んでも、CardBookのデータ読み込み処理全体としてはインターフェースを変えずに済んだのでした。

パスワード入力を求める方式にしなかった理由

CardBookのローカルデータ暗号化では、暗号化・復号に使う共通鍵は、バックグラウンドで自動生成した物を暗黙的に使い、鍵自体をユーザープロファイル内に保存する形としました。

「暗号化されたデータと鍵を同じ場所に置いておくのでは、暗号化の意味が無いじゃないか」と思うでしょうか? 実際、変更をフィードバックした際にもCardBookプロジェクトの開発者の方からも「パスワード入力を求める方式にした方がいいのではないか?」という質問がありました。Web Crypto APIの機能を使うとユーザーが入力したパスワードから秘密鍵を作る事もできる(Web Crypto APIの解説記事の「パスワードを鍵に変換する」の項をご参照下さい)のに、そうしなかったのは何故でしょうか。

ここで一旦、パスワードの安全な運用という事を考えてみましょう。パスワードを自分で記憶しておきその都度入力するという方式は、一見すると安全なように思えます。しかしながら、運用の仕方によっては却って危険になる場合があります。

  • パスワード入力には、肩越しに入力の様子を覗き見るショルダーハックや、キーの入力を監視するキーロガーなどによってパスワードを盗み取られるリスクがあります。パスワード入力の頻度が多ければ多いほど、このリスクは高まります。
  • 人間の記憶容量には限りがあるため、あまり複雑なパスワードを複数覚えるという事はできません。そのために「同じパスワードを何度も使い回す」「推測が容易なパスワードにする」といった事が行われてしまい、そうなると却って危険な状態となります。
  • 定期的なパスワード変更にも、同様の問題があります

これらの理由から、パスワードの入力は「複雑で憶えにくいパスワードを1つだけ覚える」「それをマスターパスワードとして使い、それ以外はパスワードマネージャに憶えさせる」という運用にするのが比較的安全だというのが現在の定説となっています*1

「企業でThunderbirdを使う」というシチュエーションでは、「PCのログオン」「受信メールサーバーの認証」「送信メールサーバーの認証」などでそれぞれパスワードの入力が発生する可能性があります。という事は、ここにさらに「ローカルデータの復号」のためのパスワードが加わるというのは、さすがに実運用を妨げるレベルの煩わしさでしょう。かといって、他の部分ではパスワードを使用していないのにここでだけパスワードの入力を求める、というアンバランスな運用も考えにくいです。そういった事を考慮した結果として、CardBookのローカルデータ暗号化は現在比較的安全とされている運用を想定し、

  • 秘密鍵は、自動生成した物をJWK形式でエクスポートし、「長いパスワード文字列」の一種としてThunderbirdのパスワードマネージャに記憶させる。
  • 秘密鍵の保護が必要な場合は、使用者が任意でThunderbird本体のマスターパスワード機能を有効化する。

というポリシーを採用する事にしたのでした。

まとめ

Thunderbird用アドオンのCardBookに対して行った、ローカルデータの暗号化対応の概要をご紹介しました。

当社では、一般に公開されているFirefox用アドオン・Thunderbird用アドオンをはじめとした様々なフリーソフトウェア・OSSについて機能追加・改造のご依頼を承っております。また、成果をアップストリームに還元しても差し支えがないケースでは、積極的に還元を行うようにしています。自社でフリーソフトウェア・OSSを採用したいが少しだけ要件に合わない、という事でお悩みの場合には、メールフォームからお問い合わせ下さい。

*1 ただし、これはあくまで現時点での話です。技術の進歩や、この分野での研究が進む事などによって、「最もマシ」なやり方は変わっていく可能性があります。

2019-05-28

「以前のバージョンでは使えていた機能が新バージョンでは使えなくなった」時の原因の調べ方

この記事はQiitaとのクロスポストです

恐らくあまり知られていませんが、Firefoxはインストール先の browser/chrome/icons/default/main-window.ico の位置(Firefoxのインストール先が C:\Program Files\Mozilla Firefox\ であれば、 C:\Program Files\Mozilla Firefox\browser\chrome\icons\default\main-window.ico)にICO形式のアイコン画像を置くと、それがメインウィンドウのアイコンになるという機能がありました*1

(スクリーンショット:実際にアイコンを変えた様子)

この機能について、次のESR版であるESR68でも有効なのかどうかを確かめようとNightly 68で試してみたところ、アイコンが変化しないという結果を得られました。

ということは、Firefox ESR68では機能が削除されたのだな……と決めつけるのは早計です。開発版のNightlyで駄目だったからといって、そのバージョンのFirefoxが正式リリースされた後も駄目だとは限りません*2。たまたまNightlyでは機能していないだけなのか、それともこの方法が使えなくなってしまったのか、きちんと裏付けを取らないとはっきりした事は言えません。

この記事では、フリーソフトウェア・OSSで遭遇したこのような挙動の変化の経緯を実装を辿って調べる方法について、Firefoxでの事例を示します。

調査の取っかかりを探す

この事例では「ウィンドウのアイコン」がポイントになります。Windows上で動くFirefoxはWindowsのアプリケーションなので、ウィンドウのタイトルバーのアイコンを変えるには当然Windowsのアプリケーションの流儀に則った方法を使っているはずです。

Windows window icon win32 といったキーワードで検索してみたところ、Win32アプリケーションでウィンドウのアイコンを変える伝統的なやり方に WM_SETICON という定数で定められたメッセージを使う方法 があるという事が分かりました。

機能が動いているバージョンでどう実装されているかを調べる

確認してみたところ、前述の方法でのアイコン変更はFirefox ESR60やFirefox 66では機能していました。そこで、Firefox ESR60のソースコード中で SM_SETICON という定数が使われている箇所を検索してみたところ、クロスプラットフォームな実装ではなくWindows固有の実装の部分で、この定数が使われている箇所が見つかりました

このメソッドの冒頭で呼ばれているResolveIconName()というメソッドの定義を辿り、さらにその中で呼ばれている ResolveIconNameHelper() というメソッドの定義まで辿ってみたところ、「与えられたファイルハンドラからiconsdefault、とディレクトリを辿り、与えられたファイル名のアイコンファイルに対応するアファイルハンドラを得る」という処理が行われている様子がうかがえました。これはまさしく、前述の「特定の位置に置かれたアイコンファイルを参照する」という処理そのものに見えます。

この箇所は nsWindow クラスの SetIcon() メソッドに含まれていますので、今度はこのメソッドを呼んでいる箇所を探しました。すると、XULのwindow要素のid属性の値を参照してアイコンを設定しているらしい箇所が見つかりました。

  // "id" attribute for icon
  windowElement->GetAttribute(NS_LITERAL_STRING("id"), attr);
  if (attr.IsEmpty()) {
    attr.AssignLiteral("default");
  }
  mWindow->SetIcon(attr);

ということで、先の方法でウィンドウのアイコンを変更するという機能は、この一連の箇所によって実現されている事が分かりました。

機能が動いていないバージョンでどう実装されているかを調べる

機能が動いているバージョンでの実装が分かったので、今度は対応する部分のNightly 68での実装を見てみました。

先ほど見ていった実装のそれぞれについてNightly 68での状況を見ていくと、ほとんどの箇所には変更が見られませんでしたが、nsWindow クラスの SetIcon() メソッドを呼んでいる箇所に違いがありました。こちらでは、XULのwindow要素のicon属性の値を参照して、値があった時にだけアイコンを設定するというコードになっています。

  // "icon" attribute
  windowElement->GetAttribute(NS_LITERAL_STRING("icon"), attr);
  if (!attr.IsEmpty()) {
    mWindow->SetIcon(attr);

    NS_ENSURE_TRUE_VOID(mWindow);
  }

Firefoxのソースコード検索システムでは、そのまま続けてMercurialのblameの結果を見る事ができます。そこから当該箇所の変更が行われたコミットの情報を見ると、バグトラッカー上での対応するbugの番号が記載されており、Bug 1531836 - Each new xul window does a stat call to look for non-existant window specific iconsに辿り着きました。

bugの内容を読むと、どうやら起動処理の高速化の一環として、「まず存在しないファイルを探しに行く」のではなく「明示的にアイコンが指定されている時だけファイルを探しに行く」という方針に改める変更が行われ、その一環として前述の機能が削除されたという事のようです。

まとめ

以上、フリーソフトウェア・OSSで「以前のバージョンでは使えていた機能が新バージョンでは使えなくなった」という事態に遭遇した時の原因の調べ方の、Firefoxでの事例をご紹介しました。

ユーザーの目に触れやすい機能の変更はリリースノートに記載されることが多いですし、また、開発者向けに影響の大きな変更は技術情報が別途まとめられている事もあります。しかし、この例のように影響度の小さい変更は、そのような変更があったという事自体は特に告知されないままになっている事があります。そういった場合でも、フリーソフトウェア・OSSではソースコードやバージョン管理システムの変更履歴を辿って、原因を特定したり回避方法を見つけたりする事ができます。

クリアコードではフリーソフトウェア・OSSに対する技術サポートを有償にて提供しており、お客様からの「今まで使えていた機能が使えなくなった事の理由を調べて欲しい」「回避策があれば、それを教えて欲しい」といったご依頼を受けてこのような調査も行っています。
業務上でのフリーソフトウェア・OSSの利用でお困りの方は、是非メールフォームからご相談下さい。

*1 正確には、`(id文字列).ico` という名前でファイルを置いておくと、ウィンドウの内部的なID(XULの`window`要素の`id`属性の値)が一致するファイルが自動的に使われるという事になっています。

*2 その逆に、Nightlyでは使えていた機能が同じバージョンのリリース版Firefoxで使えなくなる場合もあります。例えば `xpinstall.signatures.required` という設定はビルド時のオプションによって機能するかどうかが切り替わるようになっており、NightlyとDeveloper Editionでは使えますが、ベータ版および正式リリース版のFirefoxでは機能しないようになっています。

タグ: Mozilla
2019-05-14

SciPy Japan Conference 2019 - Apache Arrow #scipyjapan

ゴールデンウィークの前のことになりますが、SciPy Japan Conference 2019Apache Arrowの紹介をした須藤です。

関連リンク:

経緯

まず、ふだんほとんどPythonを書いていない私がどうしてSciPy Japan Conference 2019で話すことになったのか、その経緯を説明します。

もともと、Wes McKinneyさんにオファーがありました。彼はPythonでよく使われているpandasの作者でもあり、Apache Arrowの主要開発者でもあります。SciPy Japan Conference 2019でApache Arrowの紹介をする人としてはうってつけです。しかし、残念ながらSciPy Japan Conference 2019の開催日に日本に来ることができませんでした。そこで、彼が私を紹介しました。私もApache Arrowの主要開発者の1人(2019-04-23時点ではApache Arrowへのコミット数は2位)で、私は東京に住んでいるからです。

ということで、Wesさんからの紹介で私がApache Arrowの紹介をすることになりました。

内容

Pythonのカンファレンスなので、Apache Arrowの一般的な話というより、Pythonユーザーにはどううれしくなりそうかという観点で紹介したつもりです。具体的には近い将来うれしくなりそうな点として次の2点を紹介しました。

  • pikcleの代わりに使って高速化できる
  • データフレームライブラリーが高速化したり大量のデータを扱えるようになる

すでにこれらの点を実現しているプロダクトがあります。

たとえば、Sparkはpickleの代わりにApache Arrowを使うことで100倍以上高速化しています。

参考:Speeding up PySpark with Apache Arrow

たとえば、VaexというデータフレームライブラリーはApache Arrowを(も)使うことでpandasよりも高速に文字列を処理できるようになっています。

参考:Vaex: A DataFrame with super strings

また、どうして高速になるか、大量のデータを扱えるようになるかの理由も説明しました。詳細はスライドやApache Arrowの最新情報(2018年9月版)Apache Arrow東京ミートアップ2018 - Apache Arrowを参照してください。

イベントの内容

SciPy Japan Conferenceは今回が初めての開催ということで参加者は100人いかないくらいでした。PyCon JPと比べると小さな規模です。(PyCon JP 2018は1000人以上の参加者です。)初めての開催ということであまり知られていなかったこととサイエンスに特化した内容ということが影響しているのかと思います。

私は初めてPythonのイベントに参加しましたが、とても国際カンファレンスですごいなぁと思いました。私は海外で開催されている国際カンファレンスには参加したことがなく、参加したことがある国際カンファレンスはRubyKaigiだけなのですが、海外で開催されている国際カンファレンスはこんな感じなのかなぁと思いました。

SciPy Japan Conference 2019の運営をしていた方々から話を聞いたところ、SciPy Japan Conference 2019は本家のSciPy Conferenceを運営している人たちが運営しているということでした。本家のSciPy Conferenceについても教えてもらえました。「今回は機械学習の話題が多かったけど、本家のSciPy Conferenceは機械学習だけでなくサイエンス全般の話題を扱っているんだよ。地球のこととか。SciPy Japan Conferenceもサイエンス全般の話題を扱えるようになるといいな。」みたいな話を聞いてすごくおもしろそうだなぁと思いました。地球のこととかおもしろそう!Rubyでもそんなカンファレンスがあるとおもしろそう!

午前中に3時間のチュートリアルがあるのもおもしろかったです。Rubyのカンファレンスでもやりたいな。(すぐRubyのことを考えてしまう。)

午後はトークでした。知らないことばかりだったので非常に興味深かったです。Pythonではいろんなことが簡単にできるようになっていてワクワクしますね!Rubyでもそうなるといいな。

せっかくいろいろ知れたのでSciPy Japan Conference 2019の最中にこっそりOptunaのRubyバインディングであるRed Optunaを作りました。SciPy Japan Conference 2019に参加してよかったです。Red Optunaのアイディアは初日のレセプションで佐野さんと話しているときにでてきたアイディアです。話せてよかったです。佐野さんは2日目にOptunaのトークをしました。

また参加したいと思ったカンファレンスでした。

まとめ

SciPy Japan Conference 2019で日本のPythonユーザーのみなさんにApache Arrowを紹介しました。Rubyコミュニティー以外にもApache Arrowのことを紹介したいのでApache Arrowのことを知りたくなったら私に声をかけてください!

私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。

タグ: Python
2019-05-08

2019年5月4日前後から発生しているFirefoxのアドオン無効化問題の詳細な解説

「Firefox」でインストール済みアドオンが利用不能になる問題が発生中 - 窓の杜Firefox 66.0.4 リリース、拡張機能全滅問題を修正 | スラド ITなどで既報の通り、2019年5月4日前後から一般向けのリリース版Firefoxでアドオンを一切使用できない状態が発生していました。緊急の修正版としてリリースされたFirefox 66.0.4およびFirefox ESR60.6.2で現象は既に解消されており、また、既にインストール済みのFirefoxに対してもホットフィックスが順次展開されていますが、本記事では今回の問題の技術的な詳細を解説します。

ユーザー側で取れる対応

まず最初に、ユーザー側で取れる対応をまとめておきます。

  • Firefox 66.0.4以降またはFirefox ESR60.6.2以降に更新する。
  • 「Firefox調査」を通じて hotfix-update-xpi-signing-intermediate-bug-1548973 が自動的に反映されるのを待つ。
  • 有効期限が更新された中間証明書のファイルをダウンロードし、「オプション」→「プライバシーとセキュリティ」→「証明書」→「証明書を表示」で表示される証明書マネージャにおいて、「認証局証明書」の一覧の「インポート」ボタンから認証局証明書としてインポートする。
  • about:config で隠し設定の xpinstall.signatures.requiredfalse に設定する。(Firefox ESR、Developer Edition、Nightlyのみ有効で、Firefox 48以降の通常のリリース版Firefoxではこの方法は無効です)

Mozillaによるサポートが終了したバージョンのFirefoxでは「Firefox調査」を通じての修正の反映が行われないため、Firefoxの更新、証明書の手動でのインポート操作、または about:config での隠し設定の変更が必要になります。

修正が反映される前のバージョンのFirefoxを起動してアドオンが無効化されてしまった場合、証明書のインポートや about:config での暫定的対応を行った後でも「アドオンが依然として無効で、且つ、ユーザー操作で再度有効化できないまま」となる場合があります。これは、アドオンが署名の期限切れで強制的に無効化されたという情報がユーザープロファイル内に残留しているために発生する現象です。この状況に陥った場合、Firefoxを終了した状態でユーザープロファイル内の extensions.json を削除して状態をリセットすると、アドオンが自動的に有効化されます(あるいは、手動操作で有効化できる状態でアドオンマネージャに表示されるようになります)。

アドオンの電子署名の基本のおさらい

Firefoxでは、Firefox 43以降のバージョンからアドオンのインストールパッケージの電子署名が必須となっています

Firefoxのアドオンはその特性上、認証局証明書の差し替えや通信の読み取りなど、Firefox上でありとあらゆる事ができる物でした*1。これではユーザーを困らせたりユーザーの機密情報を狙ったりする事を企む攻撃者にとって格好の標的となり得るため、その種の攻撃を防ぐために、Firefox 43で以下のような仕組みが導入されました。

  • すべてのアドオンは、Mozilla Add-onsでダウンロード可能な形で公開する物も、それ以外も、審査のため一旦Mozilla Add-onsのサービスにファイルをアップロードしなくてはならない。
  • アップロードされたファイルは、エディター権限を持つ人の目視による審査を受け、受理された場合にのみMozillaによって電子署名が施される。電子署名により、エディターが審査した時のファイルと同じ内容のファイル(=安全が確認されたファイル)である事が保証される。
  • ユーザー環境のFirefoxは、入手したアドオンのパッケージの電子署名を検証し、妥当な署名である事を確認できた(=エディターが審査した通りの内容のファイルであると確認できた=安全が確認された)段階で初めてそのアドオンを使用可能にする。

ただ、これではアドオンの開発中や、社内用として機密情報を含めたアドオンを使いたい場合など、審査用にファイルを供出できないケースで支障があります。そのため、以下のような救済措置が存在しています。

  • この電子署名の検証は xpinstall.signatures.required という隠し設定を false にして無効化できる。
    • ただし、Firefox 48以降のバージョンでは、法人向けのFirefox ESRと開発者向けのDeveloper Edition、Nightlyでのみ有効で、通常リリース版のFirefoxではこの設定は機能しません。
  • about:debugging から一時的に読み込まれたアドオンに対しては、電子署名の検証は行われない。

アドオンの電子署名の詳細

ここでさらっと「電子署名が施される」と述べましたが、具体的には何が行われているのでしょうか。IE View WEの場合を例に取って解説します。

「インストール」ボタンからダウンロードできるインストールパッケージ ie_view_we-1.3.2-fx.xpi の実体はZIP形式のアーカイブですが、これを展開して取り出された中にある META-INF というフォルダとその内容がまさに電子署名の実体です。それぞれの内容は以下の通りです。

  • META-INF/manifest.mf: パッケージ内に含まれる各ファイルのハッシュ値の一覧
  • META-INF/mozilla.mf: パッケージ全体のハッシュ値
  • META-INF/mozilla.rsa: DER形式の電子署名データ

このうちの mozilla.rsa には、以下の情報が含まれています。

  • 各ファイルやパッケージ全体の情報と、Mozillaが持つ秘密鍵を組み合わせて導出した署名データ
  • 署名データの導出に使われた秘密鍵に対応する公開鍵、の情報を含む証明書

ユーザー環境のFirefoxは、実際のアドオンのパッケージに含まれるファイルの内容と公開鍵を使って署名データを導出し、それが mozilla.rsa に含まれる物(秘密鍵を使って導出された物)と一致するかどうかを以て、パッケージの内容が改竄されていないことを確認することができます。

この mozilla.rsa に含まれる証明書は、openssl コマンドを使うと以下のようにして内容を確認できます。

$ openssl pkcs7 -inform DER -text -print_certs < ie_view_we-1.3.2-fx/META-INF/mozilla.rsa
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1551780906376321839 (0x158908833dcb5b2f)
    Signature Algorithm: sha384WithRSAEncryption
        Issuer: C=US, O=Mozilla Corporation, OU=Mozilla AMO Production Signing Service, CN=signingca1.addons.mozilla.org/emailAddress=foxsec@mozilla.com
        Validity
            Not Before: Mar  5 10:15:06 2019 GMT
            Not After : Mar  4 10:15:06 2020 GMT
        Subject: C=US, ST=CA, L=Mountain View, O=Addons, OU=Production, CN=ieview-we@clear-code.com
        Subject Public Key Info:
            (省略)
        X509v3 extensions:
            (省略)
    Signature Algorithm: sha384WithRSAEncryption
         (省略)
-----BEGIN CERTIFICATE-----
(省略)
-----END CERTIFICATE-----

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1048580 (0x100004)
    Signature Algorithm: sha384WithRSAEncryption
        Issuer: C=US, O=Mozilla Corporation, OU=Mozilla AMO Production Signing Service, CN=root-ca-production-amo
        Validity
            Not Before: May  4 00:09:46 2017 GMT
            Not After : May  4 00:09:46 2019 GMT
        Subject: C=US, O=Mozilla Corporation, OU=Mozilla AMO Production Signing Service, CN=signingca1.addons.mozilla.org/emailAddress=foxsec@mozilla.com
        Subject Public Key Info:
            (省略)
        X509v3 extensions:
            (省略)
    Signature Algorithm: sha384WithRSAEncryption
         (省略)
-----BEGIN CERTIFICATE-----
(省略)
-----END CERTIFICATE-----

ここでは2つの証明書が出力されています。

  • CN=ieview-we@clear-code.com である証明書(CN=signingca1.addons.mozilla.org によって発行された):そのアドオン固有の物として発行されたコード署名証明書で、パッケージの電子署名に使われた物
  • CN=signingca1.addons.mozilla.org である証明書(CN=root-ca-production-amo によって発行された):中間証明書

CN=root-ca-production-amo の証明書は addons-public.crt というファイル名でFirefoxのリポジトリに含まれている自己署名証明書で、ビルド時にソースコード内に組み込まれ組み込みの証明書として認識される、ビルトインのルート証明書の1つです*2

ここで注目するべきなのは、mozilla.rsa に含まれる証明書のうち2番目、中間証明書の方の有効期限です。当該箇所を以下に抜粋します。

Not Before: May  4 00:09:46 2017 GMT
Not After : May  4 00:09:46 2019 GMT

これを見ると、有効期限が2017年5月4日0時9分46秒(UTC)から2019年5月4日0時9分46秒(UTC)となっています。日本標準時はUTCより9時間進んでいるので、日本では2019年5月4日の9時過ぎに有効期限を迎えた事になります。この瞬間以降、この中間証明書を含むアドオンのパッケージはすべて「期限切れの中間証明書に基づく証明書で署名されたファイル」と見なされ、「署名を検証できない」=「エディターが審査した通りの内容のファイルであると確認できない」=「安全が確認されていない」と判断されるため、新規インストールは拒絶され、インストール済みの物は無効化されるという結果になっていたわけです。

Mozillaが取った対応

上記の事を踏まえ、Mozillaではいくつかの対応が検討されたようですFirefoxのリリースブランチESR60用ブランチに実際に投入された変更を追うと、以下の3通りの方法が試された模様です。

  1. コード署名証明書の検証時にだけ現在時刻を2019年4月27日に巻き戻す
  2. 新しい中間証明書をハードコードし、それを証明書データベースに追加する*3
  3. 新しい中間証明書をハードコードし、CN=root-ca-production-amo の証明書に基づく証明書ツリーを検証する時に新しい中間証明書を代わりに使う

緊急のリリースとなったFirefox 66.0.4とFirefox ESR60.6.2には、この2番目の対応が反映されています。また、動的な反映が可能である事から、統計情報の収集・ユーザーのFirefox使用状況の収集を行うための仕組みを使って、この2番目の方法と同じ手順でインストール済みのFirefoxに暫定的対策を行う(有効な中間証明書を自動インポートする)という対応が展開されています。Firefox 66.0.3以前やFirefox ESR60.6.1以前を使用中の環境では、この対応が反映された事によってアドオンが再び使用可能になっているはずです。「オプション」→「プライバシーとセキュリティ」→「証明書」→「証明書を表示」で認証局証明書の一覧を表示し、Mozilla Corporation 配下に signingca1.addons.mozilla.org が表示されていれば、この対応が既に反映された状態であるという事を確認できます。

(スクリーンショット:認証局証明書の一覧に中間証明書が現れている状態)

一方、Firefox 66.0.5とFirefox ESR60.6.3には3番目の対応が採用されており、2番目の対応は不要になったとして取り除かれています*4

中間証明書の更新では解消されない問題

現時点では以上の通りの暫定的な対応がなされていますが、今回発生したすべての問題が解決されたわけではなく、一部のアドオンではさらに手動での対応が必要です。その中でも、アナウンスされている未解決の問題の1つである、一部のアドオンが中間証明書の更新後も有効にならない件は、ここまでで解説した電子署名の仕組みが大きく関係しています。

前述の説明の通り、アドオンは中間証明書によって発行されたそのアドオン固有の証明書によって電子署名を施された状態になっています。この証明書はアドオンごとの固有の識別子(ID)に基づいて発行されていますが、EPUBReaderなどの一部のアドオンでは、そもそもこの固有の識別子が開発元によって指定されていません。そのため、Mozilla Add-onsのシステムが自動的に一意なIDを割り当てているのですが、アドオンのファイルの最終更新日が変わったなどの契機で自動割り当てされたIDが代わってしまうと、中間証明書の更新後であっても、そもそも電子署名が不正と見なされてしまうという状況が発生します。そのため、アドオンを手動で再インストールして、電子署名に記録されたIDとアドオンの一時的なIDが一致する状態を作り直す必要があるという訳です。

他にも、コンテナータブに固有の情報が失われる、検索エンジンや起動時のホームページなどアドオンによって変更されたはずのFirefox側の設定が初期状態に戻ったままになる、といった問題が残されているようです。これらの問題が解決された場合、それらもまたホットフィックスとして展開されたり、もしくは緊急の修正版としてリリースされたりする可能性がありますので、引き続き注視が必要です。

暫定的な対応を取った後の注意点

冒頭に記載したとおり、Firefox ESR、Developer EditionおよびNightlyにおいては、 xpinstall.signatures.required による電子署名の検証の無効化によって今回の問題を回避できます。しかしながら、それは同時に、この設定と同時に導入された安全確認の仕組みを無効化する事をも意味します。企業での運用でユーザーによるアドオンのインストールを禁止していて、導入されるアプリケーションの影響範囲が厳密に制御されている状況であればよいのですが、個人でESR版を使用している場合、悪意の攻撃者によるアドオンが知らず知らずのうちに読み込まれるというリスクがあります。

この方法を取った場合は、Firefoxが更新されたり、ホットフィックスが反映されたりした事を確認できたら、早急に証明書の検証を有効化する事を強くお勧めします。

まとめ

以上、2019年5月4日以降で発生しているFirefoxのアドオン無効化問題の技術的な詳細を解説しました。

当社ではFirefoxの法人向けテクニカルサポートを有償で提供していますが、実際にお客様の環境でも影響が出ている例があります。

今回の問題に見られるような証明書の期限切れに起因する問題は、度々世間を騒がせています。記憶に新しい所では、2018年から2019年にかけても以下のニュースがありました。

証明書のように有効期限が存在するデータに依存するシステムを運用する場合、有効期限の到来には充分な余裕を持って対応できるような仕組みを作っておく必要がある、という事を思い知らされますね。

*1 これは従来型のアドオン(いわゆるXULアドオン)での話です。Firefox 57以降のバージョンで使用できるアドオン(WebExtensionsベースのアドオン)では、できる事が技術的に制限されているため、危険性は大幅に減じています。

*2 編集履歴によると、このルート証明書は2015年に電子署名の仕組みが導入された時に追加されて以降特に変更された様子はありません。ルート証明書自体は2025年3月15日まで有効なので当面は問題無く利用できますが、このファイルがもし今後更新されないままであれば、2025年に再び同様のトラブルが起こる事になるでしょう。その場合、署名の検証を無効化できなくなっている現在のリリース版Firefoxをサポート切れのまま使い続けていると、アドオンを再度有効化する手立てが全く無くなるという事にもなりかねません。これらの観点からも、サポート切れのバージョンのFirefoxを使い続ける事にはリスクがあり、原則としてサポートが継続しているバージョンを使い続ける事が望ましいという事が言えます。

*3 各アドオンの署名データに含まれる中間証明書と同じSubjectの中間証明書がFirefoxの証明書データベースにも含まれるようになり、以後はそちらが優先的に認識されるために中間証明書の期限切れ問題が解消する、という理屈です。

*4 一度証明書データベース登録された中間証明書は、明示的に削除しない限りはFirefox 66.0.5への更新後もそのまま残ります。

タグ: Mozilla
2019-05-07

回帰テストの対象は適切に設定しよう

先日、Bug 1541748 - New tab and restored tab notified via tabs.onCreated can have invalid (too large) index という報告をFirefoxのバグトラッキングシステムに行った結果、提出したパッチがFirefox 68に反映される事になりました。

とはいえ、実はこのパッチで追加されるコードは皆さんのお手元にインストールされるFirefoxには反映されません。何故なら、これは自動テストを追加するだけのパッチだからです。

パッチが投入されるまでの経緯

この報告は、「Firefoxのアドオン開発に使用するWebExtensionsのいくつかのAPIにおいて、タブの位置を示すindexというプロパティが不正な値になる場合がある」という物でした。ただ、これは実はWebExtensions APIの実装の問題ではなく、Firefoxのタブが内部的に持っている対応する情報自体が壊れていたというのが真相でした。

(図:壊れた内部状態が露出した事が原因で、APIの情報が壊れている様子)

「根本的な原因」の方は Bug 1504775 - Index is wrong for restored tabs という別のBugで取り扱われていました。今回の報告と前後してそちらのBugの作業が進行していたため、そちらの方で修正が行われた事の影響として、報告からあまり間を置かずにWebExtensions APIの不具合も自然解消したという状況になっていました。

一般的に、このような「ある報告の原因が別の報告の修正で解消された」というようなケースでは、その旨を記して修正済み扱いとしたり、もしくは重複する報告であるとして処理する事が多いです。

(図:2つのBugに因果関係がある時、両方が閉じられる様子)

このような場合、そちら側に提出されたパッチはBugのクローズに伴って破棄されがちです。しかし今回はそうではなく、Bugはクローズされずに留め置かれ、新たに自動テストを加えるだけのパッチを追加投入する事が承認されました。

これは、根本原因の方のBugがFirefox内部の問題のみを取り扱う物であったのに対し、こちらで報告したBugはアドオンという外部アプリケーションに対する互換性の問題を取り扱っていたからです。

テストは目的ごとに必要

根本原因が修正されたパッチでは、Firefox内部のAPIに対するテストケースは追加されていますが、WebExtensionsのテストケースには特に変更は加えられていませんでした。

今回はFirefoxがたまたま「タブの内部的なindexがそのままWebExtensions APIを通じてアドオンに露出する」という実装を取っていたために、WebExtensions API側の問題も偶然解消されました。しかし、それはあくまで現時点の実装がそうであったからというだけで、将来に渡ってそうであるという事の保証はどこにもありません

(図:内部向けの保証はあってもアドオン相手の保証は無いという様子)

よって、今後Firefox内部の実装やWebExtensions APIの実装が変更された場合に、人知れず再び同じ問題が起こるようになるという可能性は依然としてあります。

そのため、今回のパッチでWebExtensions APIのレイヤにおいて後退バグ*1が発生していないかを検出するための回帰テスト*2を追加することで、WebExtensions APIに意図せず影響を与えてしまう(そのような変更が気付かれずに投入されてしまう)という事故の発生を未然に防ぎ、APIの安定性を保証しやすくしたという事になります。

(図:保証する相手ごとにテストがある様子)

気をつけて欲しいのは、これは「単体テストと結合テストではレイヤが違うので、単体テストで検証済みの事もすべて結合テストで再検証しよう」という話ではない、という事です。同じ事を保証するテストがそれぞれの実装レイヤに含まれるという事はあり得ますが、それはあくまで結果的にそうなっているだけに過ぎません。テストは誰に対して何を保証するか、という観点で整備される事が重要です。

今回は「Firefox内部向け」と「アドオンという外部アプリケーション向け」のそれぞれで修正・互換性を保証する必要があったためにこうなったわけです。

まとめ

Firefoxに投入された自動テストのみのパッチの背景説明を通じて、テストの目的とテストを追加するかどうかの判断基準の一例をご紹介しました。

自動テストの追加は、ドキュメントの修正に次いで「外部のコントリビューターとして開発に参加する最初のステップ」として取り組みやすい作業です。自動テストが全く存在しないプロダクトや、自動テストがあっても特定の機能についてテストが不足しているというケースに遭遇したら、皆さんもぜひテストの追加に取り組んでみて下さい。独りでやりきるには不安があるという方は、スケジュールが合うOSS Gateのワークショップに参加してサポーターの人に相談してみても良いかもしれません。

*1 今まで期待通りに動いていた物が、別の変更の影響で期待通りに動かなくなってしまう事。

*2 ある問題を修正したときに、問題が発生しなくなっている事を検証するテストのこと。

2019-04-23

RubyKaigi 2019 - Better CSV processing with Ruby 2.6 #rubykaigi

RubyKaigi 2019の2日目にBetter CSV processing with Ruby 2.6という話をした須藤です。今年もクリアコードはシルバースポンサーとしてRubyKaigiを応援しました。

関連リンク:

内容

この1年、 @284km と一緒にcsvを改良していたのでその成果を自慢しました。せっかく2人で話をするので、時間を分割してそれぞれ話をするのではなく、ずっと掛け合いをしながら2人で話をしました。楽しんでもらえたでしょうか?

Ruby 2.6.3に入っているcsvはどんなケースでもRuby 2.5のcsvより高速になっています。この成果を使うためにぜひ最新のRubyにアップグレードしてください。

最後にも少し触れましたが、まだまだcsvやその周辺に改良すべきことがあります。今回の話でcsvの開発に興味がでてきた人は一緒に改良してきましょう。その気になった人はRed Data ToolsのGitterに書き込んでください。どうやって進めるか相談しましょう。

RubyKaigi 2019でまわりの人たちと相談してstrscanのメンテナンスを引き取ることにしたのでそのあたりの改良もできます。

RubyData Workshop

発表の後、RubyData WorkshopでもRed Data Toolsの開発に参加する人を募りました。すでに数人Red Data ToolsのGitterに書き込んでいる人もいます。やったね!

まとめ

RubyKaigi 2019で最近のcsvの開発の成果を自慢しました。今回は2人での発表だったのでやいのやいのした発表をしました。

Red Data Toolsの仲間が増えたのでよいRubyKaigiでした。

タグ: Ruby
2019-04-20

最新記事
タグ:
年・日ごとに見る
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|