;-)
  • Инвентарь
  • Техногрет
  • Сергей Чикуенок

    Eclipse: знакомство с Ant 4 марта 2009


    Задача.

    Научиться выполнять рутинную работу с помощью компьютера.

    Когда речь идет об использовании интегрированной среды разработки (IDE), часто приходится сталкиваться с мнением о том, что ее функционал слишком избыточен для решения повседневных задач. Доля правды в этом есть: появляются новые сущности, незнакомые механизмы, много настроек, о которых раньше не приходилось задумываться. Однако не стоит забывать, что работа в IDE — совершенно новый уровень разработки и многие привычные задачи в ней решаются по-другому. Прелесть интегрированной среды в том, что у разработчика есть возможность собрать под одной крышей (то есть в одной программе) много разных инструментов, которые будут не только решать разные задачи, но и тесно взаимодействовать друг с другом. Таким образом, перед разработчиком откроются абсолютно новые способы работы над проектом.

    В прошлых статьях я рассказывал об инструментах для работы с исходным кодом, сейчас начнем знакомиться с приемами работы над проектом в целом. И начнем с автоматизации рутинных задач с помощью Ant.

    Apache Ant известен любому Java-программисту: это популярный инструмент сборки ПО (build tool), полностью написанный на Java. Его поддерживают все IDE: NetBeans, IntelliJ IDEA, Eclipse. Сценарий сборки Ant представляет собой простой XML-файл. Несмотря на всю свою Java-направленность, и обычные веб-разработчики пользуются этим инструментом для решения своих задач.


    Hello world!

    Давайте начнем изучение Ant с классического примера: текст Hello world! выведем в консоль. Для начала нам нужно убедиться, что файл build.xml (так обычно называют сценарии сборки) привязан к редактору Ant в Eclipse. Идем в Preferences → General → Editor → File Associations и ищем там файл build.xml, привязанный к редактору Ant Editor. Если такой записи нет, добавьте ее: сначала нажмите Add напротив File types и введите туда build.xml, затем выделите только что добавленную запись и нажмите Add напротив Associated editors и в списке выберите Ant Editor. Выделите Ant Editor и нажмите кнопку Default.



    1. Создадим новый проект и назовем его, например, ant-test.
    2. В этом проекте создадим новый файл с именем build.xml. Файл должен сразу же открыться в Ant Editor.
    3. Запишем туда вот такой сценарий:
      01 
      02 
      03 
      04 
      05 
      06 
      <?xml version="1.0"?>
      <project name="my_test_project">
      	<target name="hello-world">
      		<echo>Hello world!</echo>
      	</target>
      </project>
      
    4. Откроем вид Ant: Window → Show View → Other...


    5. В только что открывшемся виде нажимаем на иконку , находим файл build.xml и дважды кликаем по нему. Теперь в этом виде отображается наш сценарий, который очень удобно тестировать.
    6. Раскроем список my_test_project и сделаем двойной клик по hello-world. В консоли должна появится надпись Hello world!



    Рассмотрим подробнее структуру сценария. Родительским элементом является <project> с атрибутом name — это название сценария построения.

    Внутри тега <project> находится тег <target>, внутри которого — <echo>. Тут мы подходим к двум важным понятиям Ant, которые поначалу могут немного запутать: это цели (targets) и задания (tasks). В данном случае мы выполнили цель hello-world (обязательный атрибут name у тега <target>) с помощью задания <echo>. Чтобы легче было запомнить, предлагаю пользоваться следующими правилами: цель указывает, что именно нужно сделать, а задания — с помощью чего достигается цель.

    Чтобы лучше освоиться с Ant, давайте создадим что-нибудь полезное, например, актуальное на сегодняшний день сжатие набора js-файлов с помощью YUICompressor.


    Поставим себе задачу. У нас есть набор JavaScript-файлов, представляющих собой одну библиотеку. Чтобы эта библиотека загрузилась максимально быстро для пользователя, нужно объединить все файлы в один и сжать результат с помощью YUICompressor. Создадим в нашем проекте ant-test две папки: js_src и js. В первой будем хранить исходник, а во второй — сжатый результат. Создадим несколько файлов в папке js_src: file1.js, file2.js и file3.js, в каждый из этих файлов напишем какой-нибудь код.

    Приступим к написанию сценария. Определимся, каких целей мы хотим достичь. Нам нужно построить библиотеку lib.js (первая цель), для чего нужно объединить все файлы в один (вторая цель) и сжать их (третья цель). Вот примерный сценарий:

    01 
    02 
    03 
    04 
    05 
    06 
    07 
    08 
    09 
    10 
    11 
    12 
    13 
    14 
    15 
    16 
    17 
    18 
    19 
    20 
    21 
    22 
    23 
    <?xml version="1.0"?>
    <project name="Build first JS lib" default="build-lib" basedir=".">
    
     	<target name="build-lib" depends="concat-files, minify">
    		<echo>Done.</echo>
    	</target>
    
    	<target name="concat-files">
    		<echo>Concatenating files</echo>
    		<concat destfile="./js/lib.js">
    			<filelist dir="./js_src">
    				<file name="file1.js"/>
    				<file name="file2.js"/>
    				<file name="file3.js"/>
    			</filelist>
    		</concat>
    	</target>
    
    	<target name="minify">
    		<echo>Minifying files</echo>
    	</target>
    
    </project>
    

    Сразу рассмотрим все новые элементы и атрибуты. У элемента <project> появился атрибут default — это цель или набор целей, которые будут выполняться по умолчанию при вызове этого сценария. Нам это понадобится чуть позже. Атрибут basedir задает директорию, относительно которой будут считаться все относительные пути к файлам и директориям. Его указывать необязательно, по умолчанию он равен абсолютному пути к проекту, в котором находится сценарий.

    Обратите внимание на атрибут depends у цели build-lib. Он определяет цели, от которых зависит текущая цель. В данном случае мы говорим: «Выполнить цель build-lib только после того, как будут выполнены concat-files и minify». Это очень удобный (но далеко не единственный) механизм для использования одинаковых последовательностей заданий в разных целях. Особенность зависимостей Ant заключается в том, что одна цель будет выполнена только один раз в течение сессии. Подробнее об этом читайте в документации.


    Как видно из примера, у нас есть точка входа — цель build-lib (атрибут default у  <project>), которая зависит от двух других целей: concat-files и  minify. Объединение (конкатенация) файлов выполняется с помощью задания <concat>, в атрибуте destfile указываем, в какой файл хотим сохранить результат. За то, какие именно файлы и в какой последовательности должны быть объединены, отвечает файловый ресурс <filelist>, который мы отдаем заданию <concat>. Думаю, здесь все понятно: в атрибуте dir указываем, в какой директории нужно искать файлы, а с помощью элементов <file> задаем конкретные файлы. Типов ресурсов существует довольно много, большинство из них нужны Java-программистам. Для нас наиболее востребованными будут <filelist> и <fileset>. Основное их различие в том, что в первом случае указывается четкий набор файлов, а во втором — задается набор файлов с помощью паттернов включения и исключения. Например, сейчас мы могли бы заменить <filelist> на такую конструкцию:

    01 
    02 
    03 
    <fileset dir="./js_src">
    	<include name="*.js"/>
    </fileset>
    

    Но у этого способа есть ряд недостатков. Во-первых, в набор попадут абсолютно все файлы с расширением js в папке js_src, что не всегда нужно на реальных проектах. А во-вторых, даже если указать в паттернах названия файлов, этот ресурс не гарантирует тот порядок следования, в котором вы хотите их объединить. То есть если важно, в каком порядке должны объединяться файлы, лучше задавать набор через <filelist>.

    В приведенном примере смущает то, что пути к папкам жестко зашиты в код сценария, поэтому давайте вынесем их в отдельные свойства:

    01 
    02 
    03 
    04 
    05 
    06 
    07 
    08 
    09 
    10 
    11 
    12 
    13 
    14 
    15 
    16 
    17 
    18 
    19 
    20 
    21 
    22 
    23 
    24 
    25 
    25 
    <?xml version="1.0"?>
    <project name="Build first JS lib" default="build-lib" basedir=".">
    	
    	<property name="src.dir" value="./js_src"/>
    	<property name="dest.dir" value="./js"/>
    	
    	<target name="build-lib" depends="concat-files, minify">
    		<echo>Done.</echo>
    	</target>
    
    	<target name="concat-files">
    		<echo>Concatenating files</echo>
    		<concat destfile="${dest.dir}/lib.js">
    			<filelist dir="${src.dir}">
    				<file name="file1.js"/>
    				<file name="file2.js"/>
    				<file name="file3.js"/>
    			</filelist>
    		</concat>
    	</target>
    
    	<target name="minify">
    		<echo>Minifying files</echo>
    	</target>
    
    </project>
    

    Свойства в Ant задаются с помощью тега <property>. Свойства очень похожи на константы в языках программирования: задав один раз, вы больше не сможете поменять их во время исполнения сценария. Обращаться к свойствам следует через конструкцию ${название свойства}. Свойства можно задавать не только внутри <project>, но и внутри <target>, а также глобально для всего IDE через Preferences → Ant → Runtime → Properties.


    Хозяйке на заметку

    Не зря в начале статьи отдельно подчеркивалось, что файл build.xml открывается через Ant Editor — этот редактор имеет очень удобный code complete, который работает со свойствами (<property>), целями (в атрибутах depends, default и так далее), а также с доступными задачами и их атрибутами. Не забывайте об этой помощи, когда будете писать свои сценарии.

    Проверьте работу сценария: сделайте двойной клик в Ant View на цели build-lib. У вас в папке js должен появиться файл lib.js, в котором будет содержимое всех трех файлов. Если файл не появился, обновите (Refresh) папку js в Project Explorer.

    Файлы объединять мы научились, теперь научимся сжимать их с помощью YUICompressor. Сам разработчик этой замечательной библиотеки рекомендует вызывать компрессор как внешнюю программу, мы же поступим куда более интересно: создадим отдельную задачу, которая будет сжимать файлы.

    Модульная структура Ant позволяет расширять его, дописывая новые задачи на Java. Сейчас мы научимся одному из способов добавления таких задач в сценарий сборки.

    1. Скачайте YUICompressor и распакуйте куда-нибудь архив, например, в папку C:\yuicompressor.
    2. Скачайте YUIAnt.jar и положите его в папку build, где вы распаковали YUICompressor (C:\yuicompressor\build).
    3. Добавьте вот такую конструкцию в сценарий внутрь тега <project>:
    4. 01 
      02 
      03 
      04 
      05 
      06 
      07 
      <taskdef name="yuicompress" classname="com.yahoo.platform.yui.compressor.YUICompressTask">
      	<classpath>
      		<fileset dir="C:\yuicompressor\build">
      			<include name="*.jar"/>
      		</fileset>
      	</classpath>
      </taskdef>
      

    С помощью нее мы создали новую задачу <yuicompress>. Чтобы она работала, нужно указать полный путь к Java-классу этой задачи (com.yahoo.platform.yui.compressor.YUICompressTask), а также указать путь, где находится файл с этим классом (<classpath>/<fileset>). Теперь можно дописать цель minify:

    01 
    02 
    03 
    04 
    05 
    06 
    07 
    08 
    <target name="minify">
    	<echo>Minifying files</echo>
    	<yuicompress munge="yes" linebreak="5000" preserveallsemicolons="yes" outputfolder="${dest.dir}">
    		<fileset dir="${dest.dir}">
    			<include name="*.js"/>
    		</fileset>
    	</yuicompress>
    </target>
    

    Мы вызвали задачу <yuicompress>, указав ей набор файлов, которые необходимо сжать, с помощью конструкции <fileset>. Так как мы используем задачу, а не вызов внешнего файла, Ant Editor сам подхватил нужный класс и теперь будет выдавать code complete по атрибутам задачи <yuicompress>. Итоговый сценарий сборки выглядит так:

    01 
    02 
    03 
    04 
    05 
    06 
    07 
    08 
    09 
    10 
    11 
    12 
    13 
    14 
    15 
    16 
    17 
    18 
    19 
    20 
    21 
    22 
    23 
    24 
    25 
    26 
    27 
    28 
    29 
    30 
    31 
    32 
    33 
    34 
    35 
    36 
    37 
    38 
    <?xml version="1.0"?>
    <project name="Build first JS lib" default="build-lib" basedir=".">
    
    	<property name="src.dir" value="./js_src"/>
    	<property name="dest.dir" value="./js"/>
    
    	<taskdef name="yuicompress" classname="com.yahoo.platform.yui.compressor.YUICompressTask">
    		<classpath>
    			<fileset dir="C:\yuicompressor\build">
    				<include name="*.jar"/>
    			</fileset>
    		</classpath>
    	</taskdef>
    
    	<target name="build-lib" depends="concat-files, minify">
    		<echo>Done.</echo>
    	</target>
    
    	<target name="concat-files">
    		<echo>Concatenating files</echo>
    		<concat destfile="${dest.dir}/lib.js">
    			<filelist dir="${src.dir}">
    				<file name="file1.js"/>
    				<file name="file2.js"/>
    				<file name="file3.js"/>
    			</filelist>
    		</concat>
    	</target>
    
    	<target name="minify">
    		<echo>Minifying files</echo>
    		<yuicompress munge="yes" linebreak="5000" preserveallsemicolons="yes" outputfolder="${dest.dir}">
    			<fileset dir="${dest.dir}">
    				<include name="*.js"/>
    			</fileset>
    		</yuicompress>
    	</target>
    </project>
    
    

    Проверьте работу цели build-lib, в папке js у вас должен оказаться пожатый lib.js.


    Сборка проекта

    А теперь перейдем к самому интересному. Удобство интеграции Ant с Eclipse заключается не в том, что вы можете в любой момент вызвать нужную цель из окошка, а в том, что вы можете указать сборщик для проекта.



    Зайдите в свойства проекта ant-test (Project → Properties) и перейдите в пункт Builders. Нажмите кнопку New и в диалоговом окне выберите Ant Builder. В появившемся окне задайте какое-нибудь имя вашему сборщику (например, My first Ant build). Теперь нужно выбрать сценарий (buildfile). Нажмите на кнопку Browse workspace и в проекте ant-test выберите файл build.xml. Перейдите на вкладку Refresh и отметьте пункты Refresh resources upon completition и The project containing the selected resource. Это значит, что после работы сборщика будет автоматически обновляться список файлов проекта, поэтому в Project Explorer вы всегда будете видеть актуальную версию проекта. Перейдите во вкладку Targets, там вы увидите список целей, которые будут выполняться при разных типах сборки. В частности, в разделах After a «Clean» и Manual build будет указано default target selected. Это означает, что будет выполняться цель, указанная по умолчанию у проекта (помните атрибут default у <project>?). Давайте еще укажем цель в Auto build, для этого нажмем на кнопку Set targets cправа от Auto Build и сразу нажмем на ОК — будет выбрана цель по умолчанию.

    Теперь мы можем вызывать сборщик по нажатию на Project → Build Project (на эту команду можно повесить горячие клавиши). А если выберем опцию Project → Build Automatically, то нам даже ничего делать не надо: проект будет сам собираться при любом изменении любого файла проекта.



    Таким образом, мы пришли к абсолютно прозрачной автоматизации рутинных задач при работе в IDE. Все, что необходимо сделать, — создать простой сценарий, указать его в качестве сборщика и включить опцию автоматической сборки. Каждый раз, когда вы модифицируете проект (редактируете, удаляете и добавляете файлы) все необходимые действия будут выполнятся в фоновом режиме, не отвлекая разработчика. Это особенно полезно при работе в команде: любой разработчик может внести изменения в сценарий, которые будут автоматически выполняться у всех остальных.

    В этой статье были поверхностно рассмотрены некоторые базовые приемы работы с Ant. Советую ознакомиться со списком стандартных задач, там есть такие полезные вещи, как синхронизация каталогов, закачка файлов по FTP, выполнение SQL-запросов, XSL-трансформации, архивация файлов и многое другое.



    В следующей статье мы продолжим изучение Ant — в частности, научимся писать дополнительные задачи на JavaScript. В качестве домашнего задания предлагаю сделать следующее: вынесите определение задачи <yuicompress> в отдельный файл и подключите его к сценарию (подсказка: воспользуйтесь заданием <import>, а также задайте глобальное свойство, в котором будет храниться путь к файлу с определением).



    Посмотреть на Ant в действии можно в студийной лекции для технологов.



    Дополнительные материалы:

    — Скринкаст (Квиктайм, 101 МБ)