Если ваш Android-проект компилируется и собирается с помощью Maven или SBT (а может, и чем-нибудь другим, отличным от Ant), то вы уже используете механизм управления зависимостями, предоставляемый, что называется, «из коробки». Однако, если вы используете Ant, либо просто собираете приложение в Eclipse с помощью ADT-плагина, то такой функциональности у вас нет, и каталог lib в корне проекта наполняется вручную, а желания или возможности переходить на использование Maven'а конечно же нет. Тогда, есть ли возможность не складировать jar-файлы вручную, не держать их, бинарных, в VCS, не добавлять их самим в «Build Path» в настройках проекта? Конечно есть, какие вопросы!
Думаю, что в мире «большой» Java многие слышали или даже работали с таким инструментом, который называется Ivy — «The agile dependency manager». Кстати, если вы используете SBT, то вы автоматически используете и Ivy, просто не знаете об этом. Что же может Ivy? Грубо говоря, он предназначен ровно для одной вещи — управления зависимостями вашего проекта от сторонних библиотек. Ivy не является системой сборки, он лишь может дополнять её.
Если в проекте используется одна-две библиотеки, то, возможно, использование такого инстурмента, как Ivy, покажется вам ненужным «оверхедом», однако, он может очень сильно облегчить жизнь, если ваш проект растет и «обрастает» функционалом, предоставленным третьей стороной.
Нашей целью будет приложение, которое можно собирать как из Eclipse, так и консольно. Для работы с Ivy из консоли будем использовать Ant, которому нужно сообщить об этом — я просто положил ivy.jar в ~/.ant/lib/. Ещё нам может понадобится библиотека JSch (реализация ssh2) — jsch.jar, которую тоже можно положить в ~/.ant/lib/. Не используйте устаревшие версии, с ними были проблемы. Более на этом этапе ничего делать не нужно, переходим к настройке проекта.
Следующим этапом нам нужно настроить Ivy на работу с используемыми хранилищами пакетов. Для этого есть т.н. resolvers — описания хранилищ, которые задают их URL, структуру и другие характеристики. Для нашего примера я выбрал 3 используемые библиотеки — ZXing, ACRA и RoboGuice. Последний может быть обнаружен в mvnrepository.com, две другие используют собственные Maven-совместимые хранилища. Файл с настройками Ivy называется ivysettings.xml и располагается в каталоге с build.xml — в нашем случае это корень проекта. Вот его содержимое для данного примера:
Резолвер ibiblio является стандартным из пакета поставки Ivy и подключает хранилища, расположенные на maven.org и другие. Далее, настраиваются хранилища с ZXing и ACRA. Обратите внимание на атрибут pattern, определяющий структуру хранилища. Также, резолверы объединены в цепочку chained, определяющую порядок перебора хранилищ при поиске нужного пакета.
Итак, хранилища настроены, далее опишем сами зависимости. Для этого создаётся файл ivy.xml (в одном каталоге с ivysettings.xml). Его содержимое:
Секция info является необходимой, а её назначение, думаю, понятно из примера. За ней идёт описание самих зависимостей. Подробнее стоит остановится на guice и roboguice. Первая библиотека является зависимостью для второй, и для работы достаточно их двоих (согласно документации самого RoboGuice), но guice «потянет» свои зависимости, потому оба пакета прописаны с атрибутом transitive=«false», что предотвращает дальнейшее разрешение зависимостей для них. При такой конфигурации, также будут предоставлены дополнительные пакеты -javadoc и -sources, если они есть. В случае, если вы не нуждаетесь в них, то нужно добавить секцию configurations в ivy.xml и создать пустую конфигурацию:
и в описания зависимостей добавить атрибут conf=«default».
Последним этапом станет модификация build.xml проекта (как его генерировать — описано в официальной документации). Впрочем, если вы не планируете использовать Ant, то этот шаг можно пропустить. Сначала, необходимо добавить пространство имён (namespace) Ivy в описание проекта:
И добавить новую цель (target):
В качестве последнего штриха, можно добавить нашу цель в процесс сборки приложения (если вы собираете проект с помощью Ant):
Теперь, если в корневом каталоге проекта выполнить команду
то вы должны увидеть процесс загрузки пакетов, а результатом должен стать каталог lib, содержащий jar-файлы библиотек.
Для начала, нужно установить плагин IveDE для Eclipse. Плагин доступен в Eclipse Marketplace. После установки, в панели инструментов должна появится кнопка «Resolve All Dependencies»:
Теперь, подключим зависимости к проекту: открываем свойства проекта -> Java Build Path -> переходим на вкладку Libraries -> Add Library -> в появившемся списке выбираем IvyDE Managed Dependencies -> нажимаем Next. Далее, нас спросят о местонахождении файла ivy.xml и других настройках. В данном случае, можно оставить все как есть и перейти на вкладку Settings, в которой указать в свойстве Ivy settings path путь к нашему файлу ivysettings.xml. После этого можно нажать Finish. Теперь, все библиотеки, которые были прописаны в ivy.xml в качестве зависимостей, должны быть подключены к проекту, и видны в Package Explorer:
При внесении изменений в ivy.xml Eclipse будет запускать Ivy-задачу Resolve. Так же, можно запустить ее с помощью Ant View, добавив в него build.xml проекта.
Частенько бывает, что необходимых библиотек нет в публичных хранилищах, а может, вы будете использовать библиотеку, написанную другой командой вашего проекта и вам надо подключить её как зависимость, или ещё что-нибудь. Хоть создание собственного Ivy-хранилища и несколько выходит за рамки данной заметки, но я решил включить описание очень базовой (но вполне рабочей) конфигурации.
В качестве подопытного экземпляра я взял библиотеку Droid@Screen, а располагаться хранилище будет в /var/ivy. Теперь, следует выбрать структуру расположение артефактов — а данном случае это будет /организация/название/версия/артефакт. Для нашей тестовой библиотеки, в качестве организации в именах пакетов используется com.ribomation, тогда структура хранилища будет выглядеть следующим образом:
Обратите внимание на файл ivy.xml — в нем содержится описание артефакта, его зависимостей, конфигураций и прочего. Минимальное его содержимое таково:
Далее, нужно подключить наше локальное хранилище в ivysettings.xml (точками отмечены уже существующие теги, ваш КО):
Обратите внимание на аттрибут m2compatible — его использование приведет к тому, что Ivy будет заменять точки на системные разделители в имени организации, соответственно и поиск артефактов будет вестись не в каталоге com.ribomation, а в их иерархии — com/ribomation. Теперь, добавим зависимость в ivy.xml:
Собственно, это все — можно запускать задачу resolve.
Если вам интересно иметь удалённое хранилище, доступ к которому осуществлялся по SSH, то нужно использовать в качестве резолвера не filesystem, а ssh:
В остальном, они идентичны.
За время работы с Ivy и Eclipse была замечена следующая ошибка — если ваш проект разбит на несколько (основной, тесты и т.д.), то при их открытии Ivy-плагин почему-то путает зависимости между проектами. Решение очень простое — достаточно просто нажать кнопку «Resolve All Dependencies» на панели инструментов.
Итак, цель данной заметки была в том, чтобы показать, как можно, не меняя существующую структуру (и инфраструктуру) проекта, добавить в него механизм разрешения зависимостей и избавится от ручного труда по поддержанию и добавлению их в проект. Исходный код проекта находится здесь. Надеюсь, что информация будет вам полезна.
Думаю, что в мире «большой» Java многие слышали или даже работали с таким инструментом, который называется Ivy — «The agile dependency manager». Кстати, если вы используете SBT, то вы автоматически используете и Ivy, просто не знаете об этом. Что же может Ivy? Грубо говоря, он предназначен ровно для одной вещи — управления зависимостями вашего проекта от сторонних библиотек. Ivy не является системой сборки, он лишь может дополнять её.
Если в проекте используется одна-две библиотеки, то, возможно, использование такого инстурмента, как Ivy, покажется вам ненужным «оверхедом», однако, он может очень сильно облегчить жизнь, если ваш проект растет и «обрастает» функционалом, предоставленным третьей стороной.
Подготовка
Нашей целью будет приложение, которое можно собирать как из Eclipse, так и консольно. Для работы с Ivy из консоли будем использовать Ant, которому нужно сообщить об этом — я просто положил ivy.jar в ~/.ant/lib/. Ещё нам может понадобится библиотека JSch (реализация ssh2) — jsch.jar, которую тоже можно положить в ~/.ant/lib/. Не используйте устаревшие версии, с ними были проблемы. Более на этом этапе ничего делать не нужно, переходим к настройке проекта.
Настройка
Следующим этапом нам нужно настроить Ivy на работу с используемыми хранилищами пакетов. Для этого есть т.н. resolvers — описания хранилищ, которые задают их URL, структуру и другие характеристики. Для нашего примера я выбрал 3 используемые библиотеки — ZXing, ACRA и RoboGuice. Последний может быть обнаружен в mvnrepository.com, две другие используют собственные Maven-совместимые хранилища. Файл с настройками Ivy называется ivysettings.xml и располагается в каталоге с build.xml — в нашем случае это корень проекта. Вот его содержимое для данного примера:
<ivysettings>
<resolvers>
<ibiblio name="maven2" m2compatible="true" />
<url name="acra" m2compatible="true">
<artifact
pattern="http://acra.googlecode.com/svn/repository/releases/[organization]/[module]/[revision]/[module]-[revision].jar" />
</url>
<url name="zxing" m2compatible="true">
<artifact
pattern="http://mvn-adamgent.googlecode.com/svn/maven/release/[organization]/[revision]/[module]-[revision].jar" />
</url>
<chain name="chained" returnFirst="true">
<resolver ref="maven2" />
<resolver ref="acra" />
<resolver ref="zxing" />
</chain>
</resolvers>
<settings defaultResolver="chained" />
</ivysettings>
Резолвер ibiblio является стандартным из пакета поставки Ivy и подключает хранилища, расположенные на maven.org и другие. Далее, настраиваются хранилища с ZXing и ACRA. Обратите внимание на атрибут pattern, определяющий структуру хранилища. Также, резолверы объединены в цепочку chained, определяющую порядок перебора хранилищ при поиске нужного пакета.
Итак, хранилища настроены, далее опишем сами зависимости. Для этого создаётся файл ivy.xml (в одном каталоге с ivysettings.xml). Его содержимое:
<ivy-module version="2.0">
<info organisation="com.example" module="ivy-android-example" revision="0.1" />
<dependencies>
<dependency org="org.acra" name="acra" rev="4.2.3" />
<dependency org="com.google.inject" name="guice" transitive="false" rev="2.0-no_aop" />
<dependency org="org.roboguice" name="roboguice" rev="1.1.2" transitive="false" />
<dependency org="com.google.zxing.core" name="core" rev="1.6" />
</dependencies>
</ivy-module>
Секция info является необходимой, а её назначение, думаю, понятно из примера. За ней идёт описание самих зависимостей. Подробнее стоит остановится на guice и roboguice. Первая библиотека является зависимостью для второй, и для работы достаточно их двоих (согласно документации самого RoboGuice), но guice «потянет» свои зависимости, потому оба пакета прописаны с атрибутом transitive=«false», что предотвращает дальнейшее разрешение зависимостей для них. При такой конфигурации, также будут предоставлены дополнительные пакеты -javadoc и -sources, если они есть. В случае, если вы не нуждаетесь в них, то нужно добавить секцию configurations в ivy.xml и создать пустую конфигурацию:
<configurations>
<conf name="default" visibility="public" />
</configurations>
и в описания зависимостей добавить атрибут conf=«default».
Последним этапом станет модификация build.xml проекта (как его генерировать — описано в официальной документации). Впрочем, если вы не планируете использовать Ant, то этот шаг можно пропустить. Сначала, необходимо добавить пространство имён (namespace) Ivy в описание проекта:
<project xmlns:ivy="antlib:org.apache.ivy.ant" name="IvyAndroidExample" default="help">
И добавить новую цель (target):
<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve />
</target>
В качестве последнего штриха, можно добавить нашу цель в процесс сборки приложения (если вы собираете проект с помощью Ant):
<target name="-pre-build" depends="resolve" />
Теперь, если в корневом каталоге проекта выполнить команду
$ ant resolve
то вы должны увидеть процесс загрузки пакетов, а результатом должен стать каталог lib, содержащий jar-файлы библиотек.
Интеграция с Eclipse
Для начала, нужно установить плагин IveDE для Eclipse. Плагин доступен в Eclipse Marketplace. После установки, в панели инструментов должна появится кнопка «Resolve All Dependencies»:
Теперь, подключим зависимости к проекту: открываем свойства проекта -> Java Build Path -> переходим на вкладку Libraries -> Add Library -> в появившемся списке выбираем IvyDE Managed Dependencies -> нажимаем Next. Далее, нас спросят о местонахождении файла ivy.xml и других настройках. В данном случае, можно оставить все как есть и перейти на вкладку Settings, в которой указать в свойстве Ivy settings path путь к нашему файлу ivysettings.xml. После этого можно нажать Finish. Теперь, все библиотеки, которые были прописаны в ivy.xml в качестве зависимостей, должны быть подключены к проекту, и видны в Package Explorer:
При внесении изменений в ivy.xml Eclipse будет запускать Ivy-задачу Resolve. Так же, можно запустить ее с помощью Ant View, добавив в него build.xml проекта.
Локальное хранилище
Частенько бывает, что необходимых библиотек нет в публичных хранилищах, а может, вы будете использовать библиотеку, написанную другой командой вашего проекта и вам надо подключить её как зависимость, или ещё что-нибудь. Хоть создание собственного Ivy-хранилища и несколько выходит за рамки данной заметки, но я решил включить описание очень базовой (но вполне рабочей) конфигурации.
В качестве подопытного экземпляра я взял библиотеку Droid@Screen, а располагаться хранилище будет в /var/ivy. Теперь, следует выбрать структуру расположение артефактов — а данном случае это будет /организация/название/версия/артефакт. Для нашей тестовой библиотеки, в качестве организации в именах пакетов используется com.ribomation, тогда структура хранилища будет выглядеть следующим образом:
/var/ivy └── com └── ribomation └── droidAtScreen └── 0.5.1 ├── droidAtScreen.jar └── ivy.xml
Обратите внимание на файл ivy.xml — в нем содержится описание артефакта, его зависимостей, конфигураций и прочего. Минимальное его содержимое таково:
<ivy-module version="2.0">
<info organisation="com.ribomation"
module="droidAtScreen"
revision="0.5.1" />
<publications>
<artifact />
</publications>
</ivy-module>
Далее, нужно подключить наше локальное хранилище в ivysettings.xml (точками отмечены уже существующие теги, ваш КО):
<ivysettings>
<property name="repo.dir" value="/var/ivy"/>
<resolvers>
<...>
<filesystem name="local" m2compatible="true">
<ivy pattern="${repo.dir}/[organization]/[module]/[revision]/[artifact].[ext]" />
<artifact pattern="${repo.dir}/[organization]/[module]/[revision]/[module].jar" />
</filesystem>
<chain name="chained" returnFirst="true">
<...>
<resolver ref="local" />
</chain>
</resolvers>
<...>
</ivysettings>
Обратите внимание на аттрибут m2compatible — его использование приведет к тому, что Ivy будет заменять точки на системные разделители в имени организации, соответственно и поиск артефактов будет вестись не в каталоге com.ribomation, а в их иерархии — com/ribomation. Теперь, добавим зависимость в ivy.xml:
<dependency org="com.ribomation" name="droidAtScreen" rev="0.5.1" />
Собственно, это все — можно запускать задачу resolve.
Если вам интересно иметь удалённое хранилище, доступ к которому осуществлялся по SSH, то нужно использовать в качестве резолвера не filesystem, а ssh:
<ssh name="shared" host="example.com" user="${user.name}" keyFile="${user.home}/.ssh/key">
<...>
</ssh>
В остальном, они идентичны.
Замечания
За время работы с Ivy и Eclipse была замечена следующая ошибка — если ваш проект разбит на несколько (основной, тесты и т.д.), то при их открытии Ivy-плагин почему-то путает зависимости между проектами. Решение очень простое — достаточно просто нажать кнопку «Resolve All Dependencies» на панели инструментов.
Вместо заключения
Итак, цель данной заметки была в том, чтобы показать, как можно, не меняя существующую структуру (и инфраструктуру) проекта, добавить в него механизм разрешения зависимостей и избавится от ручного труда по поддержанию и добавлению их в проект. Исходный код проекта находится здесь. Надеюсь, что информация будет вам полезна.