Всем привет. Более года назад я публиковал на Хабр статью, где рассказывал о проблемах IDEA Maven плагина, о том что с этим можно сделать и представил свою версию решения. Основная проблема которую он решает - это импорт проектов в IDEA. На тот момент это был прототип решения. За это время у меня появилось ~300 активных пользователей, добавлена поддержка MVND и WSL. И спустя год, хотел бы рассказать о изменениях которые произошли в моем плагине более подробно, а ближе к концу статьи о свежих примерах ошибок импорта проектов в IDEA, которые смог решить мой плагин.

Новое имя

Как известно в программировании есть две основные проблемы: это именование и инвалидация кеша. Изначально я назвал свой плагин GMaven (первая буква моего имени + Maven), но как заметили в комментариях к исходной статье, это пересекается с именованием Groovy плагина для Maven. Также на эту тему завели issue. Поэтому было принято решение переименовать в Easy Maven.

Интеграция с Maven

Основная часть плагина, это непосредственно плагин для самого Maven. В первой версии я использовал RMI в качестве транспорта для “общения” с Maven. Такой способ был мне хорошо знаком по работе в JetBrains т.к. использовался в bundled Maven плагине и позволял сэкономить мне время на этапе прототипа, а также получить результат - модель проекта - сразу в виде Java объекта. Но пришлось “руками” создавать свой процесс внутри которого запускать оригинальный Maven. В итоге получил ограничение - невозможность получить результат, если запустить плагин на MVND

Поэтому было принято решение возвращать результат - модель проекта в виде файла. Как это делают таски help:effective-pom или dependency:tree из соответствующих плагинов. По итогу я вынес Maven плагин в отдельный проект и опубликовал Maven Central. Пример запуска можно посмотреть в README. Результат представлен на скрине.

Да, сериализация и десериализация в файл требует дополнительных ресурсов/времени, но даже для больших проектов (> 1000 модулей) она занимает на моем ПК примерно полсекунды. Но преимуществ которые дает, сравнительно больше:

1) Поддержка MVND

Теперь нам ничто не мешает делегировать запуск нашего плагина мавен демону. Что позволяет существенно сократить время синхронизации проекта. Так на примере проекта keycloak более 100 модулей, процесс “синка” (при учете что все зависимости уже есть в локальном репозитории) занимает всего ~4 секунды. Т.к. в этом случаем нам не надо каждый раз по новой стартовать процесс и можем переиспользовать уже поднятый Maven контекст со всеми его внутренними кешами.

А это уже быстрее даже чем у bundled Maven плагина:

Но самое приятное, что мы для этого по сути ничего не сделали, просто запустили наш плагин через MVND. И вся работа по “переиспользованию” контекста лежит на плечах разработчиков “дэмона”, и мы не пишем ее сами, как в bundled плагине. Cоответственно меньше проблем с поддержкой и проще код.

Выбрать MWND в качестве Maven Home можно в настройках, в результате он будет использоваться для запуска любых Maven тасков (импорт в том числе) из плагина:

2) Поддержка WSL

Тут такая же ситуация. Мы просто запускаем Maven таск. И нам совсем не важно где будет запущен этот процесс на Windows или на WSL. Единственное что нам нужно сделать - это “смаппить” пути из WSL в Win при импорте результатов в IDEA, а это очень простая операция.

3) IDE не зависимое решение

Т.к. мы выделили плагин в отдельный проект и сделали интеграцию через файл, то с минимальными усилиями можно добавить поддержку Maven в любую IDE максимально нативным способом - нужно просто выполнить таск плагина и загрузить данные о проектной модели из файла в IDE. Но пока у меня есть готовая реализация только для IJ IDEA.

Поэтому я и выбрал такое название как Easy Maven. Т.к. все что нам надо сделать это просто запустить соответствующий Maven таск - будь то получение информации о проекте, произвести анализ зависимостей или выполнить какой либо этап сборки. И по итогу получаем простоту и максимально нативное стабильное решение.

Maven 4

Maven 4 на момент написания статьи уже находиться в статусе RC(release candidate) и его поддержка в IDE становится все более актуальной. Основная фича - это разделение модели POM на consumer и build. Ранее использовалась одна модель - “<modelVersion>4.0.0</modelVersion>” как для сборки приложения так и для деплоймента в различные репозитории. На POM V4 также были завязаны многие билд системы: Gradle, Sbt, Apache BuildR, Apache Ivy и др. Это тормозило различные доработки по улучшению модели POM, чтобы сделать ее более простой и убрать избыточность, особенно заметную при работе с много-модульными проектами. Поэтому было решено разделить POM на два вида, это не нарушит работу систем, что уже завязаны на формат V4 и позволит Maven двигаться дальше. Это изменение напрашивалось давно и выглядит логичным, что файл сборки проекта и дескриптор деплоймента, это на самом деле не одно и тоже. Ядро Maven будет уметь работать с обоими версиями и при деплое/скачивании зависимости из репозиторий будет использовать POM V4. На Habr даже есть соответствующая статья-интервью об этом, аж за 2018 год, там можно прочитать про это или есть свежая презентация Maven 4 на Youtube.

Слайд взят с презентации - The Current State of Apache Maven 4.
Слайд взят с презентации - The Current State of Apache Maven 4.

Соответственно поддержка Maven 4 в IDE будет связана со многими сложностями, если не получать модель проекта через плагин. Можно убедится в этом на примере bundled Maven плагина, который тратит много ресурсов на поддержку. И выход, каждого обновления по Maven 4 связан с немалыми затратами на новую разработку.

Список issues по Maven 4

Я же на данный момент не сделал для поддержки Maven 4 ровным счетом ничего… ну или почти ничего) Изменения не касаются API для запуска плагинов и на данный момент мой плагин свободно запускается на любой версии Maven начиная с версии 3.3.1 и получает модель проекта.

Readonly mode

У меня часто возникает необходимость клонировать проект с remote репозитория, чтобы в IDE лучше с ним разобраться, т.к. в GitHub, да и других web решениях по работе с кодом, не очень удобно навигироваться по нему и выполнять различные поиски. Но не хочется забивать свой диск, дополнительными зависимостями проекта, это могут быть десятки, сотни и более мегабайт, которые скачаиваются автоматически при импорте проекта. Да, в IDEA можно открыть проект в safe mode, но это не сильно отличается от GitHub, проект будет открыт, как обычное дерево файлов. А хочется чтобы все исходники проекта, кроме внешних зависимостей были распознаны и проиндексированы, чтобы работала вся стандартная навигация, переходы и find usages. Именно эту проблему и решает режим только для чтения, который можно включить в Easy Maven Tool Window перед тем как клонировать новый проект или в настройках. В проекте успешно распознаются и проиндексируются все директории с кодом, но внешние зависимости загружены не будут.

Работа с дочерними модулям в многомодульных проектах

Допустим у нас есть Maven проект, который содержит два модуля common и client и client зависит от common. Если мы попытаемся скомпилировать client проект в bundled плагине, то получим хорошо знакомую многим ошибку:

Хотя логично что среда разработки должна понимать контекст проекта и сама решать подобные проблема. В моем плагине таски для дочерних проектов запускаются с соответствующими Maven ключами - “-pl client -am”, которые решают данную проблему. Но пользователю про это думать не надо, он просто запускает таск. Если вдруг по какой то причине нам надо выполнить таск без учета контекста проекта, как это делает встроенный плагин, то это также можно сделать через контекстное меню “таска”, выбрав - “Run As Single Pom…”

Dependency Analyzer (DA)

Анализ зависимостей был также одним из слабых мест встроенного плагина, именно благодаря этой фиче так выстрелил, наверняка многим известный, плагин Maven Helper. Много позднее, с 2022 года, DA также появился и во встроенном плагине. Я решил тоже реализовать его у себя, чтобы не приходилось ставить сторонние плагины, и можно сразу полноценно работать с Maven, анализируя при необходимости зависимости проекта. Этот функционал также работает через запуск таска, и по сути это и есть dependency:tree. Я убрал в своем плагине отображение дерева зависимостей в Tool Window т.к. в таком виде, с ними трудно работать. И из за большого их числа, особенно в многомодульных проектах, они сильно замедляют рендеринг тул-окна. Вместо этого в дереве проекта, есть только один узел “dependency” у каждого модуля, по двойному клику на котором открывается dependency analyzer:

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

Инкрементальный апдейт проекта

Одной из главных фич встроенного плагина, является то, что он позволяет обновлять проект частично, если у нас изменился только один билд файл. Это помогает существенно сократить время импорта проекта в случае его изменений. Но как обратная сторона - требуется сложная реализация анализа изменений и их частичная перезагрузка. В своем плагине я тоже попытался реализовать подобное. Для этого я запускаю Maven task для синхронизации проекта с ключами “-pl <changed-module-id> -am -amd”. Ключ “am” позволяет обновить модули, от которых зависит измененный модуль - т.е. смотрим “вниз” по дереву проекта, от “парента” к “чилдам”. А “amd” позволяет обновить модули которые зависят от измененного, т.е. идем “вверх” по дереву проекта.

Но справедливости ради скажу, что данная функция на стороне Maven, вынуждена все равно прочитать сначала все билд файлы проекта, чтобы построить граф проектов, на основании которого и будут вычислены все зависимые модули. Поэтому она не так эффективна как во встроенном плагине. И по дефолту она отключена, т.к. реально почувствовать пользу, при данной реализации, можно только на очень больший проектах (~1000 модулей), а стабильностью работы на начальных этапах жертвовать не хочется, т.к. у меня практически нет по ней никакой обратной связи. Данный функционал, это сильная сторона встроенного плагина, но обратная сторона это сложность реализации.

Зачем это все нужно

Как я говорил в первой статье, основная мотивация заключалась в том, чтобы убрать разницу между выполнением Maven “тасок” через командную строку и импортом в IDE. Это одна из самых частых проблем, которыми мне приходилось заниматься в JetBrains - когда проект успешно собирается через командную строку, но не импортируется в IDEA. На данный момент, я выделил некоторые самые частые проблемы, которые решает мой плагин:

Eclipse based проекты

Как следует из заголовка это особенно актуально для eclipse based проектов, например DBeaver, Eclipese-Aspetj, которые используют Eclipse Tycho. Данные проекты не импортируются в IDEA корректно встроенным плагином. Но Easy Maven решает эту проблему.  Также у себя на GitHub, я сделал небольшой пример такого проекта, чтобы можно было быстро увидеть разницу.

Дополнительные плагины для резолва зависимостей

Также проблемы возникают в проектах, которые используют дополнительные плагины для резолва зависимостей. В bundled плагине на это никак нельзя исправить, без серьезных доработок, т.к. там процесс резолва зависимостей жестко задан. Один из таких случаев, показал один из пользователей, в комментариях к предыдущей статье. Т.к. в моем плагине импорт проекта, это обычный Maven task, то мы можем в дополнительных аргументах, командной строки указать доп-параметры/таски/фазы которые нужно выполнить при импорте. В данном случае пользователь уже знал, какой плагин отвечает у них в проекте за резолв версий библиотек в случае конфликтов и прописал его в настройках. И проект успешно импортировался. В самом простом случае, если вы не знаете какой таск отвечает за дополнительный резолв зависимостей в вашем проекте, можно просто указать фазу - compile, как правило это решает большинство проблем - параметр Import args в настройках.

Android Studio

Необъяснимо, но факт… Половина скачиваний моего плагина, это пользователи Android Studio.

У меня нет никакой обратной связи по этому поводу, но мои мысли следующие: скорее всего это проекты, которые были созданы еще до того как Kotlin с Gradle “ворвались” в Android разработку. И связаны они по моему мнению с использованием Scala и как следствие соответствующих плагинов в скриптах сборки. По моим наблюдениям Scala была достаточно популярна в мобильной разработке, до появления Kotlin, предоставляя более современный синтаксис чем Java 1.6 и возможность писать в функциональном стиле. Но это лишь только мои мысли, возможно это актуально также и для обычных Maven/Java Android проектов. Если кто то знает больше, то готов с радостью выслушать.

Новые “фичи” Maven

Как я показал в разделе про Maven 4, появление новых фич, это почти всегда больно для bundle плагина т.к. часто требует его доработок и он вынужден постоянно “догонять” Maven по функционалу и привел список свежих issue. Рассмотрим тут для примера один такой случай, который приключился совсем недавно с самим MVND. При попытке мигрировать его на новую версию POM V4.1 проект перестал импортироваться в IDEA. Баг вроде бы починили и ошибок при импорте нет, но есть один нюанс…

Суть проблемы:

В POM V4.1 добавили новый тэг <subproject>, который аналогичен <modules>, но основное отличие в том что модуль может быть самостоятельным проектом и может быть собран независимо от “рутового” модуля или как часть всего “реактора”, если сборка идет из корневого проекта, подробнее про это тут. Тег <modules> сделали необязательным, так что для билд файлов с типом - pom, если данный тег отсутствует, Maven будет проверять автоматически дочерние директории первого уровня на наличие файлов - pom.xml. Пример:

Родительский pom файл не содержит тегов <modules>, но как видно модули присутствуют в “реакторе” - на примере compile task и также проект успешно был импортирован Easy Maven. Но если попытаться импортировать проект встроенным Maven плагином, то получим вот что:

Импорт проекта успешно завершен, но модули не распознаны - только рутовый. Происходит это из за того, что там внутри свой “мини-мавен-дэмон”, состоящий из очень низкоуровневых Maven компонентов. Я писал про это в первой статье. Отсюда и возникают все проблемы, что нужно дорабатывать парсер для новой модели, а это совсем не простая задача. С тех пор уже вышло два мажорных релиза, а полноценной поддержки Maven 4 еще нет.

Заключение

Буду рад, если найдете время и попробуете мой плагин и дадите какую либо обратную связь. Может быть у вас также есть проблемы с импортом проекта в IDEA? Тогда не стесняйтесь писать об это в комментариях или в лс. Постараюсь помочь решить ваши проблемы. Проект можно скачать на marketplace или с GitHub.

Комментарии (0)