Maven: Running an arbitrary command build phase - maven

How do I run a simple command (or a shell script containing said command) during the maven build phase?
My specific case is I'd like to run the protocol buffer compiler (protoc) that generates a java class prior to the java compiler running.
I feel like it should possibly be part of the "process resources" phase of the build goal (see http://books.sonatype.com/mvnref-book/reference/lifecycle-sect-common-goals.html) but they only discuss copying files that happen to be shell scripts, not running a script.

One solution to your problem is to use the maven-antrun-plugin. That is what I use to build some scriptlike commands within a maven build:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<tasks>
<echo message="basedir=${basedir}" />
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
With this a simple echo task is started in the build phase generate-sources. So you could extent this solution to your needs.
I like this ant aproach more than e.g. the exec-maven-plugin because it is imho in a way more system independent. But thats a thing of preference.

Related

exec-maven-plugin equivalent / alternative

In Maven, we can use exec-maven-plugin to execute bash commands in the build.
Which plugin of Central Repository can perform the same task?
I ask it because I have to execute a bash command after another plugin that needs to be executed in the same phase only after exec-maven-plugin, so I can't do it directly inside the exec-maven-plugin.
The bash command that I want to execute in the Maven build is the following:
cat file1 >> file2
Thanks in advance.
I managed to solve my issue with maven-antrun-plugin with the <concat> task:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>final step</id>
<phase>install</phase>
<configuration>
<target>
<concat destfile="${project.build.directory}/${project.artifactId}-${project.version}.sh" binary="yes">
<fileset file="${project.build.directory}/script/self-installer.sh" />
<fileset file="${project.build.directory}/${project.artifactId}-${project.version}.tar.gz" />
</concat>
<chmod file="${project.build.directory}/${project.artifactId}-${project.version}.sh" perm="+x"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
This is the equivalent of the bash cat command.
Keep in mind that if you are concatenating a binary file, you have to set binary="yes", otherwise the Ant task will corrupt the final file.
In any case, this is still not a bash-based solution, it's only a trick that uses Ant routines, so it's not a real equivalent of exec-maven-plugin

How to run a Maven plugin after mvn gatling:test

I need to run a Maven plugin after Gatling(gatling-maven-plugin) runs to do some cleanup of my target dir.
From the following article it is called out that mvn gatling:test is bound to the integration-test phase of Maven
https://github.com/gatling/gatling/blob/65524556b1aaee7d129cc9c4794977468493ad34/src/sphinx/extensions/maven_plugin.rst#usage
Knowing this and that plugins tied to the same phase run sequentially I added a plugin after gatling-maven-plugin tied to the same integration-test phase.
Unfortunately that plugin never runs. Appreciate any advice on how to get a plugin to run after gatling-maven-plugin runs.
Snippet of what i'm trying to do below. Appreciate any advice. I do something similar and successfully with the maven-surefire-plugin by adding a plugin after that tied to the test phase and it works perfect. Scratching my head on this one.
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<simulationsFolder>src/test/java</simulationsFolder>
<includes>
<include>perf.Test</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>myId</id>
<phase>integration-test</phase>
<goals>
<goal>run</goal>
<configuration>
<target>
<echo message="running something after gatling"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
I find it strange that the plugin never runs.
Anyway, for cleaning up, I would use the post-integration-test phase.
same issue with teardown. Managed to do it within in gatling itself, but doesnt seem as perfect cleanup achieved! also interaction between the gatling and other plugins not possible during post-integration-phase. once gatling tests are started then the entire execution control ends with the same plugin.

How to invoke specific execution

I am trying to replace maven exec with MavenInvokerPlugin because of problems on Jenkins with forwarding the maven settings file.
So in bash it looks straight:
mvn dependency:copy-dependencies#resolve-maven-deps
My translation to MavenInvokerPlugin configuration is
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<projectsDirectory>${project.basedir}/src/main/docker</projectsDirectory>
<localRepositoryPath>${project.build.mavenDependencies}</localRepositoryPath>
<goal>dependency:copy-dependencies#resolve-maven-deps</goal>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>run</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
It looks like that execution id is completely ignored, because I tried random strings and mvn builds the project with success.
mvn dependency:copy-dependencies#asdfasdfa
So I'd like to know whether this feature is supported at all and what I am doing wrong.
P.S. I know that calling maven out of maven is anti pattern, but here is exactly that rare case when there is no other way.
After looking at projects using maven invoker I figured out the trick.
goal tag is not used, instead provide invokerPropertiesFile:
<pom>${project.basedir}/xxx/pom.xml</pom>
<invokerPropertiesFile>${project.basedir}/invoker.properties</invokerPropertiesFile>
content of the file:
invoker.goals=compile -P resolve-maven-deps

Launching a windows batch script using Maven exec plugin blocks the build even though the script uses "start"

I am trying to perform integration tasting of the deployment of my application on the top of a custom container. Since my container is custom, I cannot use Maven Cargo plugin to setup the container.
My container:
Has to be started though a proper bat file, which is in the path of the machine where the tests are run.
Can be manually closed since I have a single maven module containing all my integration tests, even if one day I would like to know how to shut the process down after my tests are completed.
My problem is that I have to run my container in a different process, because it needs to keep running while my tests are performed. Furthermorely, I have an API in my tests that let me wait for the container to be ready (a sort of lookup with timeout).
I have added the following lines to my pom.xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<phase>pre-integration-test</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>scripts/start-agent.bat</executable>
</configuration>
</plugin>
This will call a script, which contains only
start call gs-agent.bat
However the mvn exec plugin gets stucked and my tests are not run. According to what is suggested in How do I run a batch file from my Java Application? , I have modified my pom.xml as the following:
<configuration>
<executable>cmd</executable>
<arguments>
<argument>/C</argument>
<argument>start</argument>
<argument>gs-agent.bat</argument>
</arguments>
</configuration>
But this does not seem to solve the issue:
exec plugin is not able to do this, and I found the issue for it, too: http://jira.codehaus.org/browse/MEXEC-87 (link now dead due to codehaus rampdown, can be found from web archive)
In the jira issue linked above, there is a mention and a link of a fork for exec plugin that would have the functionality.
Other than that, I think you'll need to use an antrun-plugin for the time being.
Here's an example taken from working configuration and run with mvn verify. This needs to be in the <plugins>, not <pluginManagement> (exec could reside in pluginmanagement just fine).
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>pre-integration-test</phase>
<configuration>
<target>
<exec executable="cmd.exe"
spawn="true">
<arg value="/c"/>
<arg value="D:\myfolder\test.bat"/>
</exec>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Note that spawn="true" is key here if you don't want the execution to block, like specified in the question. If you do want it to block and see the output immediately, set it to false.
See this question: How do I run a batch file from my Java Application?
Windows Batch Files are not executable. They are scripts that are run by the cmd executable.
More Information
Exec plugin source code reveals that Apache Commons Executor is used to actually execute the command line.
There is a lot of reading you can do here, i.e. in the Apache Commons Executor documentation and their JIRA issues, but the short version is: this isn't a problem with "Maven," it's a problem with the platform-dependent nature of executing an exec() command.
I've tackled this sort of problem before, and the solution I always devise is to deconstruct the .bat script into its actual commands and launch it directly from the exec plugin, rather than calling the script.
The Fallowing Code works for me
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<tasks>
<exec dir="${project.basedir}" executable="Script.bat" failonerror="true">
</exec>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Source https://maven.apache.org/guides/mini/guide-using-ant.html
In my case I had trouble with npm and ng.. the underlying problem was exec-maven-plugin was trying to execute the sh scripts (which had no extensions).
Renaming
C:\Users\User\AppData\Roaming\npm\ng to C:\Users\User\AppData\Roaming\npm\ng.sh
and
C:\apps\nodejs\npm to C:\apps\nodejs\npm.sh
solved the problem.
If you want to use exec-maven-plugin here is an example to run a batch script with arguments. E.g. to run something like:
.\test.bat arg1 arg2 arg3
add the following to pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>exec-test</id>
<phase>prepare-package</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>cmd</executable>
<workingDirectory>./</workingDirectory>
<arguments>
<argument>/c</argument>
<argument>start</argument>
<argument>""</argument>
<argument>exec.bat</argument>
<argument>arg1</argument>
<argument>arg2</argument>
<argument>arg3</argument>
</arguments>
</configuration>
</plugin>

How can I pass the antrun plugin a new environment variable?

I'm using the maven-antrun-plugin with install4j to build installers for my application. It doesn't work if java is not on the (system) path. Since it's forking a new java process to run the task, there must be a way I can pass it environment variables, but I can't figure it out.
Install4J will use EXE4J_JAVA_HOME to select a java installation. I want to pass that to ant via the antrun-plugin. I can think of a few hackish ways of doing it, but there's got to be something simple I'm overlooking. For reference, here is my antrun config:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<!--suppress MavenModelInspection -->
<taskdef name="install4j" classname="com.install4j.Install4JTask"
classpath="${install4j.ant.path}"/>
<install4j projectfile="itma-assembly-client-swing.install4j" buildids="62">
<variable name="verbose.version.number" value="${verbose-version-number}"/>
<variable name="media.file.prefix" value="${media-file-prefix}"/>
<variable name="main.class" value="${itma-client-swing-bootstrap-main-class}"/>
</install4j>
</target>
</configuration>
</execution>
</executions>
</plugin>
Interesting question ... I did some research on it and found the property-Task for Ant which did sound promising first but it allows only read-access on environment variables. These sources on jguru and coderanch claim that an is the only way to pass environment variables from inside Ant.
It's not the nicest way but perhaps you can exec the com.install4j.Install4JTask from the command-line aka via exec - perhaps that's one of your hackish ways ... :-)

Resources