Gradle: Вопрос по модульному проекту

Версия Minecraft
1.7.10
1,159
38
544
Добрый день, товарищи. У меня очень необычная задача, к которой я не знаю как подступиться.

Есть у меня мод с апи и несколько вариантов его реализации. В корне моего проекта я хочу иметь папку examples, внутри которой будут лежать сабпроджекты, реализующий мой апи (т.е. полностью самостоятельные моды):

Bash:
ApiRootDirectory // Корень с основным модом
├───eclipse
│   ├───mods
│   └───...
│
├───examples
│   ├───case1 // Мод-пример #1
│   │   └───src
│   │       └───main
│   │           ├───java
│   │           └───resources
│   │
│   ├───case2 // Мод-пример #2
│   │   └───src
│   │       └───main
│   │           ├───java
│   │           └───resources
│   │
│   └─── ... // и так далее
│
└───src // Код основного мода
    └───main
        ├───java
        └───resources

При запуске игры из IDE с основным проектом я хочу чтобы все сабпрожекты собрались в бинарник, поместились в mods и определились в игре как самостоятельные моды. Эта фича мне нужна для того, чтобы тестировать api при различных реализациях и выверять ошибки. Билдить примеры и запихивать их в mods вручную - дело отстойное, я так не хочу. Включать их в бинарник основного мода - тоже.

Сейчас курю доки по градлу и груви, но доки написаны для новых версий, а я сижу на gradle 2.0, которая шла из коробки при разворачивании проекта. Обновил градл до 5.4.1 - сломал билдконфиг. Чувствую что для forge-плагина обновления градла будут фатальны. Посему ваш совет будет весьма кстати. Как мне решить мою задачу?
 

tox1cozZ

aka Agravaine
8,454
598
2,890
1,159
38
544
Сижу, разбираюсь... Вот смотрите что получилось

Для начала как-то нужно добавить сабпроджект. Создал в корне проекта файл settings.gradle с таким содержанием:
Код:
include ':examples:case1'
Вместо того, чтобы добавить модуль case1 градл почему-то еще и добавил модуль examples, хотя там нет нифига. Итого мы имеем 3 модуля: основной проект, сабпроджект "case1" и пустую папку "examples", которую градл принимает за модуль.

Далее нужно сделать так чтобы сабпроджект "унаследовал" конфиг из основного модуля. Открываем build.gradle сабпроджекта и пишем:
Код:
apply plugin: 'java' // Я вообще скопипастил все это из местного мануала по модульному проекту, так что хз насколько это верно и нужно

apply from: "$rootDir/build.gradle"

version = ""
archivesBaseName = "Mod Name"

sourceCompatibility = 1.6

IDEA увидела модуль case1, gradlew build прошел успешно. Запускаю игру из IDE, а там case1 не добавился в список модов. Начинаю ковырять отладчиком класс Loader и обнаруживаю что он загружает моды из classpath'а основного модуля. Я начинаю гуглить как добавить сабпроджект в класспач основного модуля и узнаю, что нужно добавить в основной модуль зависимость от сабпроджекта:
Код:
dependencies {
    compile project(':examples:case1')
}
Синхронизирую проект в IDE, запускаю игру и все ок: сабпроджект и основной модуль появились в виде отдельных загруженных модов!

Но враг подкрался незаметно. При попытке сделать gradlew build в основном модуле мы получаем Circular dependency!
Код:
FAILURE: Build failed with an exception.

* What went wrong:
Circular dependency between the following tasks:
:case1:apiClasses
\--- :case1:compileApiJava
     \--- :case1:jar
          \--- :case1:classes
               \--- :case1:compileJava
                    +--- :case1:apiClasses (*)
                    \--- :case1:jar (*)

(*) - details omitted (listed previously)


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Если уберем зависимость из билд-скрипта - моды соберутся, но сабпрождект в игру не добавится. Если оставим - то наоборот, но не сможем сбилдить.

Потом @Ivasik говорит что я горожу костыли и показывает как надо. Я следую его совету и правлю settings.gradle:
Код:
include 'case1'
project(":case1").projectDir = file("examples/case1")
Пустой модуль examples убирается и больше нас не беспокоить. Но и сабпроджект перестал в класспач добавляться!

ЧЗХ с этим градлом происходит вообще? Что я не так делаю. Апаю тред.
 
1,159
38
544
Но и сабпроджект перестал в класспач добавляться!
Надо было просто синхронизировать проект. Но все равно периодически отваливается. Дарк мэджик какой-то.

Скопипастил в build.gradle сабпроджекта билдконфиг родителя. Все работает, но какой-же это звиздец! Мне нужно использовать родительскй конфиг юзая apply from: "$rootDir/build.gradle", но игнорируя зависимости родителя. Хочу все сделать как белый человек.
 
1,159
38
544
Мне можно давать награду за самый идиотский способ решения Circular dependency для сабпроджектов. Я просто сделал вот так в скрипте-родителе:
Gradle (Groovy):
dependencies {
    if (getProject().name == rootProject.name) compile project(':case1')
}

Мерзость, но работает. Но меж тем вопрос все еще открыт. Как решить мою задача без таких вот костылей?
 
1,159
38
544
Ребята, пожалуйста помогите. Я уже задрался с этим сидеть.
 
Последнее редактирование:
1,417
44
594
Билдить примеры и запихивать их в mods вручную - дело отстойное
Так это таском можно сделать, как и все что ты пытаешься.
Берем таски билда твоего проджекта и в нем вызываем билд сабпроджекта. Тот же ран - добавляем в таск сбор саба и билдим его в папку с либами, на которую у нас есть dependencies.
 
1,159
38
544
Так это таском можно сделать,
Я уже отошел от идеи запихивать биарники в mods. Хочу из сразу в класпач толкать.

Берем таски билда твоего проджекта и в нем вызываем билд сабпроджекта.
Для этого не обязательно писать новые таски. Достаточно правильно настроить сабпроджект, чего у меня и не выходит

@Tenebrius можешь мне даже не объяснять подход - его я и так понимаю. У меня проблема именно с внутренним пониманием градла и груви. Сижу, понимаш, книженцию читаю, чтобы разобраться, т.к. официальные доки представляют собой скорее коллекцию рецептов и референсов, а не нормальное чтиво для желающих хорошенько разобраться, имхо. Поэтому если есть возможность помочь кодом, то давай уж лучше кодом.
 
1,159
38
544
закидывания бинарника в папку compiled
мне нужно сабпроджект к класспач толкать. Я не хочу перемещать джарники

Снова бампаю.

Вы бы блять знали как у меня жопа подгарает с этого ебучего градла. Мне просто нужно добавить сабпроджекты в класспач, а я сижу над этим уже целый блядский месяц! Даже на форуме градла вопрос задал, но эти пидорасы походу сами блять не знают как юзать свой же продукт. Доки градла - полная хуйня. Там есть сука все, но блять не то что мне надо! Примеры на гитхабе по мультипрождекту делают какие-то косорукие уебища, которые блять не могут объяснить нахуя мне нужно задавать rootProjectName если он уже задан! Как я только на хую не вернел этот градл. В общем, я пошел в запой. Надеюсь за это время кто-то мне поможет
 
Последнее редактирование:
1,159
38
544
699
9
53
Мне можно давать награду за самый идиотский способ решения Circular dependency для сабпроджектов. Я просто сделал вот так в скрипте-родителе:
Gradle (Groovy):
dependencies {
    if (getProject().name == rootProject.name) compile project(':case1')
}

Мерзость, но работает. Но меж тем вопрос все еще открыт. Как решить мою задача без таких вот костылей?
Вот, оно оказалось в этой теме
 
1,159
38
544
1,159
38
544
Молодец!
И так! Я наконец-то смог сделать это! Представляю решение на радостью всему форуму

Прежде всего нужно подключить сабпроджект. Т.к. в моем случае он имеет необычное для Java-проектов расопложение, то выглядеть он будет так:
settings.gradle:
include 'case1'
project(":case1").projectDir = file("examples/case1")
Файл settings.gradle нужно создать в корне основного проекта.

Сабпроджект "case1" юзает классы из рут проекта. Воспользуемся механизмом зависимости чтобы указать это. Создадим build.gradle в корне сабпроджекта с таким содержимым:
build.gradle:
apply from: "$rootDir/build.gradle" // Чтобы не копировать конфиг, мы просто построит конфиг сабпроджекта на основе конфига рут проекта

// Специфические для сабпрождекта данные
version = "0.3.0"
archivesBaseName = "weightapi-case1"

dependencies {
    compile rootProject // Зависимость от главного проекта
}
Нет. вы можете юзать блок subprojects в вашем основном build.gradle:
build.gradle:
subprojects {
    apply from: "$rootDir/build.gradle"
    version = "0.3.0"
    dependencies {
        compile rootProject
    }
}

Но тогда если вам понадобится задавать отдельные данные для каждого сабпроджекта, ваш build.gradle может значительно растянуться:
build.gradle:
project(':case1') { // И так для каждого сабпроджекта
    apply from: "$rootDir/build.gradle"
    version = "0.3.0"
    dependencies {
        compile rootProject
    }
}

Однако при запуске основного мода подпроекты тоже должны загрузиться как моды. Мы с вами не какие-нибудь лохи и знаем, что для этого необязательно билдить jar'ник и закидывать его в mods. Мы знаем что класс Launcher чекает все доступные классы в classpath и ищет среди них аннотацию [USER=7888]@mod[/USER], после чего подтягивает остальные части мода. Поэтому и мы, вместо того чтобы плодить бесполезные артефакты сборки, просто скажем градлу добавить сабпроджект в класспач основного проекта.

Вы хоть и можете сделать это так:
build.gradle:
dependencies { // НЕ НАДО ТАК ДЕЛАТЬ! Это пример!
    compile project(':case1')
}
но тогда вы и глазом махнуть не успеете как заметите во всей вашей посуде дырки и как переехали подшконку. Это все потому что при билде вы будете получать circular dependency!

Нам нужно лишь добавить сабпроджект в класспач, а не зависить от кода сабпроекта. Поэтому делаем так:
build.gradle:
dependencies { // Вот так надо!
    runtimeClasspath project(':case1')
}

Теперь вы имеете мульти-проект с собственной структурой, можете спокойно его билдить и запускать. Аплодисменты мне!

@CMTV Я так долго парился с этой херней. Будь добр, добавь мне какую-нибудь плашку что я молодец.
 
Последнее редактирование:
Сверху