- PVSM.RU - https://www.pvsm.ru -
Сначала небольшая предыстория. В начале 2010-х, я сделал небольшую утилиту-конвертер [1] для BIN файлов эмулятора БК-0010 [2] в WAV файлы. Утилита была написана на Python с целью максимальной переносимости, работала без проблем и я на какое то время забыл о ней. Но в 2016м появился пользователь "неИТшник", понятия не имеющий про Python и как его устанавливать. Он хотел простой исполняемый файл-монолит, который "просто бы работал". Мне его просьба показалась логичной и я решил переработать утилиту в виде набора бинарных исполняемых файлов для основных платформ.
Python и Java не давали такую возможность (если конечно не было желания раздуть утилиту на много десятков мегабайт). Потенциально решение можно было сделать на C/C++, но при таком целевом охвате платформ, сложности с кросс-компиляцией выходили бы за рамки отведенного на задачу времени (а мне надо было поддерживать кросс-сборку для Windows, Linux и MacOS в 64 и 32 битных вариантах). Так что я обратил внимание на набирающий популярность язык Go, который к тому времени уже стал достаточно зрелым и единственным, кто без "плясок с бубном" обеспечивает всю требуемую кросс-компиляцию прямо из коробки (!).
Одно неудобство, которое меня сильно раздражало в Go (особенно как Java разработчика) — слабая продуманность организации структуры Go проектов и потребность в постоянной настройке параметров среды. Может быть разработчики Go изначально планировали какое-то специфическое использование или была ориентация на контейнеры, но я привык к "вкусненькому" и так как моим основным инструментом в Java является Maven [3], то решил сделать плагин осуществляющий вызовы к утилитам и командам GoSDK с автоматическим формированием нужных переменных окружения.
Делать просто плагин, который вызывал бы go утилиту, было неинтересно и я решил пойти с шага — установки GoSDK на хост-платформу. Сразу после старта, проверяется наличие GoSDK в своей конфигурационной директории (я выбрал путь по умолчанию ~/.mvnGoLang
) и при отсутствии требуемой версии, производится автоматическая загрузка и распаковка архива GoSDK с официального сайта [4]. Это позволило делать переносимые Go проекты, не заботясь о том, предустановлен и сконфигурирован ли инструмент нужной версии. Конечно, многочисленными настройками я предусмотрел возможность использования предустановленной версии и добавил настройки для всех шагов в процессе, так как для многих критичны такие вопросы как например отключение проверки SSL сертификатов или вообще осуществление HTTP запросов вовне (как например для проекта Keycloak [5]).
Следующим этапом, я обернул в Maven-задачи (goals) все предоставляемые на тот момент команды утилиты go. Так как была вероятность, что с новой версией GoSDK появится еще какая-то новая команда, то была добавлена задача custom
позволяющая пользователю определять нужную команду самостоятельно. По умолчанию, в рамках maven-фаз (phases), задачи исполняются в следующем порядке:
default-clean
в фазe cleandefault-fix
в фазе validatedefault-get
в фазе initializedefault-generate
в фазе generate-sourcesdefault-fmt
в фазе process-sourcesdefault-test
в фазе testdefault-build
в фазе packagedefault-install
в фазе install выполняется внутренняя задача mvninstalldefault-deploy
в фазе deploy Любой из базовых шагов может быть отключен переводом задачи в несуществующую фазу:
<execution>
<id>default-fix</id>
<phase>none</phase>
</execution>
Во внутренней реализации, задачи были разделены на "работающие с зависимостями" и "не требующие разрешения зависимостей", после появления поддержки режима модулей, большинство перекочевало в "работающие с зависимостями".
Структуру Go-maven проекта, я постарался приблизить к стандартной принятой для Go структуре папок (т.е. /src
и /bin
в корне проекта). Но так как Maven заточен под Java, то напрямую не удалось "сломать" его подход к организации структуры проекта и сделать этот шаг невидимым для пользователя, поэтому базовая конфигурация плагина смотрится немного непривычно даже многим знакомым с maven:
<build>
```${basedir}/src</sourceDirectory>
<directory>${basedir}/bin</directory>
<plugins>
<plugin>
<groupId>com.igormaznitsa</groupId>
<artifactId>mvn-golang-wrapper</artifactId>
<version>2.3.3</version>
<extensions>true</extensions>
<configuration>
<goVersion>1.12.9</goVersion>
</configuration>
</plugin>
</plugins>
</build>
WARNING! По каким то причинам хабр может неправильно отображать при форматировании XML участок
<sourceDirectory>${basedir}/src</sourceDirectory>
Как видите, приходится напрямую определять папку с исходными текстами /src
и папку с результатом /bin
. Это с одной стороны минус, с другой стороны возможность к изменению их локации.
Целиком минималистичный pom.xml для одномодульного проекта а-ля Hello world, выглядит следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.igormaznitsa</groupId>
<artifactId>mvn-golang-helloworld</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>mvn-golang</packaging>
<build>
```${basedir}/src</sourceDirectory>
<directory>${basedir}/bin</directory>
<plugins>
<plugin>
<groupId>com.igormaznitsa</groupId>
<artifactId>mvn-golang-wrapper</artifactId>
<version>2.3.3</version>
<extensions>true</extensions>
<configuration>
<goVersion>1.12.9</goVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>
Обратите внимание, что packaging проекта обозначен как mvn-golang. Этой конфигурации достаточно, что бы на базе исходных текстов в папке src, построить исполняемый файл и положить его в результирующую папку bin. Go build cache будет так же создан в папке /bin
(как /bin/.goBuildCache
по умолчанию) и при clean будет стираться вместе с этой временной папкой.
В фазе install вызывается внутренняя задача mvninstall
, которая просто пакует весь проект в zip архив и размещает его в maven-репозитории как сгенерированный артефакт. Изначально я просто складировал эти артефакты, но с версии 2.3.2 был добавлен механизм для поддержки их как стандартных maven-зависимостей и появилась возможность разработки проектов с "расшариванием" общего кода через maven репозиторий. Понятно, что класть в репозиторий сгенерированные бинарные результаты как артефакты — плохая идея из-за требований по кросс-платформенности и поэтому содержимое папки /bin
в артефакт не пакуется.
Подключение другого проекта с packaging mvn-golang
в качестве maven-зависимости (dependency) выглядит примерно так:
<dependency>
<groupId>com.igormaznitsa</groupId>
<artifactId>mvn-go-test-mix-terminal</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>mvn-golang</type>
</dependency>
С 2.3.3 версии была добавлена поддержка работы с механизмом Go модулей [6], но по умолчанию она не активирована (для обратной совместимости) и включается при помощи конфигурационного параметра:
<moduleMode>true</moduleMode>
Когда механизм Go-модулей был еще на стадии экспериментов, я добавил поддержку работы с версиями зависимостей через прямые вызовы CVS утилит, был сделан тестовый пример [7]. Но сейчас думаю, что такой механизм уже не представляет большого интереса и выгоднее пользоваться стандартными зависимостями через модули. Тем более, что плагин умеет препроцессировать go.mod
файлы на время построения проекта, подменяя пути к локальным папкам, если идет работа в рамках многомодульного проекта.
Так как Maven предусматривает возможность шаблонизации при помощи архетипов, то я сделал два архетипа:
Пример работы с архетипом одномодульного проекта, можно увидеть на анимации ниже
.
Так же можно просто клонировать "Hello World" проект [8] и поиграться:
git clone https://github.com/raydac/mvn-golang-example.git
Нередко возникает резонный вопрос — а для чего всё это надо и какие бонусы дает использование Maven в процессе построения Go-проекта? Попробую ответить на него по пунктам:
Крупный недостаток решения, на мой взгляд, тут один — ограниченное количество разработчиков знакомых с Maven среди Golang-сообщества. Для перешедших на Golang с C/C++ понятно, что ближе и роднее make сложно что-то найти, так же никто не отменял "нативные" Go-билд системы. Я заметил, что по каким то своим причинам, многие разработчики не любят смешивать платформы.
Итак, я кратко показал один из путей использования Maven при разработке Go проектов с помощью mvn-golang-wrapper плагина. Проект плагина оформлен как OSS-проект и выложен на GitHub [15]. Если кто то заинтересуется и будет использовать в своих проектах, то не стесняйтесь задавать вопросы и "репортить баги". Я постарался сделать набор примеров на разные случаи жизни [16] (на которых плагин и тестирую), но всего не охватить.
Тестовые примеры [16], идущие в проекте плагина, используют dev-версию, так что если будет желание к их локальному построению, после клонирования проекта, то для этого требуется сначала произвести билд dev-версии плагина, при помощи команды в корневом каталоге проекта:
mvn install
после чего, можно заходить в любой подпроект mvn-golang-examples
и строить его при помощи
mvn clean install
так же можно запустить построение всех примеров из корня проекта, при помощи
mvn clean install -Pexamples
Плагин поддерживает многопоточную сборку проектов и её можно ускорить при помощи соответствующего аргумента, разбив например на 6 потоков
mvn clean install -Pexamples -T6
За время разработки, проект накопил приличное количество "наворотов", которые я решил не освещать в этой небольшой статье. Информацию о параметрах с небольшими примерами конфигурации можно найти в mind map данного плагина (исходный файл в формате SciaReto [17] находится здесь [18]):
Автор: raydac
Источник [19]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/maven/328999
Ссылки в тексте:
[1] небольшую утилиту-конвертер: https://github.com/raydac/bkbin2wav
[2] БК-0010: https://ru.wikipedia.org/wiki/%D0%91%D0%9A_(%D1%81%D0%B5%D0%BC%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D0%BE_%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D0%BE%D0%B2)
[3] Maven: https://maven.apache.org/
[4] официального сайта: https://storage.googleapis.com/golang/
[5] Keycloak: https://issues.jboss.org/browse/KEYCLOAK-7362?_sscc=t
[6] Go модулей: https://blog.golang.org/using-go-modules
[7] сделан тестовый пример: https://github.com/raydac/mvn-golang/tree/master/mvn-golang-examples/test-git-cvs
[8] "Hello World" проект: https://github.com/raydac/mvn-golang-example
[9] при помощи Go2XUnit: https://github.com/raydac/mvn-golang/tree/master/mvn-golang-examples/mvn-golang-example-testing
[10] скрещивая Go и GWT: https://github.com/raydac/go-gwt-example
[11] проект ANTLR: https://github.com/raydac/mvn-golang/tree/master/mvn-golang-examples/mvn-golang-example-antlr
[12] генерируя Go на базе Protobuf дескрипторов: https://github.com/raydac/mvn-golang/tree/master/mvn-golang-examples/mvn-golang-example-protobuf
[13] добавляя препроцессинг: https://github.com/raydac/mvn-golang/tree/master/mvn-golang-examples/mvn-golang-example-preprocessing
[14] NetBeans IDE: https://netbeans.org/
[15] Проект плагина оформлен как OSS-проект и выложен на GitHub: https://github.com/raydac/mvn-golang
[16] набор примеров на разные случаи жизни: https://github.com/raydac/mvn-golang/tree/master/mvn-golang-examples
[17] SciaReto: http://sciareto.org
[18] здесь: https://github.com/raydac/mvn-golang/blob/master/.projectKnowledge/doc_common.mmd
[19] Источник: https://habr.com/ru/post/465803/?utm_campaign=465803&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.