Апачи Айви. Транзитивные зависимости не восстановлены

#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 ним, он также извлекается.


Немного запутанное объяснение, и, возможно, оно не самое элегантное для решения, но это единственный способ, которым мне удалось заставить это работать должным образом вообще. Если кто-нибудь знает какие-либо лучшие способы сделать это, мы будем признательны за любую помощь.