Jak je to tedy s těmi závislostmi ?
Mohou nastat případy, kdy jsou cíle na sobě tolik závislé, že vlastně nevíme, jestli jsou závislé správně. Pak můžeme „zkontrolovat“ tyto závislosti pomocí ohodnoceného, orientovaného grafu. Mějme tedy například takovéto závislosti:
<project name="Priklad_1" default="Target-4"> <target name="Target-1"/> <target name="Target-2" depends="Target-1"/> <target name="Target-3" depends="Target-2, Target-1"/> <target name="Target-4" depends="Target-3, Target-1"/> </project>
Je tedy vidět, že cíle jsou různě závislé, ale není triviální určit, v jakém pořadí se budou jednotlivé cíle vykonávat. Vytvoříme si tedy ku pomoci graf, který vyjádří závislosti mezi cíli. Pořadí vykonávání cílů můžeme určit pro každý z cílů samostatně, my se zaměříme na cíl Target-4. Cíl Target-4 je závislý na cílech Target-3 a Target-1 ⇒ z uzlu Target-4 vedou hrany k uzlům Target-3 a Target-1 ohodnocené hodnotou 1 (první iterace). V další iteraci vyhodnocujeme tedy cíle Target-3 a Target-1. Postupně nám vznikne graf, který vidíme na obrázku. Ve vzniklém grafu můžeme navíc zkontrolovat, zda nám nevznikl cyklus, pokud ne, je vše v pořádku, pokud ano, pak se nám spuštění sestavovacího schématu nepovede (ant oznámí „Circular dependency“).
Jak ale teď celý graf vyhodnotit a získat pořadí vykonávání cílů, když inicializujeme cíl Target-4? Cesta je jednoduchá, začneme ve vrcholu, do nějž vede hrana s nejvyšší hodnotou. Po této hraně sestoupíme do dalšího uzlu a z něj se opět vydáme v protisměru hrany s nejvyšší hodnotou. Tímto způsobem se dopravíme až do požadovaného cíle a tím také získáváme pořadí vykonávání cílů pro náš požadovaný cíl. Může se také stát, že máme na výběr jakoby ze dvou různých cest, pak postupujeme v takovém pořadí, v jakém jsou uvedeny cíle v atributu depend. Vše nám objasní příklad:
<project name="Priklad_2" default="Target-4"> <target name="Target-1" depends="Target-2, Target-3"/> <target name="Target-2" depends="Target-4"/> <target name="Target-3" depends="Target-5"/> <target name="Target-4"/> <target name="Target-5"/> </project>
Zobrazíme-li si vše na obrázku, vidíme, že existují dvě hrany s hodnotou 2.
Ovšem nelze rozhodnout, která hrana má přednost. Dále ale vidíme, že hrany ohodnocené hodnotou 1 vycházejí ze stejného uzlu. V tomto uzlu je zároveň atributem depends dáno, v jakém pořadí se mají vyhodnotit cíle Target-2 a cíl Target-3. Výsledkem pro vyhodnocení cíle Target-1 je tedy posloupnost : Target-4 → Target-2 → Target-5 → Target-3 → Target-1.
Ještě o podmínkách
A i když je atribut depend docela šikovnou vlastností, něco tomu přece jenom ještě chybí, podmínky. Potřebujeme zajistit, aby mohl ANT rozhodnout o dalším zpracování na základě splnění, či nesplnění nějaké podmínky. Takovou možnost nám v tomto případě přináší dvojice komplementárních atributů if a unless. Pomocí těchto atributů můžeme zkoumat, zda je nastavena nějaká „property“ či je použita správná verze Javy nebo je to ten správný OS. Vše osvětlí příklad:
<project name="Priklad_3" default="if-unless"> <property name="Linux" value="yes"/> <target name="if-unless" unless="Windows"> <echo message="Ohh, yes!!"/> </target> </project>
Zde se cíl if-unless provede pouze tehdy, není-li definována konstanta Windows. Chceme-li změnit chování, odstraníme konstantu Linux a konstantu Windows dodefinujeme (ale nemusíme).
Také něco o „táscích“
Nyní, když již máme docela slušné ponětí o tom, jak vlastně vypadá sestavovací schéma ANTu z pohledu cílů, a tedy jsme již schopni vytvořit vlastní koncepci řízení překladu, se můžeme vrhnout na popis „stavebních kamenů“ = tasků.
Za task mužeme považovat vlastně jakýkoliv element, který se v sestavovacím schématu vyskytne. My ovšem budeme považovat za regulérní tasky pouze ty, které se objeví uvnitř elementu target. Všechny ostatní budou mít pro nás speciální význam. Můžeme tedy používat tasky těchto typů:
- vestavěvé (built-in) tasky: Jsou tasky, které jsou přímo poskytovány v ANTu (v instalaci či balíčku)
- volitelné (optional) tasky: Jsou dodatečné tasky, které lze k ANTu získat pomocí doplňující instalace (balíčku)
- vlastní nové tasky: Jsou nové tasky, které máme možnost si sami napsat.
V našem dalším výkladu se zaměříme na konkrétní vestavěné tasky, a to hlavně na jejich možnosti a správné využití. Nejprve si ukážeme pár jednodušších a méně komplexních tasků, na kterých si vysvětlíme, jak je korektně vytvářet.
Používáme vestavěné tasky
Nejprve si tedy představíme několik jednodušších tasků. Jako první to bude jistě velmi užitečný task manifest, který lze velmi dobře využít v téměř všech sestavovacích schématech, kde je výsledkem archiv jar. Dále si pak ukážeme často používaný task copy, který nám poslouží při kopírování souborů.
manifest
Task manifest dovoluje vytvářet soubory typu manifest, které se pak vkládají do archívů jar. Nebudeme se zde zabývat specifikací syntaxe a sémantiky tohoto dokumentu, ale zaměříme se na to, jak takový dokument vytvořit pomocí ANTu.
manifest definuje dva atributy: povinný file a nepovinnýmode. Atribut file specifikuje jméno manifest souboru, mode pak může obsahovat jednu ze dvou hodnot: update a replace, replace je implicitní.
<project name="Priklad_4" default="manifest"> <target name="manifest"> <manifest file="MANIFEST.MF" mode="replace"/> </target> </project>
Dále pak definuje dva vnořené elementy, které specifikují hodnoty vstupující do souboru manifest. Těmito elementy jsou: section a attribute. Element section specifikuje v souboru manifest pojmenovanou část, attribute pak samotné hodnoty.
<project name="Priklad_5" default="manifest"> <target name="manifest"> <manifest file="MANIFEST.MF" mode="replace"> <attribute name="Manifest-Version" value="1.0""> <attribute name="Created-By" value="${user.name}""> <section name="Description""> <attribute name="Example-Attribute" value="SomeValue""> </section"> </Manifest"> </target> </project>
Výsledkem bude vždy znovu vytvořený soubor MANIFEST.MF, který bude obsahovat v implicitní sekci Main dva atributy Manifest-Version a Created-By a dále novou námi vytvořenou sekci Description s atributem Example-Attribute.
copy
Co umožňuje udělat task copy? Můžeme s jeho pomocí vytvářet kopie souborů a adresářů. Kopírování jediného souboru je velmi jednoduché, zde využijeme task copy v jeho nejzákladnější podobě.
<project name="Priklad_6" default="copy"> <target name="copy"> <copy file="source.file" toFile="destination.file"/> </target> </project>
Kopírování adresářů nebo množiny souborů je složitější, ovšem dovoluje vytvářet specifické množiny pomocí jednoduchých pravidel. Tato pravidla a samozřejmě i kopírované soubory definují vnořené elementy fileset – výběr kopírovaných souborů, mapper – transformace jmen kopírovaných souborů, filterset- scanuje a zaměňuje hodnoty v kopírovaných souborech, filterchain – filtr obsahu kopírovaných souborů (např. řádky, které obsahují nějaké slovo). Nejlepší bude ukázat na příkladu některé z těchto možností:
<project name="Priklad_7" default="copy-more-files"> <property name="destination" location="some/directory"/> <property name="source" location="root/directory/for/fileset"/> <target name="copy-more-files"> <copy toDir="${destination}"> <fileSet dir="${source}"> <include name="**/*.java"/> </fileSet> <mapper type="glob" from="*.java" to="*.java.bak"/> </copy> </target> </project>
Předchozí ukázka nám tedy provede zkopírování souborů .java z adresáře ${source} do adresáře ${destination} s tím, že každému z těchto souborů přidá ještě příponu .bak.
Závěrem
Jak tedy vidíme, není používání vestavěných tasků žádnou složitou věcí (o to taky jde, že), v příštím pokračování se tedy můžeme pustit i do poněkud složitějších tasků. Mezi ně se řadí hlavně tasky pro překlad, ukážeme si konkrétně task javac. Předvedeme si také task pro spouštění vytvořeného programu, tím je java. A mnoho dalších šikovných tasků …