kagamihogeの日記

kagamihogeの日記です。

JEP 201: Modular Source Codeをテキトーに訳した

http://openjdk.java.net/jeps/201 をテキトーに訳した。

下記Updated 2014/08/11 18:40にあるとおりホットトピックなので、最新の情報については原文などを当たって頂くようお願いします。また、ビミョーな翻訳部分についても、原文を参照して頂けると幸いです。

JEP 201: Modular Source Code

Author   Mark Reinhold
Owner   Alan Bateman
Created 2014/07/22 14:08
Updated 2014/08/11 18:40
Type    Feature
Status  Proposed to Target
Scope   Implementation
Discussion  jigsaw dash dev at openjdk dot java dot net
Effort  L
Duration    L
Priority    1
Reviewed by Alan Bateman, Alex Buckley, Mandy Chung, Paul Sandoz
Endorsed by Brian Goetz
Release 9
Issue   8051619
Blocks  JEP 200: The Modular JDK
Relates to  8049382: Script to aid porting of patches between JDK 9 and JDK 8u

Summary

JDKソースコードをモジュールへ再編成し、モジュールをコンパイルするためのビルドシステムを改良し、ビルド時のモジュール境界を強化します。

Non-Goals

本JEPではJREJDKのバイナリイメージの構造は変更しませんし、モジュールシステムの導入も行いません。そうした作業は関連JEPと適切なJSRが担当します。

本JEPはJDK用の新しいソースコードレイアウトを定義します。このレイアウトはJDK外で利用可能ですが、本JEPは広範に受け入れられるユニバーサルなモジュールソースコードレイアウトの設計は目的ではありません。

Motivation

Project Jigsawの目的は、Java SE Platform用の標準モジュールシステムの設計と実装、および、そのモジュールシステムのJava SE PlatformとJDK自体への適用を行うことです。その主要目的は、プラットフォーム実装のスモールデバイスへのスケールダウン簡易化・セキュリティとメンテナンス性の改善・アプリケーションパフォーマンスの改善可能化・大規模プログラミングにおけるより良いツールの開発者への提供、です。

本JEPはProject Jigsawの初期フェーズの一部です。これ以降のJEPで、JREJDKイメージのモジュール化、それからモジュールシステムの導入、を行います。

初期段階でソースコードを再編成する動機は以下の通りです。

  1. JDK開発者がシステムのモジュール構造に慣れるための機会を提供する。
  2. モジュールシステムの導入前であっても、ビルド時のモジュール境界を設定することで構造を保持する。
  3. "乱雑な(shuffle)"現行の非モジュールソースコードをモジュール形式にすることなくProject Jigsawの開発を続行可能にする。

Description

Current scheme

今日のJDKソースコードの大半は、1997年に遡るスキーマでおおむね組織化されています。略記形式では以下になります。

src/{share,$OS}/{classes,native}/$PACKAGE/*.{java,c,h,cpp,hpp}
  • shareディレクトリには、共有でクロスプラットフォームのコードが含まれます。
  • $OSディレクトリには、OS固有のコードが含まれており、 $OSsolaris, windows, などの内の一つです。
  • classesディレクトリには、Javaソースファイルとリソースファイルが含まれます。
  • nativeディレクトリには、CかC++ソースファイルが含まれます。
  • $PACKAGEは関連するJava APIパッケージ名で、ピリオドはスラッシュで置換されます。

例をあげると、jdkリポジトリjava.lang.Objectクラスのソースコードは二つのファイルが存在し、一つはJavaでもう一つはCです。

src/share/classes/java/lang/Object.java
          native/java/lang/Object.c

他の例では、パッケージプライベートなjava.lang.ProcessImplProcessEnvironmentクラスのソースコードはOS固有であり、Unix系システム向けに三つのファイルが含まれています。

src/solaris/classes/java/lang/ProcessImpl.java
                              ProcessEnvironment.java
            native/java/lang/ProcessEnvironment_md.c

(上記のコードはすべてのUnix派生システムに関連するけれども、二階層目のディレクトリがsolarisになっています。この詳細については以降の文章を参照してください。)

以下に示すsrc/{share,$OS}下の一部のディレクトリはこの構造にマッチしません。

Directory                     Content
--------------------------    --------------------------
src/{share,$OS}/back          JDWP back end
                bin           Java launcher
                instrument    Instrumentation support
                javavm        Exported JVM include files
                lib           Files for $JAVA_HOME/lib
                transport     JDWP transports

New scheme

メンテナンスを容易にするために、JDKのモジュール化はソースコードの完全な再編を行う貴重な機会を提供します。我々はhotspotを除くJDK大森林*1の全リポジトリを以下のスキーマで実装することを提案します。以下は簡略化した記述です。

src/$MODULE/{share,$OS}/classes/$PACKAGE/*.java
                        native/include/*.{h,hpp}
                               $LIBRARY/*.{c,cpp}
                        conf/*
  • $MODULEはモジュール名です(例java.base)。
  • shareディレクトリには、以前同様、共有、クラスプラットフォームのコードが含まれます。
  • $OSディレクトリには、以前同様、OS固有のコードが含まれており、 $OSunix, windows, などの内の一つです。
  • classesディレクトリには、以前同様、API$PACKAGE階層を反映するディレクトリツリーに編成されたJavaソースファイルとリソースファイルが含まれます。
  • nativeディレクトリには、以前同様、CやC++のソースファイルが含まれますが、異なる点として、
    • includeディレクトリには、外部で使用するためにエクスポートするCやC++のヘッダーファイルが含まれます。
    • CやC++のソースファイルは$LIBRARYディレクトリに配置され、その名前は共有ライブラリ名になるか、コンパイルされたコードがリンクされるDLL(例:libjavalibawt)です。
  • confディレクトリには、エンドユーザが編集するための設定ファイルが含まれます。(例:net.propertiesなど)

前の例を書き直すために、java.lang.Objectクラスのソースコードは以下のようなレイアウトになります。

src/java.base/share/classes/java/lang/Object.java
                    native/libjava/Object.c

パッケージプライベートなjava.lang.ProcessImplProcessEnvironmentクラスのソースコードは以下のようなレイアウトになります。

src/java.base/unix/classes/java/lang/ProcessImpl.java
                                     ProcessEnvironment.java
                   native/libjava/ProcessEnvironment_md.c

(我々はこの機会に、最終的にはsolarisunixにリネームしておきたい)

現在の構造にマッチしないsrc/{share,$OS}ディレクトリ下にあるコンテンツは適切なモジュールに移動します。

Directory                     Module
--------------------------    --------------------------
src/{share,$OS}/back          jdk.jdwp.agent
                bin           java.base
                instrument    java.instrument
                javavm        java.base
                lib           $MODULE/{share,$OS}/conf
                transport     jdk.jdwp.agent

エンドユーザが編集することを目的としないlibディレクトリのファイルはリソースファイルに変換されます。

Build-system changes

ビルドシステムの修正は、一度に一つのリポジトリではなく、一度に一つのモジュールをコンパイルするようになります。また、モジュールグラフのトポロジカルソートの逆順に沿ってモジュールをコンパイルします。モジュールは直接間接問わず相互に依存しないため、可能であればコンカレントにコンパイルされます。

リポジトリでなくモジュール単位にコンパイルするもう一つの利点は、corba, jaxp, jaxwsリポジトリのコードで新しいJava言語の機能やAPIを利用可能になります。これらのリポジトリjdkリポジトリの前にコンパイルされるため、以前は禁止されていました。

中間ビルド*2コンパイルされるクラスはモジュールに分割されます。現在は下記のようです。

jdk/classes/*.class

改良後のビルドシステムは以下のような結果を生成します。

jdk/modules/$MODULE/*.class

上述のように、構造化イメージビルド(structure image builds)は変更しません。内容はわずかに異なります*3

モジュール境界は、ビルドシステムによって可能な限りビルド時に設定されます。モジュール境界が違反している場合、ビルドは失敗します。境界はJEP 200module.xmlで定義します。このファイルはソースコードと共にメンテナンスされます。このファイルに対する変更はProject Jigsawのコミッターのレビューが必要です。

Alternatives

ソースレイアウトのスキーマについては多数の代替案が存在します。

1. トップの{share,$OS}はそのままで、モジュールクラスファイルを含めるためのmodulesディレクトリを設ける。

src/{share,$OS}/modules/$MODULE/$PACKAGE/*.java
                native/include/*.{h,hpp}
                       $LIBRARY/*.{c,cpp}
                conf/*

2. 適切な$MODULEディレクトリ下にすべてを収めるが、{share,$OS}がトップなのはそのままにする。

src/{share,$OS}/$MODULE/classes/$PACKAGE/*.java
                        native/include/*.{h,hpp}
                               $LIBRARY/*.{c,cpp}
                        conf/*

3. この提案同様に$MODULEディレクトリ下に{share,$OS}を移動するが、中間classesディレクトリを削除してnativeconfディレクトリのプレフィクスにアンダースコアをつけます。これは純粋なJavaモジュールの良くあるケースを簡潔にするためです。

src/$MODULE/{share,$OS}/$PACKAGE/*.java
                        _native/include/*.{h,hpp}
                                $LIBRARY/*.{c,cpp}
                        _conf/*

4. 3番目のバリエーションで、{share,$OS}をトップにします。

src/{share,$OS}/$MODULE/$PACKAGE/*.java
                        _native/include/*.{h,hpp}
                                $LIBRARY/*.{c,cpp}
                        _conf/*

5. 3番目の更に別のバリエーションで、$OS固有コードの無い純粋なJavaモジュールのケースを更に簡潔にするために、{share,$OS}をより深い階層に下げます。

src/$MODULE/$PACKAGE/*.java
            _native/include/*.{h,hpp}
                    $LIBRARY/*.{c,cpp}
            _conf/*
            _$OS/$PACKAGE/*.java
                _native/include/*.{h,hpp}
                        $LIBRARY/*.{c,cpp}
                _conf/*

アンダースコアを含む3番から5番を我々は却下しており、その理由はナビゲートしにくく不親切なためです。1番と2番よりも現在の提案を選んでおり、その理由は現在のスキーマから最小の変更で単一ディレクトリ下にモジュールの全ソースコードを配置できるためです。現在のスキーマに依存するツールスクリプトは改修が必要ですが、少なくとも、各$MODULEディレクトリ下のJavaソースコード用の構造は以前と同じままです。

以下は我々が把握しているその他の問題です。

  • Javaソースファイルとは切り離しておきたいリソースファイル用のディレクトリを別途定義すべきでは? - いいえ。別途定義するメリットがありません。
  • 異なるリポジトリにまたがるコンテンツを持つモジュールは問題ではないか? - 厄介な話ですが、ビルドシステムはVPATHメカニズムによって解決が可能です。いずれ我々はクロスリポジトリ(cross-repo)モジュールを削除ないし削減するためにリポジトリの再編成に取り組みますが、それはこのJEPの範囲外です。
  • 複数のネイティブライブラリを含むモジュールが存在します。各モジュールが最大一つのネイティブライブラリを持つようにマージすべきですか? - いいえ。ある種の場合には、我々は一つのモジュールに複数のネイティブライブラリを持つ柔軟性が必要です。たとえば、AWTの"headless" vs. "headful"など。

Testing

上述したように、本JEPはJREJDKのバイナリイメージの構造は変更せず、コンテンツのマイナーチェンジのみ行います。よって、我々はビルドされたイメージを比較し、マイナーチェンジ検証用のテストを実行することで、変更内容を検証します。

Risks and Assumptions

我々は、変更を実装するための大量のファイルリネーム操作を扱うことができて、処理のすべての履歴情報を保持するために、Mercurialを使う予定です。予備テストではMercurialは十分でしたが、依然としてマイナーリスクがあり、それは新旧ファイル間の関係が正しく記録されない点です。古いロケーションのファイルの履歴がリポジトリに残ったままになる場合があります。これは発見が困難です。

新しいスキーマを使用するリポジトリに、直接古いスキーマを使用するように、リポジトリに対してパッチを適用するのは不可能です*4。逆も同様です。この点を緩和するために、我々は古いロケーションから新しいロケーションへファイル名を変換するスクリプトを開発する予定です。

Dependences

本JEPはProject Jigsaw用に存在するJEPのうち、二番目のものです。JEP 200JDKモジュール構造定義を包含していますが、明示的な依存はありません。

*1:JDK forest おそらく大量のコードが生い茂っている様の比喩なので大森林と訳しちゃった

*2: an intermediate (i.e., non-image)てどゆいみ?

*3:この辺良く分からんのでかなり適当な訳になっている

*4:against a repository using the old scheme directly to a repository using the new scheme 上手く訳せない……