#java #ant #dependency-management #ivy
#java #муравей #управление зависимостями #плющ
Вопрос:
У меня есть 3 проекта следующей структуры:
App
| |
...
| |
| --lib
| | |
| | --...
| |
| --dist
|
Lib
| |
...
| |
| --lib
| | |
| | --sublib-1.0.jar
| |
| --dist
| |
| --lib-1.0.jar
|
SubLib
|
...
|
--dist
|
--sublib-1.0.jar
Которые имеют следующее отношение:
App <-- Lib <-- SubLib
Я использую apache ivy для извлечения зависимостей для обоих App
и Lib
. Зависимости описаны следующим образом:
ivy.xml
из Lib
:
<ivy-module version = "2.0">
<info organisation = "com.test.lib" module = "lib"/>
<dependencies>
<dependency org = "com.test.sub.lib" name = "sublib" rev = "1.0" conf = "compile->default"/>
</dependencies>
</ivy-module>
ivy.xml
из App
:
<ivy-module version = "2.0">
<info organisation = "com.test.app" module = "App"/>
<dependencies>
<dependency org = "com.test.lib" name = "lib" rev = "1.0" conf = "compile->default"/>
</dependencies>
</ivy-module>
ivysettings.xml
:
<ivysettings>
<settings defaultResolver = "local"/>
<resolvers>
<filesystem name = "local">
<artifact pattern = "${ivy.settings.dir}/SubLib/dist/[artifact]-[revision].[ext]"/>
<artifact pattern = "${ivy.settings.dir}/Lib/dist/[artifact]-[revision].[ext]"/>
</filesystem>
</resolvers>
<modules>
<module organisation = "com.test.ivytest" resolver = "local"/>
</modules>
</ivysettings>
Ожидаемый результат: после выполнения ivy:retrieve
оба sublib-1.0.jar
и lib-1.0.jar
должны присутствовать в App/lib
Фактический результат: lib-1.0.jar
присутствует только в App/lib
. Сгенерированный ivy-отчет для App
не содержит никаких упоминаний о sublib
зависимости от lib
. Ничего подобного также нет в журналах ant ivy во время сборки.
Примечание: lib-1.0.jar
не создается как fat-jar.
Чего мне не хватает в этой конфигурации?
Обновить
Я немного подумал, и единственный вывод, к которому я пришел, заключается в том, что эта проблема действительно является неправильной конфигурацией. Судя по тому факту, что транзитивная зависимость не извлекается, мы можем с уверенностью сказать, что ivy не имеет какой-либо информации, когда она разрешается lib
. И это имеет смысл, потому Lib/dist
что папка может быть где угодно в файловой системе. Единственным способом получить информацию о транзитивной зависимости было бы наличие соответствующего ivy.xml
где-то рядом с этим jar. Чего нет. Это немного подтверждается сообщением в журналах [ivy:retrieve] local: no ivy file found for com.test.lib#lib;1.0: using default data
. Единственный способ сохранения информации — это кэширование данных %user%/.ivy/cache
. Там сгенерированные [org]-[artifact]-[conf].xml
файлы содержат информацию о зависимостях. Поэтому я предполагаю, что для правильной работы мне придется использовать кеш на уровне разрешения приложения.
Есть ли в этом какой-то смысл, или я снова явно ошибаюсь?
Комментарии:
1. ivy: resolve следует вызвать перед извлечением. Какие ошибки вы получаете? Есть ли что-то вроде «отсутствует конфигурация»?
2. Из того, что я смог понять, так это то, что в этом нет необходимости. Я вижу для
[ivy:retrieve]
разделов в журналах, что он выполняет разрешение. Я не получаю ошибок, просто отсутствуют транзитивные зависимости jar.3. Хорошо (также читая обновленную часть), пожалуйста, очистите кэш ivy и запустите его снова. Также добавьте к вопросу ant-скрипты, которые вы используете для публикации
lib
иsublib
.4.
ivy:cleancache
явно вызывается воApp.clean
время выполнения задачи ant. Это просто не упоминается. В данный момент я тестирую одну идею, и если она сработает должным образом, я опубликую ее здесь.5. Можете ли вы показать полную команду ant, которую вы используете?
Ответ №1:
Итак, проблема действительно заключалась в плохой конфигурации и моем непонимании этого. Вот подробное объяснение, как заставить его работать. Я не очень хорошо разбираюсь в терминологии, поэтому, возможно, я неправильно использовал некоторые слова здесь. Давайте посмотрим на конфигурацию проекта и что к чему.
Sublib
это будет зависимость от времени выполнения Lib
, поскольку она зависит от времени компиляции guava
.
SubLib
| `lib
| `guava-19.0.jar
|
`dist
| `--sublib-1.0.jar
|
`src
`...
Итак, нам нужно создать соответствующие конфигурации в SubLib ivy.xml
:
<ivy-module version="2.0">
<info organisation="com.test.sub.lib" module="sublib"/>
<configurations>
<conf name="runtime" visibility="public"/>
</configurations>
<dependencies>
<dependency org="com.google" name="guava" rev="19.0" conf="runtime->default"/>
</dependencies>
</ivy-module>
Здесь, объявляя runtime
конфигурацию, мы заявляем, что это ivy.xml
описывает модуль, который является зависимостью от времени выполнения. И поскольку у guava нет такого файла, мы описываем его как default. Здесь довольно стандартно. Теперь, чтобы другие знали, что sublib-1.0.jar
на самом деле имеет зависимость от guava-19.0.jar
, нам нужно опубликовать его в репозитории, чтобы такая информация существовала в виде файла ivy-[version].xml
рядом с jar. Я решил опубликовать в build
папке. Для этого ivysettings.xml
необходимо содержать распознаватель, который помогает сопоставлять шаблоны файлов для публикации и последующего извлечения, когда мы будем разрешать из Lib
.
<ivysettings>
<settings defaultResolver="filesystem-resolver"/>
<resolvers>
<filesystem name="sublib-resolver">
<ivy pattern="${ivy.settings.dir}/SubLib/dist/repo/ivy-[revision].xml"/>
<artifact pattern="${ivy.settings.dir}/SubLib/dist/repo/[artifact]-[revision].[ext]"/>
</filesystem>
<filesystem name="filesystem-resolver">
<artifact pattern="${ivy.settings.dir}/SubLib/lib/[artifact]-[revision].[ext]"/>
</filesystem>
</resolvers>
<modules>
<module name="sublib" organisation="com.test.sub.lib" resolver="sublib-resolver"/>
</modules>
</ivysettings>
sublib-resolver
позволит найти соответствующий ivy-[revision].xml
, который содержит информацию о зависимостях и местоположении jar
. Тогда filesystem-resolver
как мы найдем нашу guava
зависимость. Теперь мы просто публикуем sublib
с ant
помощью вызова нашего преобразователя:
<target name="publish">
<ivy:publish artifactspattern="${dist.dir}/[artifact]-[revision].[ext]"
resolver="sublib-resolver"
overwrite="true"
pubrevision="${revision}"
/>
</target>
Теперь к Lib
. Библиотека будет зависимостью во время компиляции App
, и мы описываем ее как таковую в ivy.xml
и объявляем SubLib
для нее зависимостью во время выполнения:
<ivy-module version="2.0">
<info organisation="com.test.lib" module="lib"/>
<configurations>
<conf name="compile" visibility="public"/>
<conf name="runtime" extends="compile" visibility="public"/>
</configurations>
<dependencies>
<dependency org="com.test.sub.lib" name="sublib" rev="2.0" conf="runtime->compile"/>
</dependencies>
</ivy-module>
Вот где конфигурация вступает в игру и что я сначала не понял. runtime->compile
Левая сторона понятна: sublib
была объявлена как зависимость во время выполнения, и мы присвоили ей runtime
conf в ее файле ivy. В правой части стрелки мы указываем, что нам sublib
также нужны зависимости во время компиляции. И тот, который был настроен как таковой guava
. Он будет найден распознавателем и также извлечен. Итак, нам Lib
также нужен распознаватель, поэтому полный ivysettings.xml
файл будет выглядеть следующим образом:
<ivysettings>
<properties file="${ivy.settings.dir}/ivysettings.properties"/>
<settings defaultResolver="filesystem-resolver"/>
<resolvers>
<filesystem name="sublib-resolver">
<ivy pattern="${ivy.settings.dir}/SubLib/dist/repo/ivy-[revision].xml"/>
<artifact pattern="${ivy.settings.dir}/SubLib/dist/repo/[artifact]-[revision].[ext]"/>
</filesystem>
<filesystem name="lib-resolver">
<ivy pattern="${ivy.settings.dir}/Lib/dist/repo/ivy-[revision].xml"/>
<artifact pattern="${ivy.settings.dir}/Lib/dist/repo/[artifact]-[revision].[ext]"/>
</filesystem>
<filesystem name="filesystem-resolver">
<artifact pattern="${ivy.settings.dir}/SubLib/lib/[artifact]-[revision].[ext]"/>
</filesystem>
</resolvers>
<modules>
<module name="sublib" organisation="com.test.sub.lib" resolver="sublib-resolver"/>
<module name="lib" organisation="com.test.lib" resolver="lib-resolver"/>
</modules>
</ivysettings>
И опубликовать Lib
в Lib
build.xml
:
<target name="publish">
<ivy:publish artifactspattern="${dist.dir}/[artifact]-[revision].[ext]"
resolver="lib-resolver"
overwrite="true"
pubrevision="${revision}"
/>
</target>
Теперь к основной проблеме: транзитивное извлечение. Конфигурации. В App
‘s ivy.xml
нам нужно точно указать, что мы хотим транзитивных зависимостей. Информации о том, что они существуют, хранящихся в репозиториях, недостаточно. Необходимо указать это в App
‘s ivy.xml
:
<configurations>
<conf name="compile" visibility="public"/>
</configurations>
<dependencies>
<dependency org="com.test.lib" name="lib" rev="1.0" conf="compile->compile; compile->runtime"/>
</dependencies>
Здесь происходит следующее: объявляя compile
conf, мы указываем, что App
у него есть конфигурация компиляции. Первая цепочка стрелок, как и раньше, указывает, что мы (модуль App
, настроенный на компиляцию) хотим вызвать зависимость lib
, настроенную на компиляцию. Стороны со стрелками влево и вправо соответственно. И второй набор стрелок указывает, что мы (модуль, App
настроенный на компиляцию) хотим получить зависимости, настроенные во время выполнения lib
! Что есть sublib
. И поскольку он поставляется вместе с guava
ним, он также извлекается.
Немного запутанное объяснение, и, возможно, оно не самое элегантное для решения, но это единственный способ, которым мне удалось заставить это работать должным образом вообще. Если кто-нибудь знает какие-либо лучшие способы сделать это, мы будем признательны за любую помощь.