You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 15 Next »

Status

This RFC is currently in the DRAFT state. Nothing in this RFC has been agreed or confirmed.

Contents

Introduction

The next generation Project Object Model to be used by Maven 5.0+

Background

Maven uses the Project Object Model as a descriptor for the declarative build requirements of a project.

  • Maven 1.x used a model which contained a <modelVersion>3.0.0</modelVersion> element as an immediate child of the root. 
  • Maven 2.x / 3.x has used a <modelVersion>4.0.0</modelVersion> element. 

Due to the way Maven has been implemented, the current release versions will consider any modelVersion other than the one that they target as invalid and will fail to parse the model.

For build time concerns, this is not that major a concern, and in fact may be desirable behaviour, e.g. I should not be able to build a Maven 2.x / 3.x project with Maven 1.x.

Where the modelVersion becomes a constraint, however, is when it comes to transitive dependency resolution. The Maven Central repository has grown in popularity, and now the consumers of the information in central are no longer only Apache Maven. There are other build tools that parse the POM to extract dependency information, e.g. Apache BuildrGradleApache Ivysbt, etc. As these build tools are not under the control of the Apache Maven project, we risk breaking their ability to parse the POM as a unit of dependency expression if we modify the pom schema or model version.

While we could change the schema if we "forked" the central repository, the experience from the previous reposotory fork (for the Maven 1.x / Model Version 3.0.0 to Maven 2.x / Model Version 4.0.0 transition) was traumatic and a repeat is generally considered to be a Bad Plan™.

The result of all this is that the Apache Maven project has been unable to evolve our POM to reflect the new needs.

The current plan for a Path Forward™ uses three legs:

  1. We keep deploying modelVersion 4.0.0 poms to the repository as a best effort expression of the dependency information of artifacts such that legacy clients can continue to consume artifacts deployed with non-legacy clients.
  2. We deploy a dependency-only model using a defined contract for forwards compatibility (to allow for future evolution) using a different file extension (see Project Dependency Trees schema)
  3. The POM then becomes a build-time only concern and does not need to be deployed to the repository - except for those cases where the pom may be used as either a parent or a mix-in

This page will represent (TODO replace "will represent" with "represents" when near finalised) the specification for the next modelVersion of the POM to be used by Maven.

Classification of change requests

There are currently 

type key summary assignee reporter priority status resolution created updated due

JQL and issue key arguments for this macro require at least one Jira application link to be configured

 flagged as either waiting for a major version bump in Maven because they are a behavioural change or waiting for a modelVersion bump because they change the POM schema. This section aims to classify and summarise the changes requested by users / developers in order to better understand the rationale for the proposed new POM schema.

New content to include in the POM

There are six general sub-themes around content to include in the POM.

The following issues look to add content for documentational purposes. This content would be consumed both by developers reading the POM "by hand" as well as by more automated tooling such as the Maven Site generation

  • Unable to render Jira issues macro, execution error.  looks for the ability to include links to the coding standards and formatting rules that a project uses. 
  • Unable to render Jira issues macro, execution error.  looks for the ability to document IRC channels. More generally, if IRC channels are documented, other kinds of instant messaging and social media channels should be documented.
  • Unable to render Jira issues macro, execution error.  looks for the ability to document quality management services (such as sonar) similar to how we allow defining continuous integration services (such as Jenkins)

The following issues look to add content to assist using maven on a specific project

  • Unable to render Jira issues macro, execution error.  looks for the ability to define information to display to the user if they invoke maven with no goals specified and no default goal defined in the POM. Additionally the message should be customizable per profile
  • Unable to render Jira issues macro, execution error.  looks for a way to validate plugin configuration. An interesting thought experiment would be to allow a POM to be parameterized with some parameters requiring input at invocation time such that Maven would always ask for that parameter (using a custom prompt) if it wasn't supplied.

The following issues concern configuration that needs to be shared between plugins

  • Unable to render Jira issues macro, execution error.  (probably could be handled as maven-site-plugin configuration) looks to allow defining a different site deployment URL for SNAPSHOT versions of the project compared with release versions)
  • Unable to render Jira issues macro, execution error.  looks for the ability to define the default encodings to be used when reading files (and optionally when writing files)
  • Unable to render Jira issues macro, execution error.  looks for the ability to define the default encodings to be used when writing the site reporting files

The following issues concern providing explanations of dependencies within the POM

  • Unable to render Jira issues macro, execution error.  looks for the ability to provide comments within the <dependency> tags as consumers are often unclear of the rationale for inclusion of some dependencies
  • Unable to render Jira issues macro, execution error.  looks for the ability to provide comments within the <dependency> and <excludes> tags for the same reason as MNG-3879

The following issue concerns property evaluation

  • Unable to render Jira issues macro, execution error.  looks for the ability to declare property references that would be evaluated before pulling in parent / mix-in / etc such that those property references could be used to control the parent / mix-in being pulled in.

The following issues repeat / revert changes that previous experience has deemed to be a mistake. As such the current opinion is that these issues should not be fixed.

  • Unable to render Jira issues macro, execution error.  looks to move the <distributionManagement> section out of the pom and into settings.xml. It is unclear how this would work as different projects would need different distribution management details. The only use case where this becomes valid is when deploying a custom fork of a project to an internal repository... 
  • Unable to render Jira issues macro, execution error.  looks to resurrect profiles.xml which was generally considered to be a mistake.

Supports / provides style concepts

The following issues are all essentially the same theme, namely look to add additional classes of dependency information to the dependency graph.

  • Unable to render Jira issues macro, execution error.  looks to provide a mechanism for a dependency to declare itself as being a drop-in replacement for another artifact. 
  • Unable to render Jira issues macro, execution error.  is a superset of MNG-177 and basically defines a new type of dependency graph declaration which indicates that an artifact is a drop-in replacement for another artifact.
  • Unable to render Jira issues macro, execution error.  looks to provide a mechanism to globally ban specific dependencies. The driving use case for this is that dependency A and dependency B are equivalent and the duplicate content needs to be resolved by removing one from the dependency graph.
  • Unable to render Jira issues macro, execution error.  looks to provide a mechanism for a dependency to declare itself as being a drop-in replacement for another artifact.
  • Unable to render Jira issues macro, execution error.  looks to provide a mechanism for a dependency to declare itself as being a drop-in replacement for another artifact.

Versioning related issues

There is no specific set of themes here:

  • Unable to render Jira issues macro, execution error.  looks for the ability to have automatic parent versioning. Some attempts in core have been made to enable the project version to be deterministic from e.g. source control such that the pom does not need to be modified in order to release.
  • Unable to render Jira issues macro, execution error.  looks to force users to always specify the versions of plugins.
  • Unable to render Jira issues macro, execution error.  looks to change the version range syntax from the mathematical range syntax used by Maven. 

Lifecycle related changes

Two main themes around lifecycle changes

The following issues relate to trying to solve dependency issues within a multi-module reactor where one module consumes artifacts at one point in the lifecycle which are produced at a different point in the lifecycle by a different module.

  • Unable to render Jira issues macro, execution error.  looks for a way to declare what the outputs of a plugin will be
  • Unable to render Jira issues macro, execution error.  wants to find a way around the "jar == target/classes prior to package phase" hack that normally enables mvn test to work on simple multi-module projects

The following issues relate to specification of the lifecycle itself.

  • Unable to render Jira issues macro, execution error.  looks to add a layer of indirection to the lifecycle bindings such that, say the "compile" phase could be bound to a generic "compiler" goal and then another layer could define that for a specific project / packaging the "compiler" generic goal would be fulfilled by a specific plugin's goal execution.
  • Unable to render Jira issues macro, execution error.  looks to define a specific plugin execution order within a phase. The driver for this use case is that the lifecycle cannot be customised so when a module needs a complex lifecycle in order to ensure correct inter-plugin execution order
  • Unable to render Jira issues macro, execution error.  looks to introduce more advanced constraints on the lifecycle, such as "finally" concepts as well as fork-points in the lifecycle, such as "either install or deploy but not both"

Scope related changes

There is no specific set of themes here:

  • Unable to render Jira issues macro, execution error.  looks to remove system scope.
  • Unable to render Jira issues macro, execution error.  looks to introduce a scope that would allow mix-ins for dependencyManagement

Profile activation

The following issues are all focused on gaps in profile activation:

  • Unable to render Jira issues macro, execution error.  looks to define profile deactivators which would be the inverse of profile activators. Some of the use cases seem a bit hacky, but as a principal being able to express activation via an inverse condition can be simpler for users to comprehend.
  • Unable to render Jira issues macro, execution error.  looks to be able to have profiles activated based on the project version
  • Unable to render Jira issues macro, execution error.  looks to make the activators into an extension point.

POM format

The following issues look to address deficiencies (perceved or otherwise) in the modelVersion 4.0.0 POM format:

  • Unable to render Jira issues macro, execution error.  looks to switch to attributes for some of the more annoying verbosity in the POM
  • Unable to render Jira issues macro, execution error.  looks to switch to attributes for some of the more annoying verbosity in the POM
  • Unable to render Jira issues macro, execution error.  looks to switch from XML to a custom DSL
  • Unable to render Jira issues macro, execution error.  looks to move the build/pluginManagement tag to the root level.

Mix-ins

The following issues look for mix-ins that allow content for the POM to be included from other sources:

  • Unable to render Jira issues macro, execution error.  looks for general purpose mix-ins
  • Unable to render Jira issues macro, execution error.  looks for an explicit pluginManagement scoped mix-in

Existing model

The existing 4.0.0 model POM has the following high-level structure:

<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>
 
  <!-- The Basics -->
  <groupId>...</groupId>
  <artifactId>...</artifactId>
  <version>...</version>
  <packaging>...</packaging>
  <dependencies>...</dependencies>
  <parent>...</parent>
  <dependencyManagement>...</dependencyManagement>
  <modules>...</modules>
  <properties>...</properties>
 
  <!-- Build Settings -->
  <build>...</build>
  <reporting>...</reporting>
 
  <!-- More Project Information -->
  <name>...</name>
  <description>...</description>
  <url>...</url>
  <inceptionYear>...</inceptionYear>
  <licenses>...</licenses>
  <organization>...</organization>
  <developers>...</developers>
  <contributors>...</contributors>
 
  <!-- Environment Settings -->
  <issueManagement>...</issueManagement>
  <ciManagement>...</ciManagement>
  <mailingLists>...</mailingLists>
  <scm>...</scm>
  <prerequisites>...</prerequisites>
  <repositories>...</repositories>
  <pluginRepositories>...</pluginRepositories>
  <distributionManagement>...</distributionManagement>
  <profiles>...</profiles>
</project>

The major critiques of the existing model are:

  • Overly verbose and repetitive - the main pain point is that the groupId, artifactId, etc are not specified as attributes
  • "I hate XML" - our current thinking is that this is really just a catch-all complaint from people who:
    • Don't like the schema / feel the schema is overly verbose
    • Want to produce an imperative build from a declarative build tool
    • Are tolling for fun and profit
  • Poorly specified dependency graph resolution
  • "Magic" inheritance - it can be difficult to determine how inheritance will affect the build.

The other issue with the existing model is that it is being used for two distinct purposes and as such finds it difficult to be a master of both:

  • The 4.0.0 POM serves as a declarative description of the build process for a project
  • The 4.0.0 POM serves as a description of the project dependency graph.

The vision of the 4.0.0 POM was that all projects would be cut from a series of standard templates (a.k.a. packaging):

  • Each template would define the appropriate lifecycles and phases of those lifecycles (hopefully most templates/packagings would be sufficiently served by the three default lifecycles: default, clean and site) and each template/packaging would define the plugin bindings against the lifecycle phases. 
  • Where a project needed a customized build process, the build engineer would initially explore how to develop the build process by customizing the bindings of an existing template/packaging.
  • Once the build engineer had determined the correct generic process for building this type of project, the build engineer would then solidify this build process into a custom template/packaging.

In this vision, almost all 4.0.0 POMs should basically consist of the following structure:

<project>
  <modelVersion>4.0.0</modelVersion>
  <parent> <!-- most projects should inherit from a parent pom of some sort --> 
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <relativePath/>
  </parent>
  <artifactId>...</artifactId> <!-- most projects should inherit the parent's groupId -->
  <version>...</version>
  <packaging><!-- THIS IS THE IMPORTANT BIT--></packaging>
  <dependencies>
    ... <!-- Here is the project dependencies -->
  </dependencies>
  <build> <!-- no custom plugin configuration or bindings -->
    <extensions> <!-- this is only needed if not inherited from the parent -->
      <extension>
        <groupId>...</groupId>
        <artifactId>...</artifactId>
        <version>...</version>
      </extension>
    </extensions>
  </build>
</project>

In other words, when using the 4.0.0 POM in accordance with its initial vision, there should be at most 25 lines of boilerplate above the specification of the project dependencies and in the ideal case that boilerplate can be reduced to ~14 lines which specify:

  • the versions of Maven which the POM is compatible with (the modelVersion)
  • the parent to inherit from (3 lines of information due to the use of XML elements instead of attributes)
  • the identity of this project (2 lines of information if inheriting the groupId from the parent)
  • the template/packaging that this project is built with

When we inspect real world POMs however, we see that this pattern is almost never followed. Instead of producing custom templates/packaging most projects instead just fight with a standard template/packaging. The end result of this kind of fighting is POMs that run into the 10,000+ LOC levels with many plugin bindings and overloading of an existing lifecycle binding and profiles used to enable additional side-build processes. The reasons cited for these long POMs include:

  • "It is too hard to make a custom template/packaging"
  • "This is a one-off project, we will never make another of this type, therefore it doesn't make sense to produce a custom template/packaging" 

Proposal

The most important change for the 5.0.0 POM is to split the dual usage:

  • The 5.0.0 POM will be used as a declarative description of the build processes of the project. 
  • The description of the project artifact dependency graphs will be provided by the Project Dependency Trees schema proposal.

The project dependency trees schema will be XML because that is designed to be a machine generated document that is for consumption primarily by machines but needs to remain easily parsable by humans. The choice of XML is dictated by the requirement to enable multiple tools to have a level of forward compatibility and at this time, the only cross-technology tool that can deliver a mapping 

TODO write this up... I'm just dumping stuff I have done on the mail thread here to make it easier to collaborate:

<project modelVersion="5.0.0" [groupId="..."] artifactId="..." [version="..."] packaging="...">
  [<parent groupId="..." artifactId="..." [version="..."] [relativePath="...']/>

  [<mixin groupId="..." artifactId="..." [version="..."]/>]
  [<mixin groupId="..." artifactId="..." [version="..."]/>]
  ...
  [<mixin groupId="..." artifactId="..." [version="..."]/>]

  [<lifecycle id="..." mode="override|inherit">
    <phase id="..." [after="..." | before="..."]/>
    <phase id="..." [after="..." | before="..."]/>
    ...
    <phase id="..." [after="..." | before="..."]/>

  </lifecycle>]
  [<lifecycle id="...">
    ...
  </lifecycle>]
  ...
  [<lifecycle id="...">
    ...
  </lifecycle>]

  [<scope id="compile" [mode="override|inherit"]>
    <dependency groupId="..." artifactId="..." [platformId="..."] version="..." [classifier="..."] type="..."/> <!-- type is mandatory-->
    <dependency groupId="..." artifactId="..." [platformId="..."] version="..." [classifier="..."] type="..."/>
    ...
    <dependency groupId="..." artifactId="..." [platformId="..."] version="..." [classifier="..."] type="..."/>
  </scope>]
  [<scope id="...">
    ...
  </scope>]
  ...
  [<scope id="...">
    ...
  </scope>]

  [<plugins [mode="override|inherit"]>
    <!-- this is what pluginManagement was -->
  </plugins>]

  [<bindings [mode="override|inherit"]>
    <!-- this is what plugins was, we make explicit here that this is the binding of executions into the lifecycles -->
  </bindings>]

  [<platform id="..." [mode="override|inherit"]>
    <activation>
      <!-- define how we determine that this platform can be built in the current environment -->
    </activation>
    <!-- allow platform specific mixins -->
    [<mixin groupId="..." artifactId="..." [version="..."]/>]
    <!-- allow platform specific lifecycles -->
    [<lifecycle id="...">
      ...
    </lifecycle>]

    <!-- allow platform specific dependencies -->
    [<scope>
      ...
    </scope>]

    <!-- allow platform specific bindings... but plugin management is from the root only -->
    [<bindings>
      ...
    </bindings>]

    <!-- allow most of the other root tags except platform and packaging and deployment config -->
  </platform>]
  [<platform id="...">
    ...
  </platform>]
  ...
  [<platform id="...">
    ...
  </platform>]

  <!-- packaging is only allowed in poms with an id of "parent" or "mixin". It allows a parent/mixin to be used by different packaging ids and define specialized defaults -->
  [<packaging id="...">
    [<mixin groupId="..." artifactId="..." [version="..."]/>]
    <!-- allow platform specific lifecycles -->
    [<lifecycle id="...">
      ...
    </lifecycle>]

    <!-- allow platform specific dependencies -->
    [<scope>
      ...
    </scope>]

    <!-- allow platform specific bindings... but plugin management is from the root only -->
    [<bindings>
      ...
    </bindings>]

    <!-- allow most of the other root tags except platform and packaging and deployment config -->
  </packaging>]
  [<packaging id="...">
    ...
  </packaging>]
  ...
  [<packaging id="...">
    ...
  </packaging>]

  <!-- unsure if we still need profiles -->
  <!-- perhaps we still need properties -->
  <!-- TBD deployment config, repositories, etc -->

</project>
 

 

Some things that came to mind, in no particular order:

  • scope becomes a build time only concern. Thus we can let users define custom scopes in their pom. If we let plugin executions declare scopes to resolve, we no longer need a compiler:testCompile goal as you can just have a second default execution of compiler:compile with different required scopes and different default configuration... bonus win, I can now add many different layers of test-compilation for integration tests, etc... each pulling in different scopes... ditto for surefire/failsafe... yeah integration tests
  • we should let the user define lifecycles directly in the Pom (ok, maybe we don't *encourage it*)
  • mixins can be properly considered... they only affect build time anyway
  • Pom doesn't need to be XML any more... (maybe we want to keep XML though... just a less verbose form)
  • does Maven 5 build Maven 2/3 projects?

 

Building the effective build time model would be:

 

  • Start with parent, add in matching packaging from parent, in Pom order, add each mix-in (including matching packaging from mix-in before processing subsequent mix-ins), finally apply local pom.

 

Appendix 1 - Issues flagged for consideration post-modelVersion 4.0.0

This is not a perfect query as it includes issues that target behavioural changes outside the scope of modelVersion changes, but all the relevant issues should be a subset of this list

key summary type status

JQL and issue key arguments for this macro require at least one Jira application link to be configured

  • No labels