Doxygenの内部構造

Doxygenの内部構造

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

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

データフローの概要

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

設定パーサー

プロジェクトの設定を制御する設定ファイルが解析され、設定はシングルトンクラス Config (src/config.h 内) に格納されます。パーサー自体は flex を使用して記述されており、src/config.l にあります。このパーサーは Doxywizard でも直接使用されるため、別のライブラリにまとめられています。

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

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

Cプリプロセッサ

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

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

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

プリプロセッサは flex を使用して記述されており、src/pre.l にあります。条件ブロック (#if) の場合、定数式の評価が必要です。このために、yacc ベースのパーサーが使用されており、src/constexp.y および src/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つのパーサーがあります。状態変数 insideIDL および insideJava は、言語固有の選択のために一部の場所で使用されます。

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

将来のバージョンでの改善の可能性

  • 1つの大きなスキャナーの代わりに、言語ごとに1つのスキャナー/パーサーを使用する。
  • ドキュメントブロックの最初のパスの解析を別のモジュールに移動する。
  • define を解析する(これらは現在プリプロセッサによって収集され、言語パーサーによって無視されます)。

データオーガナイザー

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

各ステップには、言語解析中に構築されたエントリのツリーを操作する関数が 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 コードからルールマッチング/デバッグ情報を取得するもう1つの方法は、makeLEX_FLAGS を設定することです (make LEX_FLAGS=-d)。

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

-d lex を付けて Doxygen を実行すると、どの 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 設定ファイルの設定とは異なる設定を伝達する必要がある場合、Doxygen コマンドを -x オプションと設定ファイルの名前 (デフォルトは Doxyfile) を付けて実行できます。出力は、デフォルト設定ではない設定のリストになります (Doxyfile 形式)。または、-x_noenv も可能で、これは -x オプションと同じですが、環境変数と CMake タイプ置換変数を置き換えない点が異なります。

インデックスに戻る。