Miesięczne archiwum: Grudzień 2013

Phing, czyli zautomatyzuj swoje zadania #2

Przyszedł czas na drugie spojrzenie na Phing – czym on jest napisałem w skrócie w poprzednim wpisie.

Parametry uruchomienia

To narzędzie konsolowe, więc jest uruchamiane za pomocą wywołania phing [opcje] [cel] w konsoli (najlepiej w katalogu zawierającym plik build.xml). W wywołaniu można umieścić parametry/opcje:

  • [nazwa celu] – nazwa celu, który ma zostać wywołany; jeżeli się nie pojawi zostanie uruchomiony cel domyślny projektu (<project ... default="domyslny_cel"/>);
  • -h / -help – wyświetlenie ogólnej pomocy aplikacji;
  • -l / -list – wyświetlenie listy celów zdefiniowanych w build.xml;
  • -f [plik] / -buildfile [plik] – nazwa pliku skryptu, jeżeli inna niż build.xml;
  • -D[właściwość]=[wartość] – przekazanie do skryptu właściwości o podanej wartości;
  • -propertyfile [plik] – wczytanie podanego pliku z deklaracjami właściwości;

Cele

W poprzedniej sekcji wspomniałem o parametrze pozwalającym wyświetlić listę dostępnych celów. Jednak nie każdy cel powinien być uruchamiany „samodzielnie” – czasem jest potrzebny tylko do spełnienia jakichś podstawowych wymagań dla innego celu, który wykonuje faktyczne operacje. Przykładem może być cel, który wczytuje/ustala odpowiednie właściwości konfiguracyjne (np. ścieżki do katalogów, dane FTP etc.), wywołanie takiego celu samodzielnie do niczego nie doprowadzi. Dlatego takie „prywatne” cele można ukryć na liście za pomocą atrybutu <target ... hidden="true"/>.

Kolejną i chyba nabardziej przydatną możliwością celów jest ustalanie dla nich zależności – na przykład, żeby wykonać cel „deploy” (który wgrywa pliki na serwer), należy najpierw te pliki/katalogi przygotować (np. usunąć pliki tymczasowe). W skrypcie Phing nie trzeba deklarować dodatkowo wywołań celów, od których zależy wykonanie bieżącego lub samemu wywoływać ręcznie cele w odpowiedniej kolejności. W tym celu wykorzystuje się atrybut <target ... depends="cel1,cel2,cel3"/>

Są jeszcze dwa pomocne atrybuty celów – if, unless. Pierwszy uruchamia cel tylko w momencie, kiedy zmienna o podanej nazwie jest zadeklarowana. Drugi ma działanie odwrotnie. Poniżej przykład:

<?xml version="1.0" encoding="UTF-8"?>

<project name="HelloWorld" default="hello">

    <property name="uruchomione" value="" />

    <target name="hello" depends="cel1,cel2">
        <echo>Hello</echo>
    </target>

    <target name="cel1" if="uruchomione">
        <echo>cel 1</echo>
    </target>

    <target name="cel2" unless="uruchomione">
        <echo>cel 2</echo>
    </target>

</project>

Poniżej wynik uruchomienia (pojawiają się nagłówki wszystkich wywołanych celów, ale kod cel2 nie został wykonany ze względu na warunek zawarty w atrybucie unless.

cim@cim-k52:~/public_html/Phing$ phing 
Buildfile: /home/cim/public_html/Phing/build.xml

HelloWorld > cel1:

     [echo] cel 1

HelloWorld > cel2:


HelloWorld > hello:

     [echo] Hello

BUILD FINISHED

Total time: 0.0581 seconds

Właściwości

Właściwości są swego rodzaju zmiennymi dla skryptu Phing i zostały już użyte w poprzednich przykładach. Warto pamiętać, że odwołania do właściwości mają budowę ${nazwaWlasciwosci}, np. <echo>Wartość zmiennej: ${nazwa}</echo>. Przydatne jest używanie kropek dla budowania hierarchii – ${aplikacja.baza.host}% – można w ten sposób grupować właściwości co mocno zwiększa czytelność.

Do tej pory w przykładach pojawiały się właściwości z wartościami zadeklarowanymi w pliku build.xml, jednak to nie jedyne możliwe źródła danych:

  • <property name="nazwa" value="wartosc" /> – pokazywany wcześniej sposób deklarowania zmiennej, w deklaracjach można odwoływać się do innych zmiennych (np. do jakiejś ścieżki dodać nazwe kolejnego katalogu);
  • <property file="plik.properties" /> – wczytuje do skryptu właściwości zdefiniowane w podanym pliku; można użyć również dodatkowego atrybutu <property file="plik.properties" prefix="zPliku" /> co spowoduje dodanie prefiksu do wszystkich nazw właściwości znajdujących się w pliku;
  • <echo>${env.PATH}</echo> – odwołania z prefiksem env. pozwalają na dostęp do zmiennych środowiskowych (zależne od systemu);

W przypadku zdublowania nazw właściwości Phing będzie zwracał pierwszą wartość właściwości, żeby zmienić to zachowanie trzeba dodać do drugiej deklaracji atrybut <property ... override="yes" />.

Podczas tworzenia ścieżek lub operacji plikowych pomocny może okazać się dostęp do ścieżki do katalogu projektu (zadeklarowanej w tagu <project ... basedir="/var/www/Projekt"> lub domyśnie ustawianej przez Phing). Ścieżka jest dostępna pod nazwą ${project.basedir}.

FileSet

Ponieważ Phing ma ułatwiać wdrożenia projektu posiada wsparcie do operowania na zbiorach plików – FileSet. Jeżeli na jakichś plikach mają zostać wykonane operacje można je wskazać bardzo dokładnie:

<target name="copy">
    <copy todir="kopia">
        <fileset dir=".">
            <exclude name="*.xml" />
            <include name="*.properties" />
            <include name="*.php" />
        </fileset>
    </copy>
</target>

Powyższa definicja przedstawia cel kopiujący pliki do katalogu „kopia” za pomocą polecenia copy. Natomiast miejsce z którego pliki są kopiowane („.” – czyli bieżący katalog) oraz deklaracje, które pliki (w tym wypadku według rozszerzeń) mają być kopiowane lub pomijane definiuje typ FileSet.

Definiowanie dołączeń/wykluczeń jest elastyczne:

  • można wykorzystywać atrybuty: <fileset ... includes="..."/> i <fileset ... excludes="..."/> z wykorzystaniem przecinków lub spacji podawać wzorce, jednak powyżej kilku wzorców ta metoda sprawia, że skrypt jest mniej czytelny;
  • można wykorzystać atrybuty: <fileset ... includesfile="..."/> i <fileset ... excludesfile="..."/> podając ścieżki do plików, w których każda linijka zawiera jeden wzorzec; jednak wymaga to utrzymywania dodatkowych plików oprócz niezbędnego build.xml;
  • można użyć tagów <include/> i <exclude/> w tagu FileSet tak jak w przykładzie powyżej; chociaż przy wielu operacjach powtarzanie tego samego zestawu dołączeń/wykluczeń i późniejsze ich utrzymywanie różnież może być kłopotliwe;
  • można użyć typu PatternSet.

Deklarowanie wzroców dopasowań nazw plików opiera się na gwiazkach („*” lub „**”). Jedna gwiazka pozwala na dopasowanie nazwy pliku/katalogu jednak w ramach tego dopasowania mogą się znajdować tylko pliki/katalogi bezpośrednio w danym katalogu. Natomiast dwie gwiazdki pozwalają na dopasowania również z uwzględnieniem zawartości podkatalogów. Przykłady z opisem są w oficjalnej dokumentacji.

PatternSet

W poprzenim akapicie wymieniłem kilka możliwości definiowania dołączeń/wykluczeń dla typu FileSet wraz z ich niedogodnościami. Ostatnią opcją jest typ PatternSet, który najprościej można opisać jako zbiór wzorców dla FileSet. Poniżej zmodyfikowany kod celu kopiującego pliki:

<patternset id="ogolne">
    <exclude name="*.xml" />
    <include name="*.properties" />
    <include name="*.php" />
</patternset>

<target name="copy">
    <copy todir="kopia">
        <fileset dir=".">
            <patternset refid="ogolne" />
        </fileset>
    </copy>
</target>

Wynik wykonania celu przedstawionego powyżej i z poprzedniego przykładu są identyczne. Różnia polega na tym, że wykorzystanie PatternSet ułatwia zapanowanie nad definicjami warunków – można je zdefiniować globalnie i tylko odwoływać się w razie potrzeby, a zmiany ograniczają się do jednego miejsca. Warto też zauważyć, że PatternSet w przeciwieństwie do FileSet jest niezależny od katalogu. Można zdefiniować warunki takie jak dołączanie plików PHP, HTML, TPL, pomijanie plików logów, plików tymczasowych etc. i używać ich w kontekście różnych katalogów. Sposoby definiowania warunków w PatternSet są takie same jak w FileSet z wyjątkiem możliwości odwoływania się do typu PatternSet.

Przydatne linki: