First, a little background. In the early 2010s, I made a small converter utility for BIN files of the BK-0010 emulator to WAV files. The utility was written in Python with the goal of maximum portability, it worked without problems and I forgot about it for a while. But in 2016, the user appeared, who had no idea about Python and how to install it. He wanted a simple executable monolith that would "just work." His request seemed logical to me, and I decided to rework the utility as a set of binary executable files for the main platforms.
Python and Java did not give such an opportunity (unless of course there was a desire to inflate the utility by many tens of megabytes). Potentially, the solution could be made in C / C ++, but with such a targeted coverage of the platforms, difficulties with cross-compilation would go beyond the time allotted for the task (and I had to support cross-build for Windows, Linux and MacOS in 64 and 32 bit options). So I drew attention to the language Go, which is gaining popularity, which by that time had already become quite mature and the only one who, without "dancing with a tambourine," provides all the required cross-compilation right out of the box (!) .
One inconvenience that annoyed me a lot about Go (especially as a Java developer) was the poor thoughtfulness of organizing the structure of Go projects and the need to constantly adjust the environment. Maybe the Go developers initially planned for some specific use or there was a focus on containers, but I got used to the “tasty” one and since my main tool in Java is Maven , I decided to make a plug-in that makes calls to utilities and GoSDK commands with the automatic generation of the necessary ones environment variables.
It was not interesting to make a simple plugin that would call the go utility, and I decided to go from the step - installing GoSDK on the host platform. Immediately after the start, the presence of GoSDK is checked in its configuration directory (I chose the default path ~/.mvnGoLang
) and in the absence of the required version, the GoSDK archive is automatically downloaded and unpacked from the official site . This allowed us to make portable Go projects without worrying about whether the tool of the required version is preinstalled and configured. Of course, with many settings, I foresaw the possibility of using a pre-installed version and added settings for all the steps in the process, since for many issues such as disabling SSL certificate verification or generally making HTTP requests outside (such as for the Keycloak project) are critical .
The next step, I wrapped in the Maven-goals (goals) all the go utility commands provided at that time. Since there was a chance that another new command would appear with the new version of GoSDK, a custom
task was added allowing the user to determine the desired command on their own. By default, within maven phases, tasks are executed in the following order:
default-clean
default-fix
default-get
default-generate
default-fmt
default-test
default-build
default-install
default-deploy
Any of the basic steps can be disabled by translating the task into a nonexistent phase:
<execution> <id>default-fix</id> <phase>none</phase> </execution>
In the internal implementation, the tasks were divided into “working with dependencies” and “not requiring dependency resolution”, after the appearance of support for the module mode, most migrated to “working with dependencies”.
The structure of the Go-maven project, I tried to bring closer to the standard folder structure accepted for Go (i.e. /src
and /bin
in the root of the project). But since Maven is imprisoned for Java, it was not directly possible to “break” its approach to organizing the project structure and make this step invisible to the user, so the basic configuration of the plugin looks a little unusual even to many familiar with 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! For some reason, the HabR may incorrectly display the<sourceDirectory>${basedir}/src</sourceDirectory>
section when formatting XML
As you can see, you have to directly determine the source folder /src
and the folder with the result /bin
. On the one hand, this is a minus, on the other hand, the possibility of changing their location.
The whole minimalistic pom.xml for a single-module project, a la Hello world, is as follows:
<?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>
Please note that the packaging of the project is designated as mvn-golang . This configuration is enough to build the executable file and put it in the resulting bin folder on the basis of the source texts in the src folder. Go build cache will also be created in the /bin
(as /bin/.goBuildCache
by default) and will be erased with clean with this temporary folder.
In the install phase, the internal mvninstall
task is mvninstall
, which simply packs the entire project in a zip archive and places it in the maven repository as a generated artifact. Initially, I just stored these artifacts, but from version 2.3.2 a mechanism was added to support them as standard maven dependencies and it became possible to develop projects with "sharing" common code through the maven repository. It is clear that putting the generated binary results into the repository as artifacts is a bad idea due to cross-platform requirements and therefore the contents of the /bin
folder are not packaged in the artifact.
Connecting another project with packaging mvn-golang
as a maven dependency looks something like this:
<dependency> <groupId>com.igormaznitsa</groupId> <artifactId>mvn-go-test-mix-terminal</artifactId> <version>1.0.0-SNAPSHOT</version> <type>mvn-golang</type> </dependency>
With version 2.3.3, support was added for working with the Go module mechanism, but by default it is not activated (for backward compatibility) and is enabled using the configuration parameter:
<moduleMode>true</moduleMode>
When the mechanism of Go-modules was still at the experimental stage, I added support for working with dependency versions through direct calls to CVS utilities, and a test example was made . But now I think that such a mechanism is no longer of great interest and it is more profitable to use standard dependencies through modules. Moreover, the plugin is able to preprocess go.mod
files at the time of building the project, replacing the paths to local folders if work is ongoing within the framework of a multi-module project.
Since Maven provides the ability to template using archetypes, I made two archetypes:
An example of working with the archetype of a single-module project can be seen in the animation below
.
You can also just clone the "Hello World" project and play around:
git clone https://github.com/raydac/mvn-golang-example.git
Often a reasonable question arises - why is all this necessary and what bonuses does Maven use in the process of building a Go-project? I will try to answer it point by point:
A major drawback of the solution, in my opinion, is one here - a limited number of developers familiar with Maven among the Golang community. For those who switched to Golang with C / C ++, it is clear that it is difficult to find something closer and dearer to make, just as no one has canceled the “native” Go-build systems. I noticed that for some reason, many developers do not like to mix platforms.
So, I briefly showed one of the ways to use Maven when developing Go projects using the mvn-golang-wrapper plugin. The plugin project is designed as an OSS project and posted on GitHub . If someone is interested and will use in their projects, then do not hesitate to ask questions and "report bugs." I tried to make a set of examples for different occasions (on which the plugin is being tested), but I can’t cover everything.
Test cases in the plugin project use the dev version, so if you want to build them locally after cloning the project, you first need to build the dev version of the plugin using the command in the project root directory:
mvn install
after which, you can go into any mvn-golang-examples
subproject and build it with
mvn clean install
you can also start building all the examples from the root of the project, using
mvn clean install -Pexamples
The plugin supports multi-threaded assembly of projects and it can be accelerated using the corresponding argument, breaking for example into 6 threads
mvn clean install -Pexamples -T6
During development, the project has accumulated a decent amount of "bells and whistles", which I decided not to cover in this short article. Information about the parameters with small configuration examples can be found in the mind map of this plugin (the source file in the SciaReto format is here ):