Chapter 17. Portability¶
Unix は異なるプロセッサー族間で移植された最初 (Version 6 Unix, 1976-77) の量産 OS だ。
移植性はつねに Unix の主な利点の一つだ。
ハードウェア依存のコードは Unix 社会では悪い形式とみなされ、OS カーネルのようなひじょうに特殊な場合にしか許容されない。
Unix プログラマーはソフトウェアを特定の腐りやすい技術に依存させることを避け、公開標準に大きく依存する傾向がある。移植性のために書くというこのような習慣が Unix の伝統に根付いているため、小さな単一用途のプロジェクトにさえ適用されている。これらの習慣は Unix 開発ツールキットの設計や、Unix の下で開発された Perl, Python, Tcl などのプログラミング言語にも二次的な影響を与えている。
移植性の直接的な利点は、Unix ソフトウェアが元のハードウェアプラットフォームより長生きするのが普通なので、ツールやアプリケーションを数年ごとに再発明する必要がないことだ。
移植性の間接的な利点は工法、インターフェイス、実装を単純化する効果がある。これはプロジェクトの成功確率を高め、生存過程の維持費を削減する。
Evolution of C¶
1973 年に開発された言語が、これほど変更を必要としなかったという事実は本当に驚くべきことであり、計算機科学・工学の他のどこにも類似点がない。
Chapter 4 では、C 言語が成功したのは、標準工法に近似した計算機上で、薄い接着剤の層として機能したからだと論じたのだった。
Early History of C¶
- BCPL (Basic Common Programming Language) → B インタープリター (1970) → C 言語 (1971)
- Dennis Ritchie の オリジナル C コンパイラー (DMR) は Unix version 5, 6, 7 あた りで貢献した。
- 最近の C 言語の実装のほとんどは Steven C. Johnson による PCC に依っている。 Version 7 でデビューし、System V と BSD 4.x の両リリースで DMR コンパイラーを 完全に置き換えた。
- 1976 年、Version 6 C は
typedef
,union
,unsigned int
を導入した。変数の 初期化と複合演算子のいくつかで構文を変更した。 - C 言語に関する記述の原典は The C Programming Language (1978) だ。白本と呼ば れている。
- Version 7 C は
enum
を導入した。struct
とunion
の値をコピー代入したり、 引数として渡したり、関数から返したりできる第一級オブジェクトとして扱うようにし た。
Steve Johnson 氏によるヘッダーファイルに関する陳述が興味深い:
Another major change in V7 was that Unix data structure declarations were now documented on header files, and included. Previous Unixes had actually printed the data structures (e.g., for directories) in the manual, from which people would copy it into their code. Needless to say, this was a major portability problem.
ANSI C Standard Draft:
const
/volatile
を追加。unsigned
型修飾子があらゆる型に適用できるように一般化。対称性のためsigned
を追加。auto
配列、struct
型、union
型それぞれに対して初期化構文を追加。- 関数プロトタイプを追加。
初期の C 言語における最も重要な変化は、ANSI C Standard Draft における定義参照への切り替えと関数プロトタイプの導入であった。
初期の詳しい履歴は設計者 Ritchie による The Development of the C Language (1993) を見ろ。
C Standards¶
C 標準開発はオリジナルの精神を守ることに細心の注意を払い、新しい機能を発明するよりも、既存のコンパイラーでの実験を追認することに重点を置いた保守的な過程であった。
言語への主な機能追加は 1986 年末までに決着し、この時点で K&R C と ANSI C を区別するのが一般的になった。
ANSI C の中核部分については早期に決着がついたが、標準ライブラリーの内容をめぐる論争は何年も続いた。
正式な規格が発行されたのは 1989 年末だ。この規格は 1990 年に ISO が主催者を引き継ぎ、ISO/IEC 9899:1990として再指定された。この規格で記述されている言語バージョンは一般に C89 または C90 として知られている。
著者のこぼれ話:
The first book on C and Unix portability practice, Portable C and Unix Systems Programming, was published in 1987 (I wrote it under a corporate pseudonym forced on me by my employers at the time).
K&R 本の第二版は 1988 年に出版された。
1993 年、ワイド文字と Unicode を支援。(ISO/IEC 9899-1:1994)
規格の改訂は 1993 年に始まった。1999 年、ISO によって ISO/IEC 9899 (C99) が採択された。きわめて多くの細かい機能が追加された。おそらくほとんどのプログラマーにとって最も重要なものは、C++ 同様、どの時点でも変数を宣言できるようになったことだろう。
C 言語の標準化は、標準化作業が開始される以前から、実用的でほぼ互換性のある実装が多種多様なシステム上で実行されていたという事実によって、大いに助けられてきた。そのため、どのような機能を標準に含めるべきかについて議論することが難しくなった。
Unix Standards¶
1973 年に Unix を C 言語で書き直したことで、移植や変更がかつてないほど容易になった。その結果、Unix の祖先は早くから OS の一族へと分岐していった。
実世界の Unix は公開標準に忠実に従っているので、開発者はたまたま使っている Unix の公式マニュアルのページよりも、POSIX 仕様のような文書に頼ることができる(頻繁にそうしている)。
新しいオープンソース の Unix (Linux) では、OS の機能が公開されている標準規格を仕様として設計されているのが一般的だ。
Standards and the Unix Wars¶
Unix 標準の開発の最初の動機は AT&T と Berkeley の開発線分裂 (Chapter 2) だった。
1979 年の Version 7 から 4.x BSD Unix が派生。
1980 年の 4.1 BSD リリース後、BSD 線は Unix の最先端としての評判を急速に高めていった。重要な追加物:
vi
- ジョブ制御機能:複数フォアグラウンドタスクとバックグラウンドタスクを管理する
- シグナル (Chapter 7) の改良
- TCP/IP ネットワーキング
1980 年に Berkeley は最も重要な追加機能である TCP/IP の開発契約を結んだものの、三年間、外部リリースとして出荷されることはなかった。
1981 年の System III がその後の AT&T の開発の基礎となった。
- System III は Version 7 の端末インターフェイスをよりきれいな形に作り直した
- Berkeley の機能拡張とは互換性が全くない
- シグナル (Chapter 7) の古さはそのまま
1983 年にリリースされた System V Release 1 では、vi(1) などの BSD ユーティリティがいくつか組み込まれた。
1983 年、この隔たりを埋める最初の試みが出された。
- どこから:Unix 使用者団体 UniForum
- 何を:UniForum 1983 Draft Standard (UDS 83)
1983 年、4.2BSD によって問題が悪化。4.2BSD は TCP/IP ネットワーキングを含む多くの新機能を追加し、祖先である Version 7 との微妙な非互換性を導入した。
1984 年の Bell 事業会社の分割と Unix 戦争 (Chapter 2) の始まりは問題を著しく複雑にした。Unix 戦争中、技術標準化は協力的な技術者たちが推し進めるものとなった。
1984 年に AT&T が System V Release 2 (SVr2) を発表したとき、標準の設定において使用者団体と協力することを宣言した。その年の UniForum Draft Standard の二回目の改訂は SVr2 の API をたどり、影響を与えた。
その後の Unix 標準も、BSD の機能が明らかに機能的に優れている分野を除いては System V に追従する傾向があった。例えば、現代の Unix 標準は、同じ機能に対する BSD のインタフェイースではなく、System V の端末制御を記述している。
1985 年、AT&T は System V Interface Definition をリリース。UDS 84 を組み込んだ SVr2 API のより正式な記述。後の改訂は System V リリース 3 と 4 のインターフェイスをだとった。SVID が POSIX 標準の基礎となり、最終的にシステムコールと C ライブラリーコールをめぐる Berkeley と AT&T の紛争の大半を AT&T に有利に傾けた。
1985 年、ネットワーク上でファイルシステムを共有するための競合する API 標準がリリースされた:
- Network File System (NFS)
- Remote File System (RFS)
Sun の NFSが優勢だったのは、単なる仕様だけでなくオープンソースのコードも他と共有することに積極的だったからだ。
この成功の教訓は RFS が純粋に論理的な根拠において優れたモデルであったからこそより強く指摘されるべきであった。
1985 年以降、Unix の標準化の主要な推進力は IEEE に移った。IEEE 1003 委員会は POSIX として一般に知られる一連の標準を開発した:
- システムコールと C ライブラリー機能
- シェルの詳細な語義と最小限のコマンド集合
- さまざまな非 C プログラミング言語用の詳細なバインディング
1990 年の最初のリリースに続き、1996 年に第二版(注:原文単数形)がリリースされた。ISO が ISO/IEC 9945 として採用した。
TODO
主な POSIX 標準一覧
- オリジナルの POSIX 標準は後のすべての Unix 標準化作業の基礎となった。
- POSIX 標準は POSIX Programmer's Guide のような参照文献を通して、今でも権威と して引用されている。
- 事実上の Unix API 標準はいまだに「POSIX プラスソケット」であり、それ以降の標準 は主に機能の追加と、珍しいエッジケースにおける適合性をより詳細に規定している。
1984 年、X/Open(後に Open Group と改名)結成。
- Unix 販売業者の共同団体
X/Open Portability Guides (XPGs) を開発した。
- XPGs は当初は POSIX 草稿と並行して発展し、1990 年以降は POSIX を組み込んで拡張 した。
- XPG は POSIX とは異なり、最先端での一般的な慣習をより志向した。
- 1987 年の XPG2 は基本的に System V の curses(3) である端末処理 API を追加した。
- 1990 年の XPG3 では X11 API が統合された。
- 1992 年の XPG4 では ANSI C 標準への完全準拠が義務付けられた。
- XPG2, 3, 4 は国際化のサポートに重点を置き、コードセットとメッセージカタログを 扱うための精巧な API を記述した。
1993 年、すべての主要 Unix 企業を含む 75 のシステムおよびソフトウェア販売業者が Unixの共通定義を開発するために X/Open を支持することを宣言し、Unix 戦争は終結した。
1999 年、X/Open は POSIX の活動を吸収した。
2001 年、X/Open は Single Unix Standard version 3 を発行した。Unix API 標準化のすべての糸が最終的に一つの束に撚られた。さまざまな Unix が共通の API に再集結したのだ。
The Ghost at the Victory Banquet¶
この取り組みを支援してきた旧来の Unix 販売業者はオープンソース Unix の新派から厳しい圧力を受けており、場合によっては専売 Unix を放棄しようとしていたのだ。
Single Unix Specification への適合性を検証するために必要な適合試験は高価だ。ほとんどのオープンソース OS の配布業者には手が届かない。いずれにせよ、Linux はとても速く変化するので、どのリリースも、おそらく認定を受ける頃には時代遅れになる。
Unix Standards in the Open-Source World¶
1990 年代半ば、オープンソース共同体は独自の標準化活動を開始した。特に Linux は POSIX のような Unix API 標準の可用性に依存する形でゼロから書かれていた。
新派の Unix にとっての問題はソースコードレベルでの API の互換性ではなかった。新たな問題はソースの互換性ではなく、バイナリーの互換性だった。
昔は、それぞれの Unix は事実上独自のハードウェアプラットフォーム上で動いていた。演算装置の命令集合や計算機構造には十分な種類があり、アプリケーションを動かすにはソースレベルで移植する必要があった。
しかしその後、ミニコンやワークステーションの販売業者は安価な 386 ベースのスーパーマイクロに席巻され、オープンソースの Unix が規則を変えた。業者はバイナリーを出荷する安定したプラットフォームがなくなった。
Linux 市場が統合されるにつれて、真の問題は時間の経過とともに変化する速度であることが明らかになった。API は安定していたが、システム管理ファイル、ユーティリティープログラム、ユーザーメールボックス名やシステムログファイルへのパスの接頭辞のようなものの予想される場所は変わり続けていた。
1993 年に始まった新派の Linux と BSD 共同体自身の中で開発された最初の標準化の努力は Filesystem Hierarchy Standard (FHS) だ。Linux Standards Base (LSB) に組み込まれ、期待されるサービスライブラリーと補助アプリケーションの集合も標準化された。この二つの標準は Free Standards Group の活動となり、2001 年までには旧式 Unix 業者の中で X/Open のような役割を担うようになった。
IETF and the RFC Standards Process¶
TODO
きちんと要約する。
Unix 共同体がインターネット技師の文化と融合したとき、Internet Engineering Task Force (IETF) の RFC 標準化過程が形成した考え方も受け継いだ。標準は実用的な試作実装の経験から生まれなければならないが、いったん標準になると、それに準拠しないコードは廃棄される。
これは標準が通常開発される方法ではない。これまで実装されたものとは似ても似つかない仕様が生み出された。さらに、その多くは現実的に実装できないほど要求が厳しかったり、混乱を引き起こすほど仕様が不十分だったりした。そしてそれらは販売業者に広められ、都合の悪いところでは無視された。
標準のバカらしさについて悪名高い例の一つが 1980 年代に一時 TCP/IP と争った Open Systems Interconnect ネットワーク規格だ。
その七レイヤーモデルは遠目には上品に見えたが、実際には複雑過ぎて実装不可能であることが判明した。似たような規格の怖い話は、この本くらいの大きさの本が一冊埋まるほどだ。
IETF の哲学は次のようにまとめられる:
We reject kings, presidents, and voting. We believe in rough consensus and running code. (Dave Clark, 1992)
最初に動く実装を求めるこの要求が、IETFを最悪の失態の範疇から救ってきた。 実際、その基準はより強い:
(RFC 2026) 仕様の候補は、インターネット標準として採用される前に、複数の独立した当事者によって実装され、正しい動作と相互運用性がテストされ、ますます要求の厳しくなる環境で利用されなければならない。
IETF 標準はすべて RFC (Requests for Comment) として一段階を超える。
- RFC の提出プロセスは意図的に非公式である。
- RFC は標準の提案、調査結果、後続の RFC のための哲学的基盤、あるいは冗談を言う ことさえある。冗談 RFC は即座に RFC になる唯一の種類の提出だ。
- 真剣な提案はパブリックコメントのために提出される Internet-Drafts として開始する。
個々の Internet-Draft には正式な地位はなく、起案者はいつでも変更したり撤回したりすることが可能だ。半年経っても RFC にならなかったものは削除される。
ソフトウェアの実装者や販売業者は Internet-Draft をあたかも仕様であるかのように準拠を主張することを特に禁じられている。Internet-Draft は通常、メーリングリストを通じて接続された作業集団において、議論の中心となるものだ。団長が適切と判断した場合、Internet-Draft は RFC 編者に提出され、番号が割り当てられる。Internet-Draft が RFC 番号とともに発行されると、実装者が準拠を主張できる仕様となる。
IETF の運営委員会 (Internet Engineering Steering Group) は成功した RFC を標準化路線に乗せる責任を負う。これは、RFC を Proposed Standard に指定することによって行われる。RFC がその資格を得るためには、仕様が安定し、査読を受け、インターネット共同体から大きな関心を集めていなければならない。
RFC が Proposed Standard に指定される前に、実装経験が非常に望ましいと考えられており、RFC がインターネット中核規格に触れたり、そうでなければ不安定になる可能性がある場合、IESG はそれを要求することがある。
IESG と IETF がより良い解決策を見出した場合、Proposed Standard は改訂される可能性があり、撤回される可能性もある。「混乱に敏感な環境」での使用は推奨されていない。航空管制システムや集中治療装置には使ってはいけない。
提案された規格について、作業可能で、完全で、独立に起草され、相互運用可能な実装が少なくとも二つ存在する場合、IESG はその規格を Draft Standard に昇格させることができる。Draft Standard への昇格は、その仕様が成熟し、有用であるという強い確信を示す、状況の大きな前進だ。
RFC の状況が Draft Standard に達した後は、仕様の論理バグに対処するためにしか変更されない。Draft Standard は「混乱に敏感な環境」での配備の準備が整うことが期待される。
Draft Standard が広く実装される試験に合格し、一般に受け入れられるようになると、インターネット標準として祝福されることがある。インターネット標準は RFC 番号を保持するが、STD シリーズの番号も取得する。この記事を書いている時点で RFC は 3000 以上あるが、STD は 60 しかない。
標準トラックに乗らない RFC には次のラベルを付ける:
- Experimental
- Informational: 冗談 RFC はこのラベルを付ける
- Historic: 廃止された標準
IETF の標準化工程は理論よりも実践による標準化を奨励し、標準規格が厳格な査読と試験を受けていることを保証するように設計されている。
Specifications as DNA, Code as RNA¶
PDP-7 時代でさえ、Unix プログラマーは古いコードを使い捨てにする傾向が強かった。これは Unix の伝統であるモジュール性を重視した結果であり、すべてを失うことなくシステムの小片を廃棄したり交換したりすることを容易にする。他のプログラミング文化ではパッチを当てようとする本能があるが、Unix の本能はスクラップアンドリビルドだ。
The IETF tradition reinforced this by teaching us to think of code as secondary to standards.
1980 年以降、この教訓の影響は Unix 共同体でますます広く感じられるようになった。したがって、ANSI/ISO C 標準 (1989-) は、欠点がないとは言わないが、その規模と重要性を持つ標準としては、例外的にきれいで実践的だ。
Single Unix Specification はより複雑な領域での三十年にわたる実験と失敗の化石を含んでおり、したがって ANSI C よりも厄介だ。しかし、その部品規格は非常に優れている。Linus Torvalds がそれを読んで Unix をゼロから作ることに成功したことがその強力な証拠だ。
公開された標準と IETF 工程を尊重することが Unix の文化に深く根付いている。
Unix プログラマーが公開されている標準を尊重するのは、他の種類の先験的な仕様に対してむしろ敵対的である可能性が高いからだ。
「ウォーターフォールモデル」がソフトウェア工学の文献で支持されなくなる頃には、 Unix プログラマーの間では何年も愚弄の対象になっていた。
Unix のベストプラクティスでは、プログラムの文書は Internet Proposed Standard と改訂の対象となる仕様として類比的に用いられる。
Keith Packard: Unix の開発では文書はプログラムより先に書かれるか、少なくともプログラムと一緒に書かれることが多い。
Keith Packard: X11 では X の核となる標準は X の最初のリリースの前に完成しており、それ以来基本的に変更されていない。異なる X システム間の互換性は、厳格な仕様主導の試験によってさらに改善されている。
Keith Packard: よく書かれた仕様書が存在することで、X のテストスイートの開発はべらぼうに容易になった。X 仕様の各陳述は実装を試験するためのコードに翻訳された。この工程で仕様に小さな矛盾がいくつか見つかったが、その結果、試供 X ライブラリーとサーバー内のコードパスのかなりの部分を網羅するテストスイートが完成した。
テストスイート生成の半自動化は大きな利点であることが証明された。X の実装は驚くべき水準の安定性と販売業者間の相互運用性を達成した。
Jim Gettys: X では仕様がどんなときでも支配してきた。(インクの価値がある仕様書なら)仕様書にも修正すべきバグがあるが、コードは仕様書よりもバグが多いのが普通だ。
システムの動作に関する議論は、仕様書に関して機能的な水準で行うことができ、実装の問題に過度に絡むことを避けることができるのだ。
Keith Packard:
- よく検討された仕様が開発の原動力となることで、バグか機能かという議論はほとんど なくなる。
- 仕様が正しく実装されていないシステムは壊れており、修正されるべきである。
標準優先の姿勢は末端使用者にも恩恵がある。1988 年に X11 用に書かれた GUI アプリケーションは今日の X 実装でも変更なく動いている。Unix 界ではこのような長寿は普通のことであり、standards-as-DNA の姿勢がその理由だ。
標準を尊重し、スクラップアンドリビルドを繰り返す Unix の文化は、標準のないコードベースの永続的なパッチ適用よりも、長期にわたってより良い相互運用性をもたらす傾向がある。Unix の最も重要な教訓の一つかもしれない。
Programming for Portability¶
ソフトウェアの移植性は、通常、横移動可能かという観点で語られる。しかし、時間経過による耐久性が同じくらい重要だ。移植性を考慮したプログラミングをする際には、そのソフトウェアが存続する可能性が最も高い環境の機能に基づいてソフトウェアを選択し、予見可能な将来に終息する可能性が高そうな技術を避けるように考えるべきだ。
Unix では、移植可能な API を指定する問題に二十年間取り組んできた結果、この問題はほぼ解決した。Single Unix Specification に記述されている機能は、今日、すべての現代的 Unix プラットフォームに存在する可能性が高く、将来的になくなる可能性はほとんどない。
Portability and Choice of Language¶
移植性を考慮したプログラミングにおける最初の問題は実装言語の選択だ。
Chapter 14 で調査した主要な言語はすべて、互換性のある実装が現代的 Unix で利用できるという意味で移植性が高い。移植性の問題は、コア言語ではなく、支援ライブラリーやローカル環境との統合度(特に、GUI の基盤を含む IPC と並行プロセス管理)に生じる傾向がある。
C Portability¶
C 言語は移植性が非常に高い。Unix の標準的な実装は GCC で、オープンソースの Unix だけでなく、最近の市販 Unix でも広く使われている。GCC は Windows や Classic Mac OS にも移植されているが、GUI への移植可能バインディングがないため、どちらの環境でも広く使われていない。
- 標準入出力
- 数学的操作
- 国際化
はすべての C 実装で移植可能だ。
- ファイル入出力
- シグナル
- プロセス制御
は Single Unix Specification 最新 API のみを使用するように注意すれば Unix 間で移植可能。
古い C コードは移植性のために #ifdef
文の茂みがあることが多い。これらは時代遅れか、古の専売 Unix の POSIX 以前の遺物インターフェイスを扱うものだ。
C 言語の移植性が問題になる点:
- プロセス間通信
- スレッド
- GUI
プロセス間通信とスレッドの移植性の問題については Chapter 7 を見ろ。
現実的な問題は GUI ツールキットだ。Chapter 15 に対処の指針がいくつかある。
移植性のある C コードの書き方については次が著者推薦書籍:
- Recommended C Style and Coding Standards
- The Practice of Programming
C++ Portability¶
Note
この節の記述は現代ではもう意味がないことを知っている。
C++ には C 言語と同じ OS 水準の移植性の問題がすべてあり、独自の問題もいくつかある。さらに、オープンソースの C++ 用 GNU コンパイラーはその存在のほとんどにおいて市販品に大きく遅れをとっている。2003 年半ばの時点では、事実上標準の基礎となる GCC と同等のものがまだ普遍的に配備されていない。
さらに、GNU C++ が最も近いとはいえ、C++ 言語の ISO 標準 C++99 を完全に実装している C++コンパイラーはまだない。
Shell Portability¶
シェルスクリプトの移植性は残念ながら低い。
純粋な bash(1) スクリプトはほとんどどこでも実行できる。問題なのは、シェルスクリプトの大部分が、移植性がはるかに低い他のコマンドやフィルターを多用していることであり、任意の対象機のツールキットにあることが保証されているわけでは決してない。
シェルで行われていたような重いプログラミングのほとんどは Perl, Python, Tcl のような第二世代のスクリプト言語に移ってしまった。
Perl Portability¶
Perl は移植性に優れている。純正 Perl には Tk ツールキットへ移植可能なバインディング集合まで用意されており、Unix, Mac OS, Windows にまたがる移植可能 GUI を支援している。Perl スクリプトは CPAN からのアドオン・ライブラリーを必要とすることが多い。これは、すべての Perl 実装に存在することを保証するものではない。
Note
特定の OS でしか動作しないアドオンに依存するという意味でなければ、問題にはならない。
Python Portability¶
Python は移植性に優れている。Perl 同様、Python は Tk ツールキットへの移植可能バインディング集合さえ搭載しており、Unix, Mac OS, Windows にまたがる移植可能 GUI を支援している。
Stock Python has a much richer standard library than does Perl and no equivalent of CPAN for programmers to rely on;
この指摘に関しては現代では PyPI という形で解決している。
Tcl Portability¶
Tcl の移植性は全体的に良好だが、プロジェクトの複雑さによって大きく異なる。
Tk は Tcl を第一言語とする。Python 同様に、コア言語の進化は比較的順調で、バージョンスキューの問題はほとんどない。残念なことに、Tcl は Perl 以上に拡張機能に依存しており、すべての実装に同梱される保証はない。
大規模なプロジェクトでは、拡張機能と(シェルプログラミングのように)対象機に存在するかどうかわからない外部コマンドの呼び出しの両方に大きく依存する傾向があるため、移植性は悪くなりがちだ。
Tcl は拡張機能の追加が容易であったためにかえって苦しんできたのかもしれない。ある拡張機能が標準配布パッケージの一部として興味を持ち始める頃には、その拡張機能にはいくつかの異なるバージョンが存在していた。
標準 Tcl にオブジェクト指向支援がない理由を、開発者である John Ousterhout 氏が多数派異教徒に取り囲まれる喩えで述べた。
Java Portability¶
Note
Java に関しては昔の話であるということを特に注意しろ。
Java の移植性は優れている。Java は write once, run everywhere を第一の目標として設計されたのだ。しかし、移植性は完璧ではない。 その困難の大部分は JDK 1.1 と古い AWT GUI ツールキット(一方)と、JDK 1.2 と新しい Swing GUI ツールキットとの間のバージョンスキューの問題だ。これらにはいくつかの重要な理由がある:
- Sun の AWT 設計が欠陥だらけで、Swing に置き換えなければならなかった。
- Microsoft が Windows での Java 開発を支援せず、C# に置き換えようとした。
- Internet Explorer のアプレットサポートを JDK 1.1 水準に留めるという Microsoft の決定。
- JDK 1.2 のオープンソース実装を不可能にし、展開を遅らせた Sun の使用許諾条項。
Java スレッド支持には移植性の問題がある。Java API は異なる OS が供給する分岐したプロセスモデル間の裂け目に橋を架けようとしている。しかし、うまくいっていない。
Emacs Lisp Portability¶
Emacs Lisp の移植性は素晴らしい。Emacs のインストールは頻繁にアップグレードされる傾向にあるため、著しく古い環境は稀だ。どこでも同じ拡張 Lisp が支援されており、事実上すべての拡張機能が Emacs 本体に同梱されている。
エディター本来の仕事については、何年も前に完全性を達成している。移植性の問題は、たいてい OS 機能の C 水準でのバインディングのクセが顕在化したものだ。このような問題が頻発するのは、メールモードにおける子プロセスの制御くらいだ。
Avoiding System Dependencies¶
主要なシステムファイルやディレクトリーのパスも移植性の問題の一部だ。この種の問題の典型は、メールを溜める場所は /var/spool/mail
か /var/mail
かということだ。
直接ファイルを開こうとせず、例えばそこから読み出すのであれば、POP3 や IMAP サーバーを通して問い合わせればいい。
ログファイルを手動で開いているのであれば、代わりに syslog(3) を使え。C ライブラリーを介した関数呼び出しインターフェイスの方がシステムファイルの場所よりも標準化されている。それを利用しろ。
コードにシステムファイルの場所を持たなければならない場合、ソースコードで配布するのかバイナリーで配布するのかによって、最適な選択肢は異なる。
- ソースコードで配布する場合、
autoconf
が役立つ。 - バイナリーで配布するのであれば、実行時にあちこちのパスを探らせ、「現地」の状況 に自動的に適応できるかどうかを確認するのがよい。
Tools for Portability¶
GNU autoconf(1) (Chapter 15) を使えば、移植性の問題を処理したり、システム構成の調査を行ったり、Makefile
を調整したりすることができる。
バイナリーで配布している場合でも、autoconf(1) は異なるプラットフォーム用にコードを条件づける問題を自動化するのに役立る。
Internationalization¶
コードの国際化=インターフェイスが複数の言語や異なる文字集合を容易に取り込めるようにソフトウェアを設計すること。
まず、メッセージベース(使用者から見えるテキストの意?)をコードから切り離せ。プログラムが使用するメッセージ文字列をコードから分離することで、コードを修正することなく他の言語のメッセージ辞書を差し込むように作れ。
この作業で最もよく知られているツールは GNU gettext で、国際化が必要な言語の文字列を特別なマクロでラップする必要がある。このマクロは、各文字列を言語ごとの辞書のキーとして使用する。そのような辞書がない場合、マクロは単に引数を返し、暗黙のうちにコード内の言語に引き下がる。
最近の Unix では Unicode 文字集合の 8 ビットシフト符号化である UTF-8 をアプリケーションで最初から話すようにしようという明確な傾向がある。UTF-8 の 下位 128 文字は ASCII で下位 256 文字は Latin-1 だ。つまり、この選択は最も広く使われている二つの文字集合と下位互換性がある。
正規表現における文字範囲に気をつけろ。正規表現 [a-z]
はそのスクリプトやプログラムが例えばドイツ語やフランス語に適用されている場合、必ずしもすべての小文字を捕らえるとは限らない。[[:lower:]]
や POSIX 標準に記述されているその他の記号の範囲を使う方が安全だ。
Portability, Open Standards, and Open Source¶
移植性には標準が必要だ。公開された標準のオープンソース実装は、開発者のコーディングの負担を大幅に軽減し、開発製品が他の人の労力から利益を得ることを可能にする。
例えば、デジタルカメラ用の画像キャプチャーソフトを設計しているとしよう。オープンソースには PNG を記述するための十分に試験された完全機能のライブラリーがある (Chapter 5) のに、画像ビットを保存するための独自のフォーマットを書いたり、市販コードを購入したりするだろうか。
オープンソースの(再)発明は標準化工程にも大きな影響を与えた。IETF は 1997 年ごろから、少なくとも一つのオープンソースの参照実装を持たない標準追従 RFC に対する抵抗感を強めている。
Henry Spencer: 裏を返せば、何かを標準にする最善の方法は、高品質のオープンソース実装を配布することである場合が多いということだ。
結局のところ、コードの移植性を確保するためにできる最も効果的な手段は、専売技術に依存しないことだ。オープンソースのコードなら、たとえ最先端のバージョンが自分のプロジェクトを壊すような形で変更されたとしても、前進する道がある。ソースコードにアクセスできるため、必要であれば新しいプラットフォームに移植することもできる。
インターネットがすべてを変えた。2003 年半ばには Linux やその他のオープンソース Unix が存在し、量産品質のソフトウェアを与えるプラットフォームとしての実力を示した。