增量项目构建器

增量项目构建器是以特定方式处理项目中的资源的一种对象。通常使用增量项目构建器来对资源应用转换,以便生成另一种类型的资源或工件。

插件为平台添加增量项目构建器,以实现专门的资源转换。例如,“Java 开发工具”(JDT)定义增量项目构建器,每当在 Java 项目中添加或修改文件时,该构建器就会将 Java 源文件编译成类文件。它还会跟踪从属文件并在必要时重新编译它们。

从 API 的角度来看,平台定义两种基本类型的构建:

增量式构建基于资源更改变化。变化反映自构建器上次构建项目后所有资源更改的净效应。此变化类似于在资源更改事件内部使用的变化。

用户会定期清除项目以强制在下次对该项目执行增量式构建时重建完整的项目。清除项目将除去构建信息,如问题标记和类文件。

通过示例最容易了解构建器。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>

扩展点中标识的必须扩展平台类 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 时,Y 的变化是通过调用 getDelta(Y) 来获得的。为了确保平台可以提供这种变化,X 的构建器必须已经通过从先前的 build() 调用返回包含 Y 的数组来声明 X 与 Y 之间的从属关系。如果构建器没有依赖项,则它可能只返回 null。有关更进一步的信息,请参阅 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());
   }

访问过程将继续,直到遍历了整个资源变化树为止。更改的特定性质类似于实现资源更改侦听器中所描述的那样。它们的一个重要区别是,对于增量项目构建器,使用基于特定项目的资源变化,而不是整个工作空间。

构建之前清除

工作台允许用户在启动构建之前清除项目或项目集合。此功能部件允许用户仅针对特定项目从头开始进行重建。构建器应实现此方法以清除项目中的所有问题标记和派生的资源。

将增量项目构建器与项目进行关联

为了让构建器可供给定项目使用,必须将它包含在该项目的构建规范中。项目的构建规范是在构建项目时要按顺序运行的命令的列表。每个命令命名一个增量项目构建器。

注意:构建命令中的构建器名称是构建器扩展的标准标识。通过将插件标识与 plugin.xml 文件中的简单扩展标识组合在一起,可以创建扩展的标准标识。例如,在插件“com.example.builders”中具有简单扩展标识“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);
   }

通常只在创建项目时配置一次项目的构建器。