ブラウザコンポーネントと層化コンポーネント料理本

https://www.chromium.org/developers/design-documents/cookbook

ブラウザコンポーネント / 層化コンポーネント料理本

新規コンポーネントの誕生、熟練の機能を依存地獄から救い輝かしいコンポーネントになるまでのストーリー

概観

この文書はChromiumの機能をコンポーネントあるいは層化コンポーネントに変えるたのめガイドである。コンポーネントと層化コンポーネントのデザイン文書は前提とする。

動機と層化

我々は最初はいくつものトップレベルアプリをもっていた。そのうちの一つ(アンドロイド用Chrome)にとって必要な方法は//chrome内での#ifdefであった。しかし今後のトップレベルアプリではこれは避けたいと思った。

//components層はChromiumコードベースからビルドされる追加機能の場所である。もともとは//chromeにあったが、ここに移動されていろいろなトップレベルアプリ(例えば//android_webview)などから利用できるようになっている。

新規機能で多くのトップレベルアプリが利用するものはコンポーネントとして記述されるべきである。アンドロイド用Chrome以外のトップレベルアプリの機能で再利用するものは//componentsへ移動すべきである。このドキュメントは新規コンポーネントを作成するための料理本風のドキュメントであり、//chromeからどうやってコンポーネントに移動させるかについて記述する。以下の図は、Chromium階層の中でのコンポーネント位置づけを示している。この図は単純化されていることに注意すること。すべてのコンポーネントが//contentに依存できるわけではない。例えば、iOSで共有されるコンポーネントは//contentに依存できないので、その場合層化コンポネント形態にするしかない。

新規ブラウザコンポーネントの作成

新規コンポーネントの作成は一直線である。デザイン文書のルールに従い、//componentsディレクトリにある例を参考にする。以下はTL;DRバージョン:

  • xyzという名前のコンポーネントは//components/xyzに置く。
  • xyzのコードは名前空間xyz内に書く。
  • 厳格な//components/xyz/DEPSが存在し、コンポーネントが配下層のみに依存することを強制する。
    • 配下層モジュール(例えば、//base, //net, もしかしたら //content)
    • ツリーを構成するような他の//components、循環依存はだめ。
  • コンポーネントは配下層か他のコンポーネントのみに具体的に依存できる。組込者が利用するなら、そのためのクライアントインタフェースを提供すること。
  • iOSから利用されるコンポーネント、または//contentに依存するコンポーネントは層化コンポーネント形態でなければならない。
  • ブラウザプロセスのみから利用されるコンポーネントはそのディレクトリに直接含まれるコードを含む。
  • いくつものプロセスタイプで利用されるコンポーネントはプロセスごとにディレクトリをもつ、例えば:
    • //components/xyz/browser
    • //components/xyz/renderer
    • //components/xyz/common
  • コンポーネントはそのビルドで分離した動的ライブラリになることもできる。
  • Create, own and configure the component in the embedder layer, e.g. in //chrome/browser.

現存する機能を抽出してブラウザコンポーネントを作成

機能を抽出するための典型的ステップ:

  1. 抽出する機能を決めて、その機能のオーナーにそのことを伝える。
  2. コンポーネント化に関するデザイン文書を作成し、ファイルを分析して//chrome依存の分析(ユニットテストのコンポーネント化は次のパスでやることもできる)する。機能がiOSで利用される場合は//content依存の分析もする。
  3. ファイルの分析では以下のことを行う:コンポーネント化に含まれるハイレベルタスクのセットを生成(例えば、ProfileからFooServiceを取得する代わりにFooService依存性を直接注入);もしその機能がiOSで利用されるなら層化コンポーネントにすべきかを決める。また機能のコンポーネント化が他の//chrome機能よってブロックされるかを判断する(その場合はステップ2にもどりブロック機能を追加)
  4. ハイレベルタスクとバグツリーをマップする。バグツリーのリーフは個別CL粒度のバグである。バグツリーのすべてのバグに関連するホットリスト(例えば、Hostlist-Foo-Component)を与える、これによりリストの検索と分析が容易になる。
  5. コンポーネントを作成(最終的なDEPS構造でセットアップ)し、いくつかのリーフファイル(問題のある依存性が殆どないファイル)をその中に移動する。この最初のパスで、コンポーネントのターゲットをスタティックライブラリにすることができることに注意。
  6. バグツリーを燃やす。問題のある依存性を除去しつつ、少しずつファイルをコンポーネントに追加する。
  7. すべてのプロダクションコードがコンポーネント化されたら、第2パスであるユニットテストのコンポーネント化を行う(プロダクションコードがコンポーネント化されたあとなら、一直線にできる)。
  8. コンポーネントのすべてのコードをコンポーネントの名前空間に入れる。(例えば//components/fooにあるコードは名前空間"foo"に入れる)
  9. もし望むなら、.gypiファイルを修正してコンポーネントとしてビルドするようにexport宣言を追加する。
    • 他のコンポーネントの実例を見る。例えば、//components/webdata/common/webdata_export.hとその利用。

ファイル移動時にインクルードガートとファイルへの参照(.gyp(i)ファイルで参照される他のソースの#includesと#imports)を更新するツールがある;src/tools/git/{move_source_file.py, mass-rename.py}を参照。Pythonの正規表現を使ってコードベースに渡っての検索と置換を行うツールもある;src/tools/git/mffr.pyを参照。最後に、"git cl format"を使ってリファクタリングの際の再フォーマットを簡単にすることを強く薦める。

Recipes for Breaking //content Dependencies

<途中>

Page last modified on July 27, 2018, at 01:38 PM
Powered by PmWiki