ソフトウェアの互換性と僕らのUser-Agent文字列問題
いろいろな環境で動くプログラムでは互換性のためにその場しのぎのことをしないといけないことがよくあるけど、歴史が積み重なってくると、アドホックな技の上にアドホックな技が積み上がる喜劇的な状態になることがある。こういう問題は認識するのは簡単だが直すことは誰にもできない。まさに僕がそのような体験をしたのでちょっと説明したい。
僕は仕事としてオープンソースのlldというリンカを書いている。リンカというのはコンパイラが生成したバイナリファイルをつなぎ合わせて最終的な実行ファイルやDLLを作成するプログラムで、知らない人も多いと思うけど、何をコンパイルしても最後にはリンカが動いている。lldは既存プログラムより何倍も速くてビルドが早くなるというので最近は結構人気が高まっていて、FreeBSDなどのいくつかのOSが全面的にスイッチしようとしたり、あるいは大規模プロジェクト(Chromeや、どうもFirefoxなども)が個別にlldにスイッチしようとしていたりする。
個別プロジェクトの場合は問題が生じてもlldかそのプロジェクトのコードを直せばいいので、互換性問題が生じやすいのは、有象無象のプログラムをたくさん含むOSの標準ビルド環境としてlldを採用する場合だ。ここで説明する問題もFreeBSDで発生した。
Unixでプログラムをビルドしたことがある人は、"./configure"スクリプトを実行したことがあると思う。UnixはLinuxやFreeBSD、macOSなどいろんな種類があるので、何かをビルドするときは、まず環境を把握し、それに合わせてビルドファイルを生成し、改めてmakeコマンドを実行するということを行うことが多い。configureスクリプトは環境を把握してビルドファイルを作成するためのシェルスクリプトで、たとえばstrnlen関数が存在するか、みたいなことを、実際にその関数を含むファイルを作成してコンパイルしてみようとすることでチェックする、といったことを行う。
FreeBSDの人たちがOSのビルド中に発見した問題は、lldが標準リンカになっている状態でconfigureを実行すると、lldがconfigureに80年代みたいな古代のUnixのリンカのように判定されてしまう、という問題だった。どうしてそうなるのかを調べてみると、configureは裏でリンカを--helpオプション付きで実行していて、表示されたヘルプメッセージの中に"GNU"あるいは"with BFD"という文字列が含まれていた場合だけモダンなリンカが存在すると判定していることがわかった。つまりこの環境ではgccなどを含むGNUプロジェクトのリンカだけがモダンであり、それ以外は極めてしょぼい、と仮定されていた。
これはちょっと困った問題だった。GNUリンカの場合、ヘルプメッセージは"GNU ld 2.23"みたいな文字列を含んでいるからよいのだが、僕らはGNUプロジェクトとは関係がないので、GNUという文字列は当然含んでいない。
考えられる解決策は2つあった。1つはconfigureスクリプトを直すことである。しかし、configureスクリプト自体はautoconfというツールから生成されているのだが、autoconfは最新版のリリースでも何年も前のことで、これを直したからといってすぐにまともなconfigureスクリプトが出回ることは期待しづらかった。それに世の中にすでに大量に出回っている生成済みconfigureスクリプトは変えられないので、autoconfに手を入れても実際の問題解決にはそれこそ10年といったレベルで時間がかかってしまう。したがってこれは「正しい」解決策かもしれないけど現実的とはいえなかった。
結局僕らが取ったもう1つの解決策は、僕らのリンカのヘルプメッセージに"(compatible with GNU linkers)"という文字列を入れることだった。この文字列は普通に人間が読んでもそれほどおかしくなくて、かつGNUという文字列を含んでいるのでconfigureにも優しい。美しくないし、その場しのぎで、誤った仮定を正すどころかむしろそれに迎合しているのだが、解決策としては現実的だ。
この問題を直しているときに僕が思い出したのは、WebブラウザのUser-Agent文字列のことだった。ブラウザが送るHTTPリクエストにはUser-Agentフィールドに「ブラウザを表す文字列」が含まれているのだが、ある先進的ブラウザがUser-Agentで何らかの名前を名乗り始めると、他のブラウザもそれにキャッチアップして同じ文字列をUser-Agentに加える、といったことが繰り返されて、2000年前後からはどのブラウザも"Mozilla/5.0"を名乗るという状態になっている。無数のWebサイトが、Mozilla/5.0ではないリクエストはWeb黎明期の古代のブラウザから来ているとみなして、すごくしょぼいページを送りつけてきていたので、どのブラウザもMozilla/5.0を名乗らざるを得なかったのだ。
僕らの直面した問題も、解決策も、ブラウザのUser-Agent問題と同じ構図だった。世の中に広まったプログラムの場合、この種の互換性問題は発生しがちだし、これをきれいに解決することは誰にもできないのだろう。その結果、ブラウザは今でも毎リクエストごとにほとんど無意味な"Mozilla/5.0"という文字列をサーバに送りつけているし、僕らのリンカも微妙に不自然な文字列をヘルプメッセージに含んでいる。ちょっと笑ってしまうような状況だが、これもリアルなソフトウェアエンジニアリングでは避けられない現実の一部なのだろう。