Doxygenの内部構造

Doxygenの内部

このセクションはまだ作成中ですのでご注意ください!

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

データフローの概要

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

設定パーサー

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

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

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

Cプリプロセッサ

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

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

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

プリプロセッサはflexを使用して作成されており、src/pre.lにあります。条件ブロック(#if)の場合、定数式の評価が必要です。このため、src/constexp.yおよびsrc/constexp.lにあるyaccベースのパーサーが使用されます。

プリプロセッサは、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で定義されており、構造が緩やかな情報のブロブです。最も重要なフィールドはsectionで、エントリに含まれる情報の種類を指定します。

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

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

データオーガナイザー

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

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

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

タグファイルパーサー

設定ファイルにタグファイルが指定されている場合、これらはSAXベースのXMLパーサーによって解析されます。このパーサーはsrc/tagreader.cppにあります。タグファイルを解析した結果は、エントリツリーに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ファイルのデバッグ情報を簡単に切り替えるために、私は次のperlスクリプトを作成しました。これはMakefile:の正しい行に-dを自動的に追加または削除します。

#!/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 codefileflexデバッグ情報が自動的に含まれます。

Doxygenを-d lexで実行すると、どのflex codefileが使用されているかに関する情報が得られます。flexパーサーの情報を表示するには、Doxygenの実行時に-d lex:<flex codefile>を指定する必要があります。

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

テスト

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も可能です。

インデックスに戻る。