Maven and Profiles: Using the same plugin in two different profiles and have both active at the same time
Asked Answered
E

2

6

We use the frontend-maven-plugin for using grunt and bower in our builds. With the Frontend Maven Plugin, I can install NPM locally, use Bower to download Java libraries, and run Grunt to optimize and obfuscate my code.

Like this with some simplification:

<plugin>
  <groupId>com.github.eirslett</groupId>
  <artifactId>frontend-maven-plugin</artifactId>
  <version>0.0.24</version>
  <executions>
    <execution>
      <id>install node and npm</id>
      <goals> <goal>install-node-and-npm</goal> </goals>
      ...
    </execution>
    <execution>
      <id>npm-install</id>
      <goals> <goal>npm</goal> </goals>
      ...
    </execution>
    <execution>
      <id>bower-install</id>
      <goals> <goal>bower</goal> </goals>
      ...
    </execution>
    <execution>
      <id>grunt-build</id>
      <goals> <goal>grunt</goal> </goals>
      ...
    </execution>
  </executions>
</plugin>

Note that the last execution is grunt-build which is where the JavaScript files are concatenated together, optimized (returns, comments, and other things removed) and obfuscated.

This works well for releases. However, developers would like to deploy the war without the JavaScript files being concatenated, optimized, and obfuscated. This will help them with debugging. To do that, we merely have to remove the grunt-build execution section from this plugin's configuration.

I'd like to use profiles to do this. I could have a profile called development that allows developers to do a build without that last section. I could simply copy and paste this section of the pom.xml file, remove that last execution, and put it into a separate profile. All done.

However, there's the old programming adage of don't repeat yourself. I'd be duplicating about 50 lines of code in my pom.xml.

What I'd like to do is have a way to execute the first three executions, and do the fourth only if this isn't a development build. I would have a few other nips and tucks in this as well. For example, I would have to copy in the JavaScripts themselves rather than the grunted results. But, that's easy enough to do and doesn't duplicate code.

This would cause duplicate code because I'd have to define the frontend-maven-pluginwith two configurations. Once for the development profile and once for the standard release build. As far as I know, I can't say, run this configuration of the frontend-maven-plugin, and if this isn't a development build, run this instance of the frontend-maven-plugin which will only do the grunt stuff.

Is there a way to define the same plugin twice in the pom.xm, and have Maven run both instances in the right order?

Eleanoraeleanore answered 22/10, 2015 at 19:21 Comment(2)
Rather than fixing your problem with maven I would suggest you start generating MAP files (html5rocks.com/en/tutorials/developertools/sourcemaps). This way your devs are testing the code that is going to go live, but they should still be able to debug it and see what's going on. I've seen optimisation and minification break JavaScript on more than one occasion. IMHO it's good practise to test using your final minified JS.Couching
Just out of curiosity: Did you implement the suggestions I gave in my answer or did you do something else?Horacehoracio
H
4

Let me cite from the Maven forum:

My takeaway was that if one just jumps on profiles as the solution to every conditional situation, the build will grow a second head like a hydra in a bad horror flick and you'll really regret it.

The point is that profiles are often an expedient substitute for using Maven correctly. They should always be considered "last resort" unless you know what you are getting yourself into.

In my situation, [...]. Profiles work great for that, but I can see exactly where the hydra head fits on the beast now and don't intend on doing anything more with them unless absolutely necessary.

I have made my experiences with profiles. I second this view.

Maven's POM is of declarative nature. That means you don't define what to do during a build but you declare how your project looks like (via a Project Object Model). With that Maven knows what to do and when to do it during a build. By using XML it's also strictly hierarchical top-down. I consider all this being a big advantage (not the only ones, there are a few more) compared to procedural or scripted build definitions (note declaration vs. definition) like with Ant or Gradle. With Maven profiles you "inject" something into this (hierarchical) declaration from "sideways". You can do this, of course, but you have to know what you do and you should've thoroughly thought about before why you do it.

From a conceptual point of view you have two projects that have most of their things in common. This is where Maven's POM hierarchy with its inheritance comes into play:

product ... parent for all projects below
  +- pom.xml ... contains build steps (apart from Grunt) and all other declarations 
  +- dev
  |    +- pom.xml ... almost empty since everything's inherited from parent
  |    +- ... sources, resources, etc. ...
  +- release
       +- pom.xml ... contains Grunt build step only, everything else's inherited from parent
                      and redirects all of <build>
                          <outputDirectory> <testOutputDirectory>
                          <sourceDirectory> <scriptSourceDirectory> <testSourceDirectory>
                          <resources> <testResources>
                      to ../dev/<all of the dirs above>

See The BaseBuild Element Set, Resources and The Build Element Set in the POM Reference for all the build directories to be redirected.

See Maven include another pom for plugin configuration:

The only correct answer is to use inheritance.

I second that, as well.

If you go for the profile nevertheless I recommend to call it dev rather than development. Your devs will be thankful forever. :)

Horacehoracio answered 22/10, 2015 at 22:10 Comment(8)
I'm not sure having two separate Maven modules for two environments is a good idea. This duplicates all the application code inside the modules.Ec
@Ec No, it doesn't. See dev: „... sources, resources, etc. ...“. See release: „redirecting <build directories> to ../dev/...“.Horacehoracio
Looks like a whole load of work and duplication for no good reason to me.Couching
@Couching „a whole load of work“? • Creating 2 (project) directories, • creating 2 almost empty (POM) files, • C&P devs declarations to the product and the release POM accordingly and • moving dev into the new structure? You're kidding, aren't you? And where exactly do you see duplication? The good reason is mentioned at the beginning of my answer. I also can go into details concerning my experiences with profiles if it turns out that it's really necessary for you and others.Horacehoracio
@GeroldBroserreinstatesMonica what about the need to create another git repository and keep it in synch with the main one? Maybe I did not get your point, but I can understand Ardesco's concerns. Decoupling a project may be worse than a complex configuration.Slabber
@Slabber Sorry for the late reply. I somehow overlooked your comment in the past. Please explain, perhaps with a use case, what you exactly mean by "another git repository" and "the main one" in the environment containing the three projects product, dev, release above.Horacehoracio
Sorry @GeroldBroser, by reading again the whole text, your proposal looks legit, probably I did not understand properly what you meant the last time. Btw I would have preferred to have a base configuration, and activate the "dev-only" features by using, or not, a certain profile (dev in this case).Slabber
@Slabber It's perfectly fine using a base cfg and adding something for a certain use case via a profile. The key points for my answer were: "[DRY] I'd be duplicating about 50 lines of code in my pom.xml.". The Maven solution to this is inheritance. David wrote too: "we merely have to remove the grunt-build execution section". A skip property as described by Tunaki in his answer isn't supported by all plugins/goals. I wasn't aware that it was/is available in the frontend-maven-plugin:grunt goal. Hence the split into product, dev, release to be able to build them independently.Horacehoracio
E
2

You are in luck, the grunt goal of that plugin defines a skip property so you only need to set this property to true in your custom profile.

As such, you need to create a profile development that sets a custom property skipGrunt to true and a default profile that sets it to false.

Then, in the plugin configuration of the grunt goal, you can add

<configuration>
    <skip>${skipGrunt}</skip>
    <!-- rest of configuration -->
</configuration>

This remark can be made more general: when you need to skip a specific execution of a plugin, there's generally a custom skip property that you can set to true.

Ec answered 22/10, 2015 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.