Сергей Чикуенок
4 марта 2009 |
|
Задача. |
Научиться выполнять рутинную работу с помощью компьютера. |
||
Когда речь идет об использовании интегрированной среды разработки (IDE), часто приходится сталкиваться с мнением о том, что ее функционал слишком избыточен для решения повседневных задач. Доля правды в этом есть: появляются новые сущности, незнакомые механизмы, много настроек, о которых раньше не приходилось задумываться. Однако не стоит забывать, что работа в IDE — совершенно новый уровень разработки и многие привычные задачи в ней решаются по-другому. Прелесть интегрированной среды в том, что у разработчика есть возможность собрать под одной крышей (то есть в одной программе) много разных инструментов, которые будут не только решать разные задачи, но и тесно взаимодействовать друг с другом. Таким образом, перед разработчиком откроются абсолютно новые способы работы над проектом.
В прошлых статьях я рассказывал об инструментах для работы с исходным кодом, сейчас начнем знакомиться с приемами работы над проектом в целом. И начнем с автоматизации рутинных задач с помощью Ant.
Apache Ant известен любому Java-программисту: это популярный инструмент сборки ПО (build tool), полностью написанный на Java. Его поддерживают все IDE: NetBeans, IntelliJ IDEA, Eclipse. Сценарий сборки Ant представляет собой простой XML-файл. Несмотря на всю свою Java-направленность, и обычные веб-разработчики пользуются этим инструментом для решения своих задач.
Давайте начнем изучение 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.
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>
Рассмотрим подробнее структуру сценария. Родительским элементом является <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. Сейчас мы научимся одному из способов добавления таких задач в сценарий сборки.
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 МБ)
© 19952024 Студия Артемия Лебедева
|