Doxygenの内部構造

Doxygenの内部構造

このセクションはまだ作成中です!

以下の図は、Doxygenがソースファイルをどのように処理するかを示しています。

データフローの概要

以下のセクションでは、上記のステップについて詳しく説明します。

設定パーサー

プロジェクトの設定を制御する設定ファイルはパースされ、設定はsrc/config.hにあるシングルトンクラスConfigに保存されます。パーサー自体はflexを使用して書かれており、src/config.lで見つけることができます。このパーサーはDoxywizardによっても直接使用されるため、別のライブラリに配置されています。

各設定オプションには、StringListEnumIntBoolの5つの可能な型があります。これらのオプションの値は、グローバル関数Config_getXXX()を通じて利用できます。ここでXXXはオプションの型です。これらの関数の引数は、設定ファイルに表示されるオプションの名前を示す文字列です。例えば、Config_getBool(GENERATE_TESTLIST)は、設定ファイルでテストリストが有効になっている場合にTRUEとなるブール値への参照を返します。

src/doxygen.cppにある関数readConfiguration()は、コマンドラインオプションを読み込み、その後設定パーサーを呼び出します。

Cプリプロセッサ

設定ファイルに記載されている入力ファイルは、(デフォルトで)Cプリプロセッサに供給されます(利用可能な場合は、ユーザー定義フィルターを介してパイプされた後)。

プリプロセッサの動作は、標準的なCプリプロセッサとは多少異なります。デフォルトではマクロ展開を行いませんが、すべてのマクロを展開するように設定できます。典型的な使用法は、ユーザーが指定したマクロのセットのみを展開することです。これは、たとえば関数パラメータの型にマクロ名が現れることを可能にするためです。

もう一つの違いは、プリプロセッサが#includeに遭遇したときにコードをパースするものの、実際にはインクルードしないことです({ ... }ブロック内で見つかった#includeを除く)。この標準からの逸脱の理由は、同じ関数/クラスの複数の定義がDoxygenのパーサーに供給されるのを防ぐためです。例えば、すべてのソースファイルが共通のヘッダーファイルをインクルードする場合、クラスや型の定義(およびそれらのドキュメント)は各翻訳単位に存在することになります。

プリプロセッサはflexを使用して書かれており、src/pre.lで見つけることができます。条件ブロック(#if)の場合、定数式の評価が必要です。このため、yaccベースのパーサーが使用され、src/constexp.ysrc/constexp.lで見つけることができます。

プリプロセッサはsrc/pre.hで宣言されたpreprocessFile()関数を使用して各ファイルに対して呼び出され、前処理された結果を文字バッファに追加します。文字バッファのフォーマットは次のとおりです。

0x06 file name 1
0x06 preprocessed contents of file 1
...
0x06 file name n
0x06 preprocessed contents of file n

言語パーサー

前処理された入力バッファは言語パーサーに供給されます。これはflexを使用して大きな状態機械として実装されています。src/scanner.lファイルで見つけることができます。すべての言語(C/C++/Java/IDL)に対して1つのパーサーがあります。状態変数insideIDLinsideJavaは、言語固有の選択のために一部の場所で使用されます。

パーサーのタスクは、入力バッファをエントリのツリー(基本的には抽象構文ツリー)に変換することです。エントリはsrc/entry.hで定義されており、緩やかに構造化された情報の塊です。最も重要なフィールドはsectionで、エントリに含まれる情報の種類を指定します。

将来のバージョンでの改善点

  • 1つの大きなスキャナーではなく、言語ごとに1つのスキャナー/パーサーを使用する。
  • ドキュメンテーションブロックの最初のパスのパースを別のモジュールに移動する。
  • 定義をパースする(これらは現在プリプロセッサによって収集され、言語パーサーによって無視されている)。

データオーガナイザー

このステップは、抽出されたクラス、ファイル、名前空間、変数、関数、パッケージ、ページ、グループの辞書を構築する多くの小さなステップで構成されています。辞書を構築するだけでなく、このステップでは抽出されたエンティティ間の関係(継承関係など)も計算されます。

各ステップにはsrc/doxygen.cppで定義された関数があり、言語パース中に構築されたエントリのツリーに対して操作を行います。詳細については、parseInput()の「情報の収集」部分を参照してください。

このステップの結果は、src/doxygen.hで定義されているDoxygenの「名前空間」で見つけることができる多数の辞書です。これらの辞書のほとんどの要素は、クラスDefinitionから派生しています。例えば、クラスMemberDefはメンバーに関するすべての情報を保持します。このようなクラスのインスタンスは、ファイル(クラスFileDef)、クラス(クラスClassDef)、名前空間(クラスNamespaceDef)、グループ(クラスGroupDef)、またはJavaパッケージ(クラスPackageDef)の一部になることができます。

タグファイルパーサー

設定ファイルでタグファイルが指定されている場合、これらはsrc/tagreader.cppにあるSAXベースのXMLパーサーによってパースされます。タグファイルのパース結果は、エントリツリーへのEntryオブジェクトの挿入です。Entry::tagInfoフィールドは、エントリを外部としてマークするために使用され、タグファイルに関する情報を保持します。

ドキュメンテーションパーサー

特殊なコメントブロックは、それらがドキュメント化するエンティティ内に文字列として保存されます。概要記述用の文字列と詳細記述用の文字列があります。ドキュメンテーションパーサーはこれらの文字列を読み込み、その中に見つかるコマンドを実行します(これはドキュメンテーションのパースにおける第2パスです)。結果は直接出力ジェネレーターに書き込まれます。

パーサーはC++で書かれており、src/docparser.cppで見つけることができます。パーサーが処理するトークンはsrc/doctokenizer.lから供給されます。コメントブロック内で見つかったコードフラグメントはソースパーサーに渡されます。

ドキュメンテーションパーサーの主なエントリポイントは、src/docparser.hで宣言されているvalidatingParseDoc()です。特殊なコマンドを含むシンプルなテキストにはvalidatingParseText()が使用されます。

ソースパーサー

ソースブラウジングが有効になっている場合、またはドキュメンテーション内にコードフラグメントが見つかった場合、ソースパーサーが呼び出されます。

コードパーサーは、パースしたソースコードとドキュメント化されたエンティティを相互参照しようとします。また、ソースの構文ハイライトも行います。出力は直接出力ジェネレーターに書き込まれます。

コードパーサーの主なエントリポイントは、src/code.hで宣言されているparseCode()です。

出力ジェネレーター

データが収集され相互参照された後、Doxygenは様々な形式で出力を生成します。これには抽象クラスOutputGeneratorが提供するメソッドを使用します。複数の形式で一度に出力を生成するために、代わりにOutputListのメソッドが呼び出されます。このクラスは具体的な出力ジェネレーターのリストを保持し、呼び出された各メソッドはリスト内のすべてのジェネレーターに委譲されます。

各具体的な出力ジェネレーターへの出力にわずかな差異を許容するため、特定のジェネレーターを一時的に無効にすることが可能です。OutputListクラスには、これのための様々なdisable()およびenable()メソッドが含まれています。OutputList::pushGeneratorState()およびOutputList::popGeneratorState()メソッドは、有効/無効な出力ジェネレーターのセットをスタックに一時的に保存するために使用されます。

XMLは収集されたデータ構造から直接生成されます。将来的にはXMLが中間言語(IL)として使用される予定です。出力ジェネレーターはその後、このILを開始点として特定の出力形式を生成します。ILを持つ利点は、様々な言語で書かれた様々な独立して開発されたツールがXML出力から情報を抽出できることです。可能なツールは次のとおりです。

  • インタラクティブなソースブラウザ
  • クラス図ジェネレーター
  • コードメトリクスの計算。

デバッグ

Doxygenは多くのflexコードを使用しているため、flexがどのように機能するか(これについてはmanページを読むべきです)、およびflexが何らかの入力をパースしているときに何をしているのかを理解することが重要です。幸いなことに、flex-dオプションで使用すると、どのルールが一致したかが出力されます。これにより、特定の入力フラグメントで何が起こっているかを追跡することが非常に簡単になります。

特定のflexファイルのデバッグ情報を切り替えやすくするために、Makefileの正しい行から-dを自動的に追加または削除する以下のperlスクリプトを作成しました。

#!/usr/bin/perl

$file = shift @ARGV;
print "Toggle debugging mode for $file\n";
if (!-e "../src/${file}.l")
{
  print STDERR "Error: file ../src/${file}.l does not exist!\n";
  exit 1;
}
system("touch ../src/${file}.l");
unless (rename "src/CMakeFiles/doxymain.dir/build.make","src/CMakeFiles/doxymain.dir/build.make.old") {
  print STDERR "Error: cannot rename src/CMakeFiles/doxymain.dir/build.make!\n";
  exit 1;
}
if (open(F,"<src/CMakeFiles/doxymain.dir/build.make.old")) {
  unless (open(G,">src/CMakeFiles/doxymain.dir/build.make")) {
    print STDERR "Error: opening file build.make for writing\n";
    exit 1;
  }
  print "Processing build.make...\n";
  while (<F>) {
    if ( s/flex \$\‍(LEX_FLAGS\‍) -d(.*) ${file}.l/flex \$(LEX_FLAGS)$1 ${file}.l/ ) {
      print "Disabling debug info for $file\n";
    }
    elsif ( s/flex \$\‍(LEX_FLAGS\‍)(.*) ${file}.l$/flex \$(LEX_FLAGS) -d$1 ${file}.l/ ) {
      print "Enabling debug info for $file.l\n";
    }
    print G "$_";
  }
  close F;
  unlink "src/CMakeFiles/doxymain.dir/build.make.old";
}
else {
  print STDERR "Warning file src/CMakeFiles/doxymain.dir/build.make does not exist!\n";
}

# touch the file
$now = time;
utime $now, $now, $file;

flexコードからルールの一致/デバッグ情報を取得する別の方法は、makeLEX_FLAGSを設定することです(make LEX_FLAGS=-d)。

デフォルトでは、Doxygenのデバッグバージョン(つまり、CMake設定-DCMAKE_BUILD_TYPE=Debugで作成された実行可能ファイル)には、すべてのflex codefileflexデバッグ情報が自動的に含まれます。

Doxygenを-d lexで実行すると、どのflex codefileが使用されているかに関する情報が得られます。flexデバッグオプションでコンパイルされたflexパーサーの情報を表示するには、Doxygenを実行するときに-d lex:<flex codefile>を指定する必要があります。

lexパースに関する情報はstderrに出力され、他のデバッグ出力は-d stderrを使用しない限りデフォルトでstdoutに出力されることに注意してください。

テスト

Doxygenには、一部のコードの整合性をテストするための少数のテストセットがあります。テストは、コマンドmake testsを使用して実行できます。1つまたは少数のテストのみが必要な場合は、テスト実行時に変数TEST_FLAGSを設定できます。例えば、make TEST_FLAGS="--id 5" tests、または複数のテストの場合はmake TEST_FLAGS="--id 5 --id 7" testsです。すべての可能性については、コマンドmake TEST_FLAGS="--help" testsを実行してください。TEST_FLAGSを環境変数として指定することも可能です(Visual Studioプロジェクト経由でのテストにも機能します)。例えば、setenv TEST_FLAGS "--id 5 --id 7"としてからmake testsを実行します。

Doxyfileの差異

例えばフォーラムを通じて、標準のDoxygen設定ファイルの設定とは異なる設定を伝える必要がある場合、-xオプションと設定ファイルの名前(デフォルトはDoxyfile)を指定してDoxygenコマンドを実行できます。出力は、デフォルトではない設定のリスト(Doxyfile形式)になります。あるいは、-xオプションと同じですが、環境変数やCMake型置換変数を置換しない-x_noenvも可能です。

インデックスに戻る。