読者です 読者をやめる 読者になる 読者になる

NO_WAIT

主にプログラミング

mrubyのRakefileに定義されたタスクの依存関係をjsTreeで表示

このところmrubyが気になっており、ソースを少しずつ読んでいます。 ビルドシステムがどうなっているか調べようとしたところ、なかなか複雑で すんなりとは読み進めることができませんでした。 特にmrbcが絡んだ部分のタスクの依存関係が読み取りにくかったため、 何らかの可視化手段を用いて簡略化できないかと考えました。 そこで、手軽さを求め、依存関係グラフをツリー構造に変換し、 JavaScriptライブラリjsTreeを用いてブラウザで表示してみました。

jsTreeによるツリー表示のサンプル

上のサンプルプログラムは"all"タスクが依存するタスク、それらが依存するタスクという風に再帰的に依存関係を表示しています。 いくつかのノードは別のノードへのリンクになっています。それらの多くはアーカイブファイル(*.a)のタスクです。 複数箇所から参照されるため、木構造で表現すると無駄が多くなります。そのため、アーカイブファイルなどはツリー構造の上部に配置して、参照個所からリンクさせています。 ノードを選択すると右側(下側)に追加情報が(あれば)表示されます。現状"Defining Actions"のみで、これはそのタスクが定義された ファイルと行番号を示します。

このツリー表示を眺めることで下記のような推測が(正しいかどうかは別にして)できました。

どうやらlibmruby.aが主要な実行形式の中核を成している

f:id:shinaisan:20151226222711p:plain

  • mrubymirbが依存しています。
  • mrbcは代わりにlibmruby_core.aに依存しています。これはlibmruby.aにあるmrbgemsが含まれていないアーカイブのようです。
  • mrbtestlibmruby.alibmruby_core.aの両方に依存しています(なぜかはわかりません)。

どうやらmrbcgem_init.cを生成しているようだ

f:id:shinaisan:20151226222654p:plain

例えば、上図において、mrbgems/mruby-sprintf/gem_init.ogem_init.cからコンパイルされると思われますが、 このCソースはmrbcmrblib下のRubyスクリプトに依存しています。 上図は「依存している」ということ以外は何も示しませんが、mrbcRubyスクリプトからCソースを生成しているのではないかとの 推測が可能です。 実際、gem_init.cを見てみると、中間コード列とおぼしき初期化コードが目につきます。 tasks/mrbgem_spec.rakeへの依存関係も見えますが、これが何のためにあるのかは不明です。

test/tにあるRubyスクリプトはCに変換されmrbtestの一部となる

f:id:shinaisan:20151226222724p:plain

はじめtest/tディレクトリにあるRubyソースが何なのかわかりませんでした。 mrbtestmrbcとともにそれらに依存しているのを見て、 どうやらmrbcで変換され、mrbtestに組込まれるのではと推測します。

データ変換とjsTreeによる表示について

Rubyによるデータ変換

下記Rubyスクリプトにより、mrubyソースツリーからタスクの依存関係を読み取り、JSONファイルに書き出します。 Task Dependency Generator for the Build System of mruby · GitHub

  1. 上記スクリプトRakefileに定義された全タスクを読み取ろうとします。MiniRake::Task.tasksでこれが可能と 思ったのですが、抜けがあり、MiniRake::Task[name]の形式で参照されないと現れないタスクがありました。 そのため、allタスクからはじめて、prerequisitesパラメータを参照しつつ再帰的にタスクを収集します。
  2. クラスJstree::Graphインスタンス化されると、上記のように集めたタスクをノードとして持つグラフとなりますが、 ノード間の辺はありません。インスタンスに対してconstructメソッドを呼び出すことで辺を登録していきます。
  3. グラフのto_jsonメソッドが呼ばれ、依存関係がJSON形式に変換されます。

jsTreeによる表示

上記Rubyスクリプトvar json = [ ... ];の形式でJSONデータが生成されます。 ツリー表示用のJavaScriptライブラリであるjsTreeに対して下記のように指定するだけで表示できます。

    $("#tree-view-area").jstree({
        "core": {
            "data": json
        }
    });

下記JSONデータの抜粋にはtextchildrenパラメータがあり、これらが jsTreeによる表示の際に参照されます。 infoなどの追加データがありますが、これはjsTreeの処理とは何の関係もありません。 任意のデータを追加するために混入させたものです。 こうしたユーザーデータはjsTreeのget_node APIでは 取得できませんが、originalというパラメータから元々のJSONデータが取れるようになっており、 そこからアクセスできるようになっています。 具体的にはjsTreeのイベントハンドラにおいて data.instance.get_node(data.selected[0]).original.infoのような形で 取り出すことが出来ます。

var json = [
    // snip
      {
        "text": "build/host/src/array.o",
        "info": { "defining_actions": ["tasks/mruby_build_commands.rake:104"] },
        "children": [
          {
            "text": "src/array.c",
            "info": { "defining_actions": [] },
            "children": []
          },
          {
            "text": "build_config.rb",
            "info": { "defining_actions": [] },
            "children": []
          }
        ]
      },
    // snip
];

参考