понедельник, июля 17, 2006

Сборка Flex-приложений с помощью Ant

В то время как Adobe выпустили бесплатный SDK для разработки Flex-приложений, просто неприлично не уметь собирать приложения с его помощью. А если вы работаете на заказ, то бессвязный набор ваших исходных кодов вряд ли вдохновит заказчика: что ему с ними делать? Как собрать из них приложение в случае необходимости? Тем более, что часто заказчик неспециалист: ему что, покупать себе Flex Builder, осваивать его? Если в разработке Flash-приложений мы отдавали fla с классами и на пальцах объясняли что куда (а заказчик вынужден был смириться с тем, что ему понадобится среда разработки Flash - отнюдь не бесплатная), то с бесплатным SDK мы просто обязаны дать заказчику код, который он в любой момент может использовать для сборки приложения.

В качестве лирического отступления замечу, что раньше для того чтобы удовлетворить потребности заказчика во внезапной кастомизации вашего детища, вы вынуждены были создавать конфигурационные xml-и, совершать действия по их загрузке и парсингу: все только для того чтобы снизить вероятность необходимости перекомпилировать приложение. Если предоставить заказчику возможность легко собирать приложения, мы можем дать ему на откуп кастомизацию средствами css, xml-файлов, включаемых в mx:Model, а также resource bundles (то есть упрощенная локализация и тексты менюшек-заголовков-кнопочек, которые могут по вашему незнанию языка содержать ошибки), которые компилируются в приложение. Представьте, как можно упростить жизнь разработчику! При этом вовсе не умаляя преимуществ заказчика, а, возможно, лишь увеличивая их!

Ну и, опять же, все преимущества, которые дает автоматическая сборка: возможность автоматических билдов, легкость в поддержке сложных проектов, возможность запускать с билдом тесты (об этом - в следующих сериях).

В общем, перейдем к демонстрации.

Повторю цель моих изысканий: дать возможность любому, у кого на компьютере распакован Flex SDK, собрать ваше приложение, указав при этом лишь путь к папке с SDK. Причем любому - это любому вне зависимости от платформы (Windows, MacOS, Unix...). При этом я, лично, не хотел бы включать в проект файлы самого sdk без необходимости: зачем? Они ведь и так есть!

Основными кирпичиками нашего эксперимента будут: платформонезависимый компилятор mxmlc, файл flex-config.xml, html-темплэйты (wrappers) и, собственно, сам Ant, о котором мы уже писали.

Пока вы изучаете ссылки, вкратце скажу, что компилятор может принимать параметры как из коммандной строки, так и из специального файла flex-config.xml (который может называться и по-другому). А может и оттуда, и оттуда. Вообще, это довольно сложный и неочевидный путь подобрать все опции чтобы ничего не конфликтовало и все компилилось. Потому я и выкладываю тут проверенный результат. Flex SDK содержит темплэйт такого файла, используемый по умолчанию (в том числе, как я понимаю, и самим Flex Builder'ом). Он находится flex_sdk_2_root\frameworks\flex-config.xml. Понятно, что менять параметры в нем некошерно: во-первых, это отразится и на остальных ваших проектах, а, во-вторых, у другого человека, компилирующего ваш проект, настройки могут оказаться другими.

А содержится там многое: пути к библиотекам, опции компилятора, информация о шрифтах, метаданные генерируемого swf-файла (для тех, кто не в танке: mxmlc занимается только генерацией swf. html и прочее он не генерирует). То есть все, что можно задать компилятору в командной строке. Но представьте себе такую командную строку и структурированный читаемый xml-файл. По-моему, с xml работать приятнее.

Для простоты манипуляции я решил задавать пути к классам из командной строки (чтобы в любой момент легко их было поменять в ant'овском скрипте используя его переменные итд. Хотя и в flex-config.xml имеются для этого токены.). Но мое решение не абсолютное - вы сами можете выработать свое. Остальные опции, не требующие путей, будут задаваться во flex-config.xml. В частности, для задания пути к стандартным библиотекам мы будем использовать токен flexlib, который сами же и зададим.

Далее приведу мой билд-файл чтобы можно было его изучить (весь тестовый проект можно будет скачать - ссылка внизу :):

<?xml version="1.0"?>
<!-- ======================================================================
Jul 15, 2006 7:02:58 PM

Test App
A simple test app

Constantiner (constantiner (at) gmail.com)
====================================================================== -->
<project
name="Test App"
default="build"
basedir=".">

<property file="local.properties"/>
<!-- dirs -->
<property name="flex.sdk.dir" location="."/>

<property name="build.dir" location="build/"/>
<property name="src.dir" location="src/"/>
<property name="lib.src" location="lib/src/"/>
<property name="lib.swc" location="lib/swc/"/>
<property name="flex.config.xml" location="flex-config.xml"/>

<!-- files -->
<property name="mxmlc.jar" location="${flex.sdk.dir}/lib/mxmlc.jar"/>
<property name="main.application" location="${src.dir}/Main.mxml"/>
<property name="output.swf.name" value="test_app"/>
<property name="main.application.out" location="${build.dir}/${output.swf.name}.swf"/>

<!-- wrapper -->
<property
name="wrapper.dir"
location="${flex.sdk.dir}/resources/html-templates/express-installation-with-history"/>
<property name="output.html.name" value="${output.swf.name}.html"/>
<property name="output.html" location="${build.dir}/${output.html.name}"/>
<property name="unnamed.output.html" location="${build.dir}/index.template.html"/>

<property name="swf.width" value="640"/>
<property name="swf.height" value="480"/>
<property name="swf.title" value="Пример очень простого приложения"/>
<property name="swf.version.major" value="9"/>
<property name="swf.version.minor" value="0"/>
<property name="swf.version.revision" value="0"/>
<property name="swf.application" value="${output.swf.name}"/>
<property name="swf.swf" value="${output.swf.name}"/>
<property name="swf.bgcolor" value="#FFFFFF"/>

<description>
A simple test app
</description>

<!-- =================================
target: build
================================= -->
<target
name="build"
depends="clear,init,compile,copy.wrapper,prepare.wrapper"
description="-->Simple test application">
</target>

<!-- - - - - - - - - - - - - - - - - -
target: copy.wrapper
- - - - - - - - - - - - - - - - - -->
<target name="copy.wrapper">
<copy todir="${build.dir}">
<fileset dir="${wrapper.dir}" />
</copy>
</target>

<!-- - - - - - - - - - - - - - - - - -
target: prepare.wrapper
- - - - - - - - - - - - - - - - - -->
<target name="prepare.wrapper">
<move
file="${unnamed.output.html}"
tofile="${output.html}" />
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{width\}"
replace="${swf.width}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{height\}"
replace="${swf.height}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{title\}"
replace="${swf.title}"
encoding="utf-8"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{version_major\}"
replace="${swf.version.major}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{version_minor\}"
replace="${swf.version.minor}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{version_revision\}"
replace="${swf.version.revision}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{application\}"
replace="${swf.application}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{bgcolor\}"
replace="${swf.bgcolor}"/>
<replaceregexp
file="${output.html}"
flags="gs"
match="\$\{swf\}"
replace="${swf.swf}"/>
</target>

<!-- - - - - - - - - - - - - - - - - -
target: compile
- - - - - - - - - - - - - - - - - -->
<target name="compile">
<java
jar="${mxmlc.jar}"
fork="true"
maxmemory="512m"
failonerror="true">
<arg value="+flexlib=${flex.sdk.dir}/frameworks"/>
<arg value="-load-config=${flex.config.xml}"/>
<arg value="-output=${main.application.out}"/>
<arg value="-source-path"/>
<arg value="${src.dir}"/>
<arg value="${lib.src}"/>
<arg value="-library-path+=${lib.swc}"/>
<arg value="${main.application}" />
</java>
</target>

<!-- - - - - - - - - - - - - - - - - -
target: clear
- - - - - - - - - - - - - - - - - -->
<target name="clear">
<delete failonerror="false">
<fileset dir="${build.dir}" includes="**/*.*" />
</delete>
</target>

<!-- - - - - - - - - - - - - - - - - -
target: init
- - - - - - - - - - - - - - - - - -->
<target name="init">
<mkdir
dir="${build.dir}"/>
</target>
</project>

По традиции в файле local.properties мы указываем путь к папке, содержащей Flex SDK (той, что содержит в себе bin, framework, lib итд.). Это единственная переменная величина в проекте, задаваемая ручками при компиляции на другой машине (свойство flex.sdk.dir).

Структура проекта проста: она содержит папку src, содержащую Main.mxml (само приложение) и другие написанные нами as- и mxml-классы. То есть это те артефакты, которые созданы в рамках данного проекта. Есть также папочка lib, которая содержит внешние библиотеки (реюзабельную составляющую). Она делится на две подпапки: src и swc. В первой содержатся библиотеки, поставляемые в виде исходного кода, а во второй, соответственно, в виде swc (в данном проект е таковых нет, но я эту возможность занес).

Ну и по целям нашего билд-файла:


  • clear очищает каталог с прежним билдом.

  • init создает каталог с билдом.

  • compile компилирует наш проект.

  • copy.wrapper копирует наш html-wrapper.

  • prepare.wrapper приводит его в вид, пригодный для использования.


И действительно, мы можем заметить, что в свойстве wrapper.dir ant-файла мы указываем каталог с нужным нам темплейтом (из шести имеющихся). Также в отдельном блоке свойств мы задаем все возможные параметры нашего html-фйла, в него подставляемые. Можно создать и свой вариант по образу и подобию предзаготовленных.

Вот такая вот приятная вещь. И, главное, что с этим можно баловаться: менять wrapper (очень просто!), менять тему (тоже просто: compile.theme в flex-config.xml). Куча приятных вкусностей.

Ну а теперь можно скачать мой простой тестовый проект. Кстати, специально для Роста он содержит в себе более навороченные примеры работы с Flex 2 logging API (а сам по себе тестовый проект, собственно, ничего и не делает. Так, безделица :)

Ну вот, такая сумбурная заметка, которая будет полезна профессионалам. В принципе, можно задавать вопросы :)

Комментариев 12:

Blogger Rip The Spam сообщает:

>>Пока вы изучаете ссылки, вкратце скажу, что компилятор может принимать параметры как из коммандной строки, так и из специального файла flex-config.xml

Так мы же ссылки изучаем! Как мы можем при этом слушать, что ты говоришь? Издеваешься, да?

17 июля, 2006 20:05  
Blogger Unknown сообщает:

Ну это же типо лекция. Типа вы там картинки смотрите, спите, а я распинаюсь, рассказываю. Ты лучше скажи - у тебя получился билд?

17 июля, 2006 20:38  
Blogger Rip The Spam сообщает:

Почти получился.

Cлетает на compile:

[java] Unable to access jarfile D:\Swf\Flex 2\flex_ant_test"C:\Program Files\Adobe\Flex Builder 2\Flex SDK 2\lib\mxmlc.jar +flexlib=C:\Program Files\Adobe\Flex Builder 2\Flex SDK 2/frameworks -load-config=D:\Swf\Flex

Подозреваю, это из-за того, сколь смело я использую пробелы в именах каталогов (d:\swf\flex2\): переношу все во d:\temp, получаю уже более оптимистичную структуру:

compile:
[java] Unable to access jarfile D:\tmp\flex_ant_test"C:\Program Files\Adobe
\Flex Builder 2\Flex SDK 2\lib\mxmlc.jar +flexlib=C:\Program Files\Adobe\Flex Bu
ilder 2\Flex SDK 2/frameworks -load-config=D:\tmp\flex_ant_test\flex-config.xml
-output=D:\tmp\flex_ant_test\build\test_app.swf -source-path D:\tmp\flex_ant_tes
t\src D:\tmp\flex_ant_test\lib\src -library-path+=D:\tmp\flex_ant_test\lib\swc D
:\tmp\flex_ant_test\src\Main.mxml

К сожалению, прямо сейчас зовет работа, так что я еще с этим побалуюсь немного попозже.

17 июля, 2006 23:45  
Blogger Rip The Spam сообщает:

Чорт, имена каталогов -- d:\swf\flex 2\, и d:\tmp\, как же я так.

17 июля, 2006 23:47  
Blogger Unknown сообщает:

Так что в итоге? Получилось?

17 июля, 2006 23:51  
Anonymous Анонимный сообщает:

*сорри за оффтоп*
помнишь, ты на семинаре риалтайма цитировал Фаулера, прикольную фразу про то, что чем больше ИДЕ за тебя делает, тем больше времени остается на то, чтобы пить пиво. :)

можешь вспомнить точную цитату или дать ссылку, откуда это взято? :)

18 июля, 2006 13:20  
Blogger Unknown сообщает:

Фаулер. Рефакторинг. Улучшение существующего кода. СПб. Символ, 2004. Стр. 64:
Я очень ленивый программист. Моя лень проявляется в частности, в том, что я не запоминаю деталей кода, который пишу. На самом деле, опасаясь перегрузить свою голову, я умышленно стараюсь не запоминать того, что могу найти. Я стараюсь записывать все, что в противном случае пришлось бы запомнить, в код. Благодаря этому я меньше волнуюсь из-за того, что Old Peculier [Jackson] убивает клетки моего головного мозга.

И там примечание, что Old Peculier - это сорт пива.

Думаю, что речь идет об этой цитате :)

18 июля, 2006 14:31  
Anonymous Анонимный сообщает:

спасибо. :) хм. тоже хорошая формулировка, но тогда ты её сформулировал более лаконично :)

18 июля, 2006 20:16  
Blogger Unknown сообщает:

Думаю, что тогда это была вольная интерпретация :)

18 июля, 2006 23:08  
Anonymous Анонимный сообщает:

Кость, давай закончим с самзнаешь чем и попробуем сделать work environment наподобие Rails Live CD? Не обязательно для запуска чисто с диска, но в идеале разворачивания всего, что нужно для сборки/работы, 1-2 кликами.

Я долгое время изучаю и трясу знакомых сишников, как делается пакетная установка большого количества софта в их компаниях. Пока, если честно, все весьма глухо, но если речь об Eclipse/Ant, то их мы поставим. А там и Flex, и FDT, и JDT, и RadRails.

И Red5 поставится. Дело за малым... слушай, да вообще почти ни за чем :)

20 июля, 2006 06:00  
Blogger Unknown сообщает:

Но это, я думаю, уже осенью. Но обсудить можно и сейчас :)

20 июля, 2006 09:48  
Anonymous Анонимный сообщает:

Бизнес на ровном месте! Сетка тематических сайтов,... Кто хочет много трафика с YouTube? Новые идеи полу...

28 августа, 2007 20:44  

Отправить комментарий

Вернуться на главную