Дополняющие компоновщики проектов

Дополняющий компоновщик проектов - это объект, который особым образом обрабатывает ресурсы проекта. Дополняющие компоновщики проектов часто используются для преобразования ресурса с целью создания ресурса или артефакта иного типа.

Модули добавляют в платформу дополняющие компоновщики проектов, которые позволяют осуществлять специализированные преобразования ресурсов. Например, средства разработки на языке Java (JDT) определяют дополняющий компоновщик проектов, компилирующий исходный файл Java в файл .class при каждом добавлении или изменении файла в проекте Java. Кроме того, он отслеживает зависимые файлы и при необходимости компилирует их.

Для API платформа определяет два основных типа компоновки:

Дополняющие компоновки начинаются на основе поправки изменения ресурсов. Поправка отражает суммарное воздействие всех изменений ресурсов с момента последней компиляции проекта. Эта поправка аналогична той, которая используется внутри событий изменения ресурсов.

Пользователь может периодически очищать проекты с целью их полной перекомпоновки во время следующей дополняющей компоновки. Очистка проекта удаляет информацию о компоновке, например, маркеры неисправностей и файлы классов.

Поясним работу компоновщиков на примере. Компилятор Java JDT запускается дополняющим компоновщиком проектов 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 {
         // алгоритм компоновки
      return null;
      }
      protected void startupOnInitialize() {
         // алгоритм инициализации компоновщика
      }
      protected void clean(IProgressMonitor monitor) {
         // алгоритм очистки компоновщика
      }
   }

Обработка компоновки начинается с метода 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",  например, если класс Java в проекте X использует интерфейс проекта Y.  При компоновке X метод getDelta(Y) позволяет определить поправку для проекта Y.  Для того чтобы платформа могла предоставить эти поправки, компоновщик проекта X должен объявить зависимость между проектами X и Y с помощью массива, содержащего Y из выполненного ранее вызова build(). Если зависимости отсутствуют, метод возвращает пустое значение. Дополнительную информацию см. в описании класса IncrementalProjectBuilder.

Полная компоновка

Алгоритм обработки запроса полной компоновки зависит от конкретного модуля. Он может предусматривать просмотр всех ресурсов данного проекта, а также ресурсов других проектов, если между ними существуют зависимости. Следующий фрагмент кода содержит пример реализации полной компоновки.

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

Компоновщик выполняет компоновку выбранного ресурса (и продолжает просматривать все дочерние ресурсы).

   class MyBuildVisitor implements IResourceVisitor {
      public boolean visit(IResource res) {
         //компоновка указанного ресурса.
         //возвращает true для продолжения просмотра дочерних объектов
         return true;
      }
   }

Процесс просмотра продолжается до тех пор, пока не будет пройдено все дерево ресурсов.

Дополняющая компоновка

При выполнении дополняющей компоновки компоновщик оперирует поправкой изменения ресурсов вместо полного дерева ресурсов.

   protected void incrementalBuild(IResourceDelta delta, 
         IProgressMonitor monitor) throws CoreException {
      // собственно алгоритм
      delta.accept(new MyBuildDeltaVisitor());
   }

Процесс просмотра продолжается до тех пор, пока не будет пройдено все дерево поправок. Изменения подобны описанным в разделе Создание получателей событий изменения ресурсов.  Основное отличие заключается в том, что дополняющие компоновщики проектов используют поправку ресурсов для конкретного проекта, а не для всей рабочей области.

Очистка перед компоновкой

С помощью рабочей среды пользователь может очистить проект или группу проектов перед началом компоновки. Эта функция позволяет выполнять перекомпоновку "с нуля" только для выбранных проектов. Компоновщики должны использовать данный метод для удаления маркеров неисправностей и порожденных ресурсов проекта.

Связывание дополняющего компоновщика с проектом

Для того чтобы компоновщик стал доступен выбранному проекту, его необходимо добавить в спецификацию компоновки для данного проекта. Спецификация компоновки проекта представляет собой список команд, последовательно выполняемых в ходе компоновки проекта. Каждая команда определяет отдельный дополняющий компоновщик проектов.

Примечание: В качестве имени компоновщика в команде указывается полный ИД расширения компоновщика. Полный ИД расширения создается путем объединения ИД модуля с ИД простого расширения из файла plugin.xml. Например, компоновщик с ИД простого расширения "mybuilder", входящий в состав модуля "com.example.builders", будет называться "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) { 
      // добавление компоновщика в проект
      ICommand command = desc.newCommand();
      command.setBuilderName(BUILDER_ID);
      ICommand[] newCommands = new ICommand[commands.length + 1];

      // добавление компоновщика в начало
      System.arraycopy(commands, 0, newCommands, 1, commands.length);
      newCommands[0] = command;
      desc.setBuildSpec(newCommands);
      project.setDescription(desc, null);
   }

Настройка компоновщика проекта выполняется один раз, обычно при создании проекта.