Как собрать jar файл с зависимостями в многомодульном проекте в Gradle?

Untitled_Diagram.png

Есть Module 1 и Module 2, оба зависят от модуля Common. Зависимости от сторонних библиотек в модулях пересекаются.

Зависимости Common (файл common/build.gradle):
dependencies {
    compile 'com.google.code.gson:gson:2.2.4'
    compile 'org.apache.logging.log4j:log4j-core:2.0-rc2'
}
Module-2 (файл module-2/build.gradle):
dependencies {
    compile 'com.google.code.gson:gson:2.2.4'
    compile 'org.apache.logging.log4j:log4j-core:2.0-rc2'
    compile project(':common')
}
Module-1 (файл module-1/build.gradle):
dependencies {
    compile 'com.google.code.gson:gson:2.2.4'
    compile 'org.apache.logging.log4j:log4j-core:2.0-rc2'
    compile 'com.google.guava:guava:17.0'     //  её нет в Common
    compile project(':common')
}


Задача: создать jar файлы для Module-1 и Module-2 со всеми необходимыми зависимостями внутри, которые можно было бы запускать таким образом: java -jar module-1.jar.

Первая попытка: сделать таску jar, отвечающую за итоговый файл, одинаковой для всех трех модулей:
jar {
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    //настройка manifest'a опущена
}
Вышеприведенный способ работает, но в jar файл module-1.jar (и в module-2.jar) дважды попадает пересечение зависимостей для Module-1 и Common, итого размер файла равен:
SIZE( src_module1 + 2 (dep_module1 ∩ dep_common) + ( dep_module1 \ dep_common) )

Вторая попытка: исключить из таски jar модуля Common файлы библиотек:
jar {
    //from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
В итоге в common.jar попадают только class файлы модуля Common , как следствие, в файл module-1.jar зависимости архивируются только один раз, уже хорошо.
Но возникает проблема: в случае, если у Common есть какая-то зависимость, а у Module-1 её нет, на уровне Module-1 придется описывать все необходимые зависимости Common, что неприемлимо.

Третья попытка: вернуть Common к шагу 1, а содержимое результатируещего jar описать вручную всеми зависимостями, исключая их пересечение Пример для Module-1:
jar {
    // из зависимостей добавляем только ту, которая в названияя содержит common
    from { configurations.compile.collect {  it.toString().contains("common") ? zipTree(it) : false } }
    // тут нужно подключить 'com.google.guava:guava:17.0'
}
Этот вариант показался правильнее, но труден для синтаксического описания.

Вопрос 1. Как указать в качестве источника from для таски jar
a) модуль проекта (код в примере выше - ущербен);
b) сторонюю библиотеку.
Вопрос 2. Существуют ли правильное решение данной задачи?
  • Вопрос задан
  • 6402 просмотра
Решения вопроса 1
@kubashin_a
Совсем недавно задавался похожим вопросом...

1) Вместо configurations.compile лучше использовать configurations.runtime.

2) В моём случае следующее утверждение не верно:
Но возникает проблема: в случае, если у Common есть какая-то зависимость, а у Module-1 её нет, на уровне Module-1 придется описывать все необходимые зависимости Common, что неприемлимо.

В нашем проекте 'module-1' и 'common' это два независимых проекта. Commons публикуется в Artifactory (или локальный репозиторий), при этом pom файл содержит все прописанные для него зависимости. При сборке module-1 "вытягиваются" как его собственные зависимости так и зависимости специфичные для common, при этом они все попадают в итоговый jar.

3)
Ещё одно отличие нашего проекта в том, что jar файлы не распаковываются, а подгружаются с помощью jar-in-jar-loader.zip из eclipse (не нашёл как его скачать отдельно, но из eclipse его можно добыть так: blog.eqlbin.ru/2012/04/runnable-jar-eclipse.html ). Итоговый таск выглядит примерно так:
task staticJar (type: Jar, dependsOn: 'classes') {
    archiveName = 'static.jar'
    destinationDir = releaseDir
    from sourceSets.main.output
    from zipTree("${libsDir}/jar-in-jar-loader.zip")
    into ('libs') {
        from configurations.runtime
    }
    def manifestClasspath = './ ' + configurations.runtime.collect { 'libs/' + it.getName() }.join(' ')
    manifest {
        attributes(
            'Main-Class': 'org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader',
            'Rsrc-Main-Class': 'org.company.main.class',
            'Rsrc-Class-Path': manifestClasspath
        )
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
anyd3v
@anyd3v
вы можете сделать exclude дублирующихся библиотек в модуле Common
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы