インクリメンタル・プロジェクト・ビルダー

インクリメンタル・プロジェクト・ビルダーは、プロジェクト内のリソースを特定の方法で操作するオブジェクトです。 インクリメンタル・プロジェクト・ビルダーは、変換をリソースに適用するために頻繁に使用され、 リソースまたは他の種類の成果物を生成します。

プラグインは、専門化されたリソース変換のインプリメントを行うために、プラットフォームにインクリメンタル・プロジェクト・ビルダーを追加します。 例えば、Java 開発ツール (JDT) は、 ファイルが Java プロジェクトに追加または変更される毎に、Java ソース・ファイルをクラス・ファイルに コンパイルするインクリメンタル・プロジェクト・ビルダーを定義します。 また、従属ファイルを追跡し、必要に応じて再コンパイルします。

API の観点から、プラットフォームは、ビルドの基本型を 2 つ定義します。

インクリメンタル・ビルドは、リソース変更デルタと共に取り込まれます。デルタは、ビルダーがプロジェクトを最後にビルドしてから行われたすべてのリソース変更による実質的な影響を反映しています。 このデルタは、リソース変更イベント内で使用されるものと同様です。

次回完了プロジェクトに対してインクリメンタル・ビルドが実行されたときに、このプロジェクトが再ビルドされるようにするために、ユーザーがプロジェクトを定期的にクリーンにすることができます。 プロジェクトをクリーンにすると、問題マーカーやクラス・ファイルなどのビルド情報が除去されます。

ビルダーを理解するには、例を見るのが一番です。 JDT Java コンパイラーは、 変更によって影響されるプロジェクト内のファイルを再コンパイルする Java インクリメンタル・プロジェクト・ビルダーによって駆動されます。フル・ビルドが起動されると (またはクリーン後のインクリメンタル・ビルド)、プロジェクト内のすべての .java ファイルがコンパイルされます。検出されたすべてのコンパイルの問題は、影響された .java ファイルに問題マーカーとして追加されます。インクリメンタル・ビルドが起動されると、 ビルダーは、リソース・デルタで記述されている、追加、変更または影響された .java ファイルを選択して再コンパイルし、 必要に応じて問題マーカーを更新します。適切でなくなった .class ファイルまたはマーカーは除去されます。

インクリメンタル・ビルドは、数百または数千のリソースを持ち、そのほとんどは指定時間に変更されることはないプロジェクトのパフォーマンスを著しく向上させます。

インクリメンタル・ビルドにおけるテクニカルな問題は、再ビルドする必要があるものを正確に判別するということです。例えば、Java ビルダーが保持する内部状態には、 依存性グラフおよび報告されたコンパイル時の問題のリストが含まれています。 この情報は、インクリメンタル・ビルド中に Java リソース内の変更に応じて再コンパイルする必要があるクラスの識別に使用されます。

ビルドにおける基本構造がプラットフォームに定義されているにもかかわらず、実際の作業はビルダー・コードで実行されます。 複雑なインクリメンタル・ビルダーをインプリメントするパターンは、インプリメンテーションがビルダー特有の設計に依存しているため、本書の解説の対象外としています。

ビルドの起動

ビルダーは、以下の方法のいずれかで明示的に起動可能です。

実際には、ワークベンチ・ユーザーは、「リソース・ナビゲーター」メニューで対応するコマンドを選択することによって、ビルドを起動します。

インクリメンタル・プロジェクト・ビルダーは、自動ビルド中にプラットフォームによって暗黙的にも起動されます。 自動ビルドが使用可能になっている場合には、ワークスペースが変更されるごとに自動ビルドが実行されます。

インクリメンタル・プロジェクト・ビルダーの定義

org.eclipse.core.resources.builders 拡張ポイントは、 プラットフォームへのインクリメンタル・プロジェクト・ビルダーの追加に使用します。次のマークアップは、 仮想プラグイン com.example.builders がインクリメンタル・プロジェクト・ビルダーをどのように追加するかを示しています。

   <extension
      id="mybuilder" name="My Sample Builder" point="org.eclipse.core.resources.builders">
      <builder
         <run
            class="com.example.builders.BuilderExample">
            <parameter name="optimize" value="true" />
            <parameter name="comment" value="Builder comment" />
         </run>
      </builder>
   </extension>

拡張ポイントで識別される class は、プラットフォーム・クラス IncrementalProjectBuilder を拡張する必要があります。

   public class BuilderExample extends IncrementalProjectBuilder {
      IProject[] build(int kind, Map args, IProgressMonitor monitor)
         throws CoreException {
         // add your build logic here
      return null;
      }
      protected void startupOnInitialize() {
         // add builder init logic here
      }
      protected void clean(IProgressMonitor monitor) {
         // add builder clean logic here
      }
   }

ビルド処理は、要求されたビルドの種類に関する情報を含む build() メソッドで始まります。 ビルドは、以下の値のいずれかとなります。

インクリメンタル・ビルドが要求されると、 最後のビルド以後にリソースで行われた変更を記述するために、リソース・デルタが提供されます。 次のコードの断片は、 build() メソッドをさらに洗練したものになっています。

      protected IProject[] build(int kind, Map args, IProgressMonitor monitor
         throws CoreException {
      if (kind == IncrementalProjectBuilder.FULL_BUILD) {
            fullBuild(monitor);
         } else {
         IResourceDelta delta = getDelta(getProject());
         if (delta == null) {
            fullBuild(monitor);
         } else {
            incrementalBuild(delta, monitor);
         }
      }
      return null;
   }

プロジェクト "X" をビルド時に、ビルダーが、他のプロジェクト "Y" における変更情報を必要とする場合があります。(例えば、X の Java クラスが Y で提供されるインターフェースを実装する場合。)  X をビルド中に、getDelta(Y) を呼び出すことにより Y のデルタが使用可能になります。  プラットフォームがこのようなデルタを提供できるようにするには、X のビルダーが、 直前の build() 呼び出しから Y を含んでいる配列を戻すことによって、X と Y の間の依存性を宣言する必要があります。 ビルダーに依存性がない場合、単にヌルが戻ります。詳しくは、IncrementalProjectBuilder を参照してください。

フル・ビルド

フル・ビルド要求の処理に必要なロジックは、プラグインに固有です。 ロジックには、プロジェクト内のすべてのリソースの訪問、またはプロジェクト間に依存性がある場合には、他のプロジェクトの検査が含まれる場合があります。 以下のコードの断片は、フル・ビルドのインプリメント方法を示しています。

   protected void fullBuild(final IProgressMonitor monitor) throws CoreException {
      try {
         getProject().accept(new MyBuildVisitor());
      } catch (CoreException e) { }
   }

ビルド・ビジターは、特定のリソースに対してビルドを実行 (および true を戻してすべての子リソースの訪問を継続) します。

   class MyBuildVisitor implements IResourceVisitor {
      public boolean visit(IResource res) {
         //build the specified resource.
         //return true to continue visiting children.
         return true;
      }
   }

訪問処理は、リソース・ツリーが完全に訪ねられるまで継続します。

インクリメンタル・ビルド

インクリメンタル・ビルドを実行すると、ビルダーは、完全なリソース・ツリーではなく、リソース変更デルタで作業することに注意してください。

   protected void incrementalBuild(IResourceDelta delta,
         IProgressMonitor monitor) throws CoreException {
      // the visitor does the work.
      delta.accept(new MyBuildDeltaVisitor());
   }

アクセス処理は、リソース・デルタ・ツリーが完全にたどられるまで継続します。 変更の特質は、リソース変更リスナーのインプリメントに記述されている特質と同様です。  1 つの重要な違いとして、インクリメンタル・プロジェクト・ビルダーでは、ワークスペース全体ではなく、特定のプロジェクトに基づくリソース・デルタで作業を行うということが挙げられます。

ビルドの前のクリーン

ワークベンチを使用すると、ユーザーは、ビルドを開始する前に、 プロジェクトまたはプロジェクトのセットをクリーンにすることができます。 ユーザーは、このフィーチャーを使用して、特定のプロジェクトのみに対して最初から再ビルドを強制することができます。ビルダーは、このメソッドをインプリメントして、プロジェクト内の問題マーカーおよび派生リソースをクリーンアップする必要があります。

インクリメンタル・プロジェクト・ビルダーとプロジェクトの関連付け

ビルダーを指定されたプロジェクト用に使用可能にするには、そのプロジェクトに対するビルド仕様にそのビルダーが組み込まれている必要があります。プロジェクトのビルド仕様は、プロジェクトのビルド時に、順次実行するコマンドのリストです。それぞれのコマンドは、 単一のインクリメンタル・プロジェクト・ビルダーを命名します。

注: ビルド・コマンドのビルダー名は、ビルダー拡張の完全修飾 ID です。拡張の完全修飾 ID は、plugin.xml ファイルのプラグイン ID と単純拡張 ID を結合して作成されます。例えば、プラグイン "com.example.builders" の単純拡張 ID "mybuilder" を使用するビルダーの名前は、"com.example.builders.mybuilder" になります。

以下のコードの断片は、新規ビルダーを、ビルダーの既存リスト内の最初のビルダーとして追加します。

   final String BUILDER_ID = "com.example.builders.mybuilder";
   IProjectDescription desc = project.getDescription();
   ICommand[] commands = desc.getBuildSpec();
   boolean found = false;

   for (int i = 0; i < commands.length; ++i) {
      if (commands[i].getBuilderName().equals(BUILDER_ID)) {
         found = true;
         break;
      }
   }
   if (!found) {
      //add builder to project
      ICommand command = desc.newCommand();
      command.setBuilderName(BUILDER_ID);
      ICommand[] newCommands = new ICommand[commands.length + 1];

      // Add it before other builders.
      System.arraycopy(commands, 0, newCommands, 1, commands.length);
      newCommands[0] = command;
      desc.setBuildSpec(newCommands);
      project.setDescription(desc, null);
   }

プロジェクト・ビルダーの構成は、通常はプロジェクトの作成時に、一度だけ実行されます。