Making Maven Grunt - Windows Edition

— 5 minute read

Recently, I needed to make a single-page web app that would be served by a Java backend. That would be as simple as copying the static files into the relevant folder, right? Well, as it turns out, not quite, as each project used a different build system.

Single-page web apps are typically built using the Grunt and Bower toolchain, which are NodeJs scripts. Java backends are typically built using either the Ant or Maven toolchains.

Pick one, or make a bridge? permalink

So, should we replace the build tool of with the build tool of the other, or should we just make one of them talk to the other? Addy Osmani has an article, Making Maven Grunt, whichdescribes this situation quite succinctly:

"More times than not, it’s the back-end team who will define the build process and end up having to wrangle your front-end code into there."

"Whilst there are many plugin options for JavaScript processing in the Maven world, these plugins can be a real chore to create, maintain and update. In addition, they often have outdated dependencies – meaning you need to monkey-patch them to work and developers – especially those not coming from a Java background – dislike it."

Thus it appears that in order to get the best of both worlds, the best option would be to create the bridge that allows one build system to talk to the other. In this case, to make maven invoke grunt.

The options permalink

Turns out that this is a problem that several have had, and solved, before.

If you your web app is generated by yeoman, then the yeoman-maven-plugin provides some sensible configurations that work out of the box. However, if you need something a little more flexible, the maven-antrun-plugin, or the exec-maven-plugin is your best bet.

However, I did not want to throw Ant into the mix, as I was already using two other build systems, and their syntaxes - and thus reluctant to throw a third syntax into the mix; thus exec-maven-plugin it was!

Why don’t my plugins run? permalink

I asked a question on Stackoverflow: "Maven exec-maven-plugin and maven-resources-plugin not running, not sure why"

Excuse the newbie question on this one - this was my first time using maven after all!

When including a maven plugin, you define a list of <executions>. I thought that defining a <phase> for each <execution> was sufficient to trigger them. I learnt that it simply was not. It is necessary to define at least one <goal> for the plugin to get invoked.

Why can’t my plugin find executables? permalink

A follow up question, also on Stackoverflow: exec-maven-plugin says cannot run specified program, even though it is on the PATH

Now this one was not a newbie question at all - it turned out to be a Windows-specific bug.

Basically, even though entering grunt on the command line worked, as it was on the path, when the maven plugin ran, it failed to find it, much to my puzzlement.

After much tinkering, and inspecting the source code of exec-maven-plugin, I found what I think is a bug in the plugin:

    CommandLine toRet;
    if ( OS.isFamilyWindows() && exec.toLowerCase( Locale.getDefault() ).endsWith( ".bat" ) )
    {
        toRet = new CommandLine( "cmd" );
        toRet.addArgument( "/c" );
        toRet.addArgument( exec );
    }
    else
    {
        toRet = new CommandLine( exec );
    }

It assumes that only commands that invoke .bat files need to be prepended with cmd /c.

I checked the npm path, where the links to all the global installed NodeJs libraries are, i.e. the ones installed using npm install -g, and found that it had, for each, a Unix shell script, with no extension, and a Windows batch script, with a .cmd extension.

Fixing the problem

Normally I would have fixed the problem by patching the bug in exec-maven-plugin. However, it was using a svn instead of git, and using xircles instead of github. I was not new to Subversion, but had not used it before in the context of a open source contributions. Prior to github the way I contributed to open source projects was:

  • svn diff > bug-that-this-patch-should-fix.diff
  • Attach bug-that-this-patch-should-fix.diff to the issue tracker, forum, or email the maintainer of the project.

I have since been spoilt by git’s cheap branching, and the ease with which pull requests can be managed in github’s interface.

I did give it a go, nonetheless, but I the web interface took more than a minute to load (kid not!), and that was all it took for me to start looking for another way to solve the problem.

A quick and easy way was to simply rename the .cmd files to .bat files, since they were batch script files anyway, and everything magically began working once more.

See my answer for a more detailed explanation.

Rant over permalink

That has been half rant, half description of my learning experience. I must say, I wish that a rather simple task like this were a little easier.

Hopefully this helps anyone else trying to use maven with grunt, on Windows.