JMIX 1.5. Задание css стилей в "функциональном" add-on'е

Добрый день.

Ситуация следующая.
Разрабатываю некоторый “функциональный” аддон myaddon (не аддон с темой), в котором у меня есть какие-то сущности и экраны. И, допустим, я хочу к экранах ТОЛЬКО ЭТОГО аддона некоторую кнопку сделать розового цвета. Описывая её в дескрипторе, я задаю стиль <button stlename="my-pink-button"/>. Далее, подключая этот аддон к основному проекту я хочу чтобы в экранах эта кнопка была розовой. Но при этом я не хочу, чтобы стиль my-pink-button мне приходилось описывать в расширении темы основного проекта, а чтобы стиль был описан в аддоне и “приезжал” вместе с этим аддоном.
Что я пытался сделать и ничто не заработало:

1. Расширял тему в аддоне и прописывал там стиль .my-pink-button{...}.

2. По аналогии с аддоном-темой я в resources (функционального модуля, не стартере) создал структуру каталогом VAADIN/addons/myaddon/myaddon.scss в котором добавил .my-pink-button{...}. Далее в myaddon.gradle добавил

jar.manifest {
    attributes(['Vaadin-Stylesheets': 'VAADIN/addons/myaddon/myaddon.scss'])
}

В основном проекте я расширяю тему и туда делаю импорт addons, как в документации.
Но если посмотреть в demo/build/tmp/themes/VAADIN/themes/hover-ext/addons.scss там нет импорта scss аддона, ну и по итогу в скомпилированную тему он не попадает.

Ни в одном из этих случаем стиль .my-pink-button в скомпилированном styles.css не появился и, соответственно, кнопка розовой не стала.

Во втором случае не понятно почему это не срабатывает, вроде соблюдаю требования описанные тут в первой разделе. Может из-за особенностей подключения аддона через стартер.

Что с этим можно сделать? Очень бы не хотелось подключая аддон ещё и стили сидеть копировать.

Добрый день, а в проекте к которому подключаете аддон, тема расширена? Если нет, то в build.gradle у вас подключается уже скомпилированная тема, а значит, что исходники из аддона игнорируются.

Глеб

Да, расширяю. я дополнил вопрос, наверно уже когда Вы отвечали.

Это по второму варианту.
У меня в основной проект ещё и аддон с темой подключается. он работает, все стили описанные в аддоне-теме попадают в основной проект.

Привет
Я подозреваю, что у тебя стартер грузится через implementation(project(":your-addon")) или api(project(":your-addon")).

Попробуй делать все то же самое, но через localMaven и непосредтвенное указание координат артефакта твоего аддона: implementation("org.company:my.addon:0.0.2-RC")

Но вообще, как сказал мой коллега, хотелось бы увидеть полностью build.gradle в вашем проекте, который в основном проекте и еще 1 файл - myaddon.gradle. Просто посмотреть что происходит у вас в сборке

Да, конечно, я делал api(project(":your-addon")) ведь по умолчанию так.
Далее я со скриншота, потому что текстом будет неудобно. Получилось выяснить следующее.

У меня композитный проект в котором есть (в числе прочих) fullstack приложение demo, аддон с темой jmix-hover-kolnpp и “тот самый” аддон asup-reference.
asup-reference-1
Если я в .gradle стартера пишу implementation и исключаю аддон из композитного проекта, то стартер выгрущаеться из localMaven, то он не тянет за собой “функциональщину” и вообще ничего не работает.

asup-reference-2
Если в .gradle стартера оставляю implementation и аддон оставляю в композитном проекте, то в принципе имеем тоже самое.

asup-reference-3
Если я исключаю аддон из композитного проекта, но оставляю в стартере api project(":asup-reference"), то всё работает.
То есть получается проблема не в api project, а в механиках композитного проекта?

Вероятно, если отказаться от композитного проекта - будет хорошо) А вот как бы чтобы и композитный оставить? Есть предложения?

Как-будто проблема тут комплексная. Ведь аддон с темой в композитном проекте подключается и работает, а аддон “функциональный” из-за того что подключается через стартер не хочет работать.

Проблема не в композиции проекта, а в той разнице, как работает project(":") и полные координаты. Я точно сказать не могу в чем проблема, но над решением я тоже много времени просидел год назад где-то.

Суть в том, что sass нормально билдится только в случае если мы указывает зависимость через координаты.

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

ну вот на втором скриншоте видно, что я везде указал полные координаты, но в панели gradle видно, что он всё равно тянется как project да ещё и как-то криво.
может есть способ “заставить” gradle пойти в maven?

По поводу папки build да, в курсе. на последнем скриншоте как раз показал файл addons.scss где видно что миксин аддона появился.

  1. В стартере желательно указывать api на основной аддон
  2. Если вы не используете project(":") в стартер проекте, то дополнительно надо запушить в локальный макен репозиторий еще основной аддон
  3. Можете вместо скриншотов использовать код секции при показе файлов(градл конфигов), а то не совсем ясен контекст
    UPD: Вот второй вариант выглядит как рабочий, только где то у вас чего то не хватает

Что имеем спустя часы мучений)

Вариант 1. Стили не приезжают.

#settings.gradle(jmix-dev-demo-all)
includeBuild '../asup-reference-addon'

#build.gradle(demo)
dependencies {
    implementation 'ru.consyst.kolnpp.addon:asup-reference-starter:0.0.12'
}

#asup-reference-starter.gradle
dependencies {
    api "ru.consyst.kolnpp.addon:asup-reference:0.0.12"
}

Вариант 2. Стили не приезжают.

#settings.gradle(jmix-dev-demo-all)
includeBuild '../asup-reference-addon'

#build.gradle(demo)
dependencies {
    implementation 'ru.consyst.kolnpp.addon:asup-reference-starter:0.0.12'
}

#asup-reference-starter.gradle
dependencies {
    api project(':asup-reference')
}

Вариант 3. Стили asup-reference приехали в demo.

#settings.gradle(jmix-dev-demo-all)
#//includeBuild '../asup-reference-addon' <- убираю asup-reference-addon из композитного проекта

#build.gradle(demo)
dependencies {
    implementation 'ru.consyst.kolnpp.addon:asup-reference-starter:0.0.12'
}

#asup-reference-starter.gradle
dependencies {
    api project(':asup-reference')
    #api "ru.consyst.kolnpp.addon:asup-reference:0.0.12"        <- и так работает
    #api "ru.consyst.kolnpp.addon:asup-reference:${version}"  <- и так тоже
}

Отсюда у меня напрашивается вывод, что всё таки не project(":") “виноват”, а какие-то механики композитной сборки. Не буду утверждать, но впечатление такое. В композитной сборке я не силён, перечитал документацию gradle по этой части, но не увидел как там эти модули обмениваются артефактами.

Каким-либо образом и оставить asup-reference-addon в композитном проекте, и заставить появиться стили я не смог. К сожалению.

2 симпатии

Извиняюсь за поздний ответ,
Да там какие-то интересные вещи происходят при сборке, хоть саму сборку дебажь)

Спасибо что оставили решение!
Для решения в будущем другим людям мб поможет. Вот я нигде не задокументировал, а вспомнить уже не судьба где именно проблема была :sweat_smile:

1 симпатия

Разобрался в этом вопросе полностью, почему так происходит в композитном проекте и всё заставить работать.

Первое на что обратил внимание - это почему проблемы не возникает с аддоном темы? Это объясняется тем, что у аддона с темой и “функционального” аддона разные шаблоны, которые прописывают в build.gradle разные плагины. У аддона с темой указан плагин java, а у “функционального” java-library - и по факту именно в этом проблема.

Если почитать описание в документации gradle на The Java Library Plugin, то можно найти такое:

A feature of the java-library plugin is that projects which consume the library only require the classes folder for compilation, instead of the full JAR.

То есть, если мы в аддоне используем плагин java-library, то потребители этого аддона, в нашем случае проект demo, просто использует скомпилированные классы этого аддона (из папки с классами), вместо того, чтобы “тащить” JAR-архив.

А путь то к стилям прописыватся именно в процессе сборки jar, в манифесте:

jar.manifest {
    attributes(['Vaadin-Stylesheets': 'VAADIN/addons/asup-reference/asup-reference.scss'])
}

Подебажить это можно взяв исходники jmix плагина для gradle. Там можно найти класс ThemeCompile и в нём (среди прочих) пару методов (collectAddonIncludes - который собирает стили из аддонов, и walkDependencies - который рекурсивно шарится по зависимостям) и в walkDependencies увидим такую проверку:

if (artifact.getFile().getName().endsWith(".jar")) {
    artifactAction.accept(artifact); // Consumer который извлекёт стили
}

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

То есть, если артифакт не являеться JAR’ом, то ничего не происходит, и стили не извлекаются. А так как аддон использует плагин java-library, то (по умолчанию) в композитном проекте потребители в качестве артефактов берут скомпилированные классы, а не jar.

Поэтому когда аддон исключается из композитного проекта и загружается из репозитория, то тут уж, конечно, артифактом являеться JAR-файл и всё работает.

Чтобы в композитном проекте заставить аддон работать, есть два решения:

  1. Использовать в аддоне плагин java вместо java-library. Но тут, из минусов, например, то что не получиться использовать конфигурацию api(), потому что она есть только в java-library.

  2. “Заставить” потребителя тащить JAR вместо классов. Подглядел тут, но там с ошибкой, решение ниже исправленное. Для этого в build.gradle проекта можно указать, что для всех зависимостей, которые включены к compileClasspath в качестве артифакта нужно брать JAR:

configurations.compileClasspath {
    attributes {
        attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR))
    }
}

Думаю можно сделать и более тонкую настройку - указать для каких аддонов брать jar, а для каких классы.

Тут тоже есть минусы. Из тех что увидел - это ошибка о том что нет jar-файла аддона. Она появляеться если очистить билд (gradle clean), что логично, ведь мы удалили jar. При последующих сборках ошибка не появляется, но не знаю, будут ли ещё какие-то побочные эффекты.

Так же у меня был вопрос про транзитивные зависимости на аддоны и стили из них.
Допустим, в проекте demo используется addon2, который в свою очередь использует addon1 с UI компонентами и стилями для них. Хотелось бы, добавив в demo зависимость только на addon2 получить и стили addon1, чтобы UI компоненты выглядили корректно.

В документации gradle написано следующее:

Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers. Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers’ compile classpath.

То есть, если в addon2 добавить зависимость на addon1 в конфигурацию implementation, то ничего не получиться, нужно использовать api.

dependencies {
    api 'my.company:addon1-starter:0.0.1'
}

В demo зависимость на addon2 можно добавить уже в implementation.

Таким образом addon1 попадёт в compileClasspath demo проекта, где задача compileThemes его сможет найти и добавить стили из addon1.

Получается, что в addon1 нужно использовать плагин java-library для gradle, а demo настроить так, чтобы он тащил jar-файлы для своих зависимостей (как я описал вше в решение 2).

Однако тут появляется другая проблема. Так как мы “заставили” demo тащить jar-файлы в качестве артефакта аддона, то при изменении стилей в аддоне теперь, перед вызовом compileThemes, нужно собрать новые jar’ы.

Эту задачу можно упростить, добавив в build.gradle композитного проекта новый task для компиляции темы (и запускать его):

task compileDemoThemes {
    dependsOn gradle.includedBuild('addon1-addon').task(':addon1:jar')
    dependsOn gradle.includedBuild('addon2-addon').task(':addon2:jar')
    dependsOn gradle.includedBuild('demo').task(':compileThemes')
}

Вероятно, описанные выше решения не оптимальны, и у них точно есть минусы, которые, в принципе, описаны в документации на gradle, но надеюсь кому-то ещё это пригодиться. Мне пригодилось, выходные убиты не в пустую).

2 симпатии