Programy przyrostowo budujące projekty

Program przyrostowo budujący projekty to obiekt, który manipuluje zasobami w projekcie w szczególny sposób. Programy przyrostowo budujące projekty są często używane do transformowania jednego zasobu w drugi zasób lub innego rodzaju artefakt.

Moduły dodatkowe dodają programy przyrostowo budujące projekty do platformy, aby zaimplementować specjalne transformacje zasobów. Na przykład środowisko JDT definiuje program przyrostowo budujący projekt, który kompiluje plik źródłowy Java na plik CLASS za każdym razem, gdy do projektu Java jest dodawany plik lub modyfikowany jest istniejący plik. Ten program śledzi również pliki zależne i rekompiluje je w razie potrzeby.

Z punktu widzenia interfejsu API, platforma definiuje dwa podstawowe typy budowania:

Budowanie przyrostowe zaczyna się od delty zmiany zasobu. Delta odzwierciedla efekt netto wszystkich zmian zasobów dokonanych od czasu ostatniego budowania projektu przez program budujący. Ta delta jest podobna do używanej wewnątrz zdarzeń związanych ze zmianami zasobów.

Projekty mogą być okresowo czyszczone przez użytkownika w celu wymuszenia ponownego budowania całego projektu przy jego kolejnym budowaniu przyrostowym. Czyszczenie projektu usuwa informacje dotyczące budowy, takie jak znaczniki problemów oraz pliki CLASS.

Programy budujące najłatwiej zrozumieć na przykładzie. Kompilator Java środowiska JDT jest sterowany przez program przyrostowo budujący projekty Java, który rekompiluje zmienione pliki projektu. Gdy wyzwalane jest budowanie pełne (lub budowanie przyrostowe po czyszczeniu), wszystkie pliki .java są kompilowane. Wszystkie problemy napotkane w trakcie kompilacji są dodawane jako znaczniki problemów do odpowiednich plików .java. Gdy wyzwalane jest budowanie przyrostowe, program budujący rekompiluje opisane w delcie zasobu wybrane pliki .java, które zostały dodane, zmienione lub naruszone w inny sposób, oraz aktualizuje w razie potrzeby znaczniki problemów. Wszystkie niewłaściwe pliki .class oraz znaczniki są usuwane.

Budowanie przyrostowe jest oczywiście wydajniejsze w przypadku projektów z setkami lub tysiącami zasobów, z których większość od pewnego momentu nie ulega zmianie.

Technicznym wyzwaniem dla budowania przyrostowego jest określenie, co dokładnie wymaga ponownego budowania. Na przykład wewnętrzny stan zachowany przez program budujący Java zawiera takie elementy, jak wykres zależności oraz lista zgłoszonych problemów dotyczących kompilacji. Te informacje są używane podczas budowania przyrostowego do zidentyfikowania klas wymagających rekompilacji w wyniku zmian zasobu Java.

Mimo że podstawowa struktura budowania została zdefiniowana w platformie, główna część pracy jest wykonywana wewnątrz kodu programu budującego. Wzorce implementacji skomplikowanych programów przyrostowo budujących projekty wykraczają poza zakres tego omówienia, ponieważ implementacja zależy od konkretnego projektu programu budującego.

Wywoływanie budowania

Program budujący może zostać jawnie wywołany w jeden z następujących sposobów:

W praktyce użytkownik środowiska roboczego wyzwala budowanie, wybierając odpowiednie komendy w menu nawigatora zasobów.

Programy przyrostowo budujące projekty są również wywoływane niejawnie przez platformę w trakcie budowania automatycznego. Budowanie automatyczne, jeśli jest włączone, uruchamia się po każdej zmianie obszaru roboczego.

Definiowanie programu przyrostowo budującego projekty

Do dodawania programu przyrostowo budującego projekty do platformy używany jest punkt rozszerzenia org.eclipse.core.resources.builders. Poniższy kod pokazuje, jak hipotetyczny moduł dodatkowy com.example.builders mógłby dodać program przyrostowo budujący projekty.

      <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>

Element class określony w punkcie rozszerzenia musi rozszerzać klasę platformy IncrementalProjectBuilder.

   public class BuilderExample extends IncrementalProjectBuilder {
      IProject[] build(int kind, Map args, IProgressMonitor monitor)
         throws CoreException {
         // tutaj dodaj logikę budowania
      return null;
      }
      protected void startupOnInitialize() {
         // tutaj dodaj logikę inicjowania programu budującego
      }
      protected void clean(IProgressMonitor monitor) {
         // tutaj dodaj logikę czyszczenia programu budującego
      }
   }

Proces budowania rozpoczyna się od metody build(), która zawiera informacje dotyczące rodzaju żądanej budowy. Metoda build przyjmuje jedną z następujących wartości:

Jeśli żądane jest budowanie przyrostowe, dostarczana jest delta zasobu opisująca zmiany dokonane w zasobach od czasu ostatniego budowania. Poniższy fragment kodu udoskonala jeszcze bardziej metodę 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;
   }

Czasami się zdarza, że w trakcie budowania projektu "X" program budujący potrzebuje informacji o zmianach w innym projekcie "Y". (Jeśli na przykład klasa Java w projekcie X implementuje interfejs udostępniany w projekcie Y). W trakcie budowania projektu X można uzyskać deltę projektu Y, wywołując metodę getDelta(Y). Aby się upewnić, że platforma może udostępnić takie delty, program budujący projekt X musi wcześniej zadeklarować zależności między projektem X i Y przez zwrócenie tablicy zawierającej projekt Y z poprzedniego wywołania metody build(). Jeśli program budujący nie znajdzie zależności, po prostu zwróci wartość null. Więcej informacji na ten temat zawiera opis klasy IncrementalProjectBuilder.

Budowanie pełne

Logika wymagana do przeprowadzenia procesu budowania pełnego zależy od danego modułu dodatkowego. Może wymagać sprawdzenia każdego zasobu, a nawet innych projektów, jeśli między projektami występują zależności. Poniższy fragment kodu sugeruje sposób implementacji budowania pełnego.

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

Gość procesu budowania wykonałby budowanie konkretnego zasobu (oraz zwrócił wartość true w celu kontynuowania sprawdzania wszystkich zasobów potomnych).

   class MyBuildVisitor implements IResourceVisitor {
      public boolean visit(IResource res) {
         //budowanie określonego zasobu
         //zwrócenie wartości true w celu sprawdzania zasobów potomnych
               return true;
      }
   }

Proces sprawdzania będzie kontynuowany, dopóki całe drzewo zasobów nie zostanie przeanalizowane.

Budowanie przyrostowe

W trakcie wykonywania budowania pełnego, program budujący zamiast z pełnym drzewem zasobu pracuje z deltą jego zmian.

   protected void incrementalBuild(IResourceDelta delta, 
         IProgressMonitor monitor) throws CoreException {
      // gość wykonuje pracę
      delta.accept(new MyBuildDeltaVisitor());
   }

Proces sprawdzania będzie kontynuowany, dopóki całe drzewo delty zasobu nie zostanie przeanalizowane. Specyficzna natura zmian jest podobna do opisanej w sekcji Implementowanie obiektu nasłuchiwania zmian w zasobach. Jedną istotną różnicą jest fakt, że programy przyrostowo budujące projekty pracują z deltą zasobu opartą na konkretnym projekcie, a nie na całym obszarze roboczym.

Czyszczenie przed budowaniem

Środowisko robocze umożliwia użytkownikom czyszczenie projektu lub zestawu projektów przed uruchomieniem budowania. Ta funkcja pozwala użytkownikowi na wymuszenie ponownego budowania od podstaw tylko określonych projektów. Programy budujące powinny implementować tę metodę w celu oczyszczenia projektu ze znaczników problemów oraz zasobów pochodnych.

Przypisywanie programu przyrostowo budującego projekt do projektu

Aby program budujący był dostępny dla danego projektu, musi zostać uwzględniony w jego specyfikacji budowania. Specyfikacja budowania projektu jest listą komend uruchamianych kolejno w trakcie budowania projektu. Każda komenda nadaje nazwę pojedynczemu programowi przyrostowo budującemu projekty.

Uwaga: Nazwa programu budującego w komendzie budowania jest pełnym identyfikatorem rozszerzenia programu budującego. Pełny identyfikator rozszerzenia jest tworzony przez połączenie identyfikatora modułu dodatkowego z prostym identyfikatorem rozszerzenia z pliku plugin.xml. Na przykład program budujący z prostym identyfikatorem rozszerzenia "mybuilder" będący częścią modułu dodatkowego "com.example.builders" ma nazwę "com.example.builders.mybuilder"

.Poniższy fragment kodu dodaje nowy program budujący jako pierwszy na istniejącej liście programów budujących.

   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) { 
      //dodawanie programu budującego do projektu
      ICommand command = desc.newCommand();
      command.setBuilderName(BUILDER_ID);
      ICommand[] newCommands = new ICommand[commands.length + 1];

      // dodawanie przed innymi programami budującymi
      System.arraycopy(commands, 0, newCommands, 1, commands.length);
      newCommands[0] = command;
      desc.setBuildSpec(newCommands);
      project.setDescription(desc, null);
   }

Program budujący konfiguruje się dla danego projektu tylko raz, zazwyczaj przed utworzeniem projektu.