7

The problem to which I cannot find a nice, scalable solution:

I have a project that delivers multiple flavours of the given artifact. This has been set up as a multimodule project, currently with 3 modules:

  • /flavour1_module
  • /flavour2_module
  • /flavour3_module

The thing is that I have another 50 projects that need to be setup in the same way, i.e. delivering the 3 flavours.

Solutions considered:

  1. turning the already created multimodule project into a parent for all other 50 projects
    • Cons: It just doesn't work. The instructions kept withing parent's modules are not inherited, thus they are not executed.
  2. using maven-archetype-plugin to create a multi-module project template, and then creating all 50 projects based on the template
    • Cons: if I would need flavour4, I need to manually update all 50 projects to add flavour4_module (and duplicate its content). Not scalable.
  3. embedding the configuration of all flavours into a single pom and enable or disable them based on profiles (i.e. using composition by profiles instead of inheritance via modules). Then pointing the 50 projects to it, as their parent. This would create "inline" modules
    • Cons: I would need to implement on my own mechanisms which are provided by modules out of the box. (e.g. building every flavour in a separate directory). I would also lose the clear seperation that modules provide.

Any ideas how to do it nicely? Is there any other option?

Thanks, Lukasz

Edit:

Another option would be extending the maven-reactor-plugin with reactor:inject-modules goal, which would download module definition from an external artifact, and attach its definition as a normal module. This would create a new module on the fly. Then all 50 projects could make this pom.xml their parent.

The configuration would look like this (a draft):

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-reactor-plugin</artifactId>
  <version>1.0</version>
  <executions>
    <execution>
      <id>inject</id>
      <phase>initialize</phase>
      <goals>
        <goal>inject-modules</goal>
      </goals>
      <configuration>
        <modules>
          <module>
            <artifactId>flavour1_module</artifactId>
            <groupId>[ groupId ]</groupId>
            <version>[ version ]</version>
          </module>
          <module>
            <artifactId>flavour2_module</artifactId>
            <groupId>[ groupId ]</groupId>
            <version>[ version ]</version>
          </module>
          <module>
            <artifactId>flavour3_module</artifactId>
            <groupId>[ groupId ]</groupId>
            <version>[ version ]</version>
          </module>
        </modules>
      </configuration>
    </execution>
  </executions>
</plugin>

Would going this way make sense?

Update:

Writing a plugin that manipulates the list of modules to execute (the idea of module injection that I described above) doesn't seem to be possible to implement, because modules are processed by maven core, and the mechanism was not designed to be extended with a plugin. This is confirmed by the fact that both plugins that do a similar job i.e. manipulating the list of projects to execute:

do the trick by executing a system call to create maven child process. For me it is not the way to go, because it's a very unstable solution. Indeed maven-reactor-plugin became incompatible with Maven3.

maven-invoker-plugin still looks promising. The plugin was originally designed to run integration tests, but it would be possible to use it to extend e.g. the compilation phase. But it requires the child pom.xml-s to be treated as resources and modified on the fly. For the problem that I described here the solution would be too complicated and unstable. I would prefer something lighter that could operate in memory while building maven model.

So for now I use profiles, trying to make them as compact as possible. Probably in a while I will need again to think about the problem.

Lukasz Guminski
  • 862
  • 7
  • 20
  • Does that mean that the different flavours of the artifacts are differ in configuration files or do they differ in more essential parts? – khmarbaise Apr 21 '12 at 09:32
  • At the moment they differ in configuration files and format of packaging. Soon it is expected to be more differences. – Lukasz Guminski Apr 21 '12 at 10:30
  • Hm. Packaging? Do you mean by that to have number of classes packaged as jar and than packaged into .tar.gz or what do you mean by format of packaging ? Or is just the structure of the package meant? – khmarbaise Apr 21 '12 at 10:34
  • Each flavour is intended for a different customer, who receives applications with a different subset of resources packed inside. There are also native parts inside compiled for different architectures depending on the customer. – Lukasz Guminski Apr 21 '12 at 11:01
  • @LukaszGuminski Funny enough, but I cannot see the goal inject-modules on the maven-reactor-plugin on version 1.0 which seems to be the latest. Also even if that worked, you still have to manually create those modules? – Kalpak Gadre Apr 22 '12 at 13:33
  • @Kal There is no such functionality. That's why I wrote about "extending the maven-reactor-plugin with reactor:inject-modules goal". The modules would need to be created once, then they would be imported in the parent pom, as shown above, and then executed while building each of 50 projects. This way redundancy would be avoided. – Lukasz Guminski Apr 22 '12 at 21:11
  • It's funny I asked this very question today. I think I might've gotten closer to a better solution than 1, 2 and 3 --> please see http://stackoverflow.com/questions/11749375/import-maven-plugin-configuration-by-composition-rather-than-inheritance-can-it. – Tony Lâmpada Aug 01 '12 at 04:38
  • And please vote for [MNG-5127](http://jira.codehaus.org/browse/MNG-5127) (or even [Sponsor it](http://www.freedomsponsors.org/core/issue/add/?trackerURL=http://jira.codehaus.org/browse/MNG-5127)) – Tony Lâmpada Aug 01 '12 at 04:41

4 Answers4

1

Now you can use the maven-tiles plugin to get the desired behaviour.

With maven-tiles you can define build behaviours in different poms and import them wherever you like.

There's a working sample attached to MNG-5102 --> daddy3.zip

Maven's inheritance-only strait jacket is now officially removed.

Tony Lâmpada
  • 5,301
  • 6
  • 38
  • 50
  • If `mvn help:effective-pom` works with tiles, then this would be a perfect solution. Composition solves this problem much better than inheritance. – Lukasz Guminski Oct 30 '12 at 14:45
0

In my opinion, you could create multiple different assembly descriptors for this and configure multiple plugin executions with each execution referring to different descriptor. You will have to maintain your project as a single module instead of multi module project. Provided your distributions contain same set of classes but different resources or libraries, it could work.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <id>assembly</id>
                    <phase>package</phase>
                    <configuration>
                        <descriptors>
                                <descriptor>one.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
                <execution>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <id>assembly</id>
                    <phase>package</phase>
                    <configuration>
                        <descriptors>
                                <descriptor>two.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
                <execution>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <id>assembly</id>
                    <phase>package</phase>
                    <configuration>
                        <descriptors>
                                                      <descriptor>three.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
            </executions>
        </plugin>

You could create a parent pom with assembly pluing configured there so that you can control the number of distributions and the change in packaging from there. Your projects will not need to know about details of different packaging.

I will strongly recommend to keep native libraries as separate module and use repository mechanism to install the compiled libraries into it. You can use different classifiers to segregate the platforms like,

mylib-2.0.0-win32_x86.dll
mylib-2.0.0-linux_x86.so
mylib-2.0.0-linux_x86_64.so

You can then reference to these libraries as dependencies in your project and then be able to package them along with your distribution.

The overall solution will greatly depend on how various distributions will differ and the overall process of packaging the distribution but I think this will work.

The ultimate and more scalable solution is to create your own packaging by implementing a Maven Plugin.

Kalpak Gadre
  • 6,285
  • 2
  • 24
  • 30
  • This kind of solutions work as long differences between flavours are relatively small (e.g. limited to packaging). But in my case the responsibility for providing each flavour belongs to a different team, so I cannot make the assumption that the changes will be small. – Lukasz Guminski Apr 22 '12 at 10:21
  • The organizational constraint means also for me, that I cannot allow "inlining" the configuration of all flavours in a single pom, without providing a rudimentary structure for the teams to not interfere with each other. So Maven profiles, as a simple mechanism of grouping configurations, is the minimal thing I need to provide. But still I would prefer modules. – Lukasz Guminski Apr 22 '12 at 10:22
  • Regarding native components, they are already managed as separate Maven projects. But the problem that I described in my question affects also them. How to describe how to compile for a new architecture, without the need of changing each native Maven project separately? – Lukasz Guminski Apr 22 '12 at 10:22
  • Summing up, I still cannot see a good solution to the problem of managing multiple flavours in large projects. – Lukasz Guminski Apr 22 '12 at 10:22
  • BTW in my case also resources are maintained as separate Maven projects. So each flavour has its own dependencies on resources it needs. This already indicates that the dependencies for all flavours should not be maintained in a single pom, but split into modules where each module manages its own dependencies. – Lukasz Guminski Apr 22 '12 at 10:32
  • @Lukasz Giminski How do you build your native projects? In past I have had native projects built using a make file, where make file was called through Maven antrun plugin. Maven also has a Native plugin, but haven't used it much. In both cases, you simply need to delegate the build on the right platform? I had done that using Build Agents on Jenkins where each build agent represented the target platform like Linux, Solaris, Windows etc. – Kalpak Gadre Apr 22 '12 at 13:11
  • Also in case you have make files specific to platform, you could handle that by use of profiles (activation by architecture) – Kalpak Gadre Apr 22 '12 at 13:18
  • Kal, How to build native components is not related directly to this question. But for your information, I use antrun, which triggers building for various architectures using cross-compilers. However because for each architecture there is a separate build process, I had to handle on my own how to build on release process all flavours one after another into separate directories. This is the feature that modules give out of the box, and with profiles it is much harder. – Lukasz Guminski Apr 22 '12 at 21:31
0

In my experience option 3 has worked the best, but I haven't had to scale it like you need to- when I've had to do that I've used the maven-ant-plugin to create a parameterized ant script to do the customization. It has some downsides- namely working with both ant and maven so the actual POM is more difficult to comprehend but it does allow for more flexibility than is otherwise possible with maven.

Steven Fines
  • 467
  • 4
  • 14
-1

If you are using maven 3, you can define profiles the parent pom and activate them base on file existance. On the child modules you can "inherit flavours" by simply creating empty files.

This is better documented on the links below:

Community
  • 1
  • 1
Tony Lâmpada
  • 5,301
  • 6
  • 38
  • 50