Status
This RFC is currently in the DRAFT state. Nothing in this RFC has been agreed or confirmed.
Contents
Introduction
The Project Dependency Trees artifact defines all the side artifacts of a project as well as each artifacts tree of dependencies. This can be used by consumers to decide what the consumers effective tree of dependencies is as well as allowing consumers to perform intelligent substitutions in the tree. By providing the entire tree we can reduce the number of requests a consumer needs to make in order to resolve all the artifacts the consumer requires.There are a number of issues with the current Project Object Model used by Maven:
- We do not have a good way to evolve the model or even change the model version
- We do not have a good way to model the differences in dependencies for the individual artifacts that get deployed as part of the project
- We do not have a good way to augment the information if we are deploying artifacts from the project non-atomically
- The model is weakly specified with regards to conflict resolution and exposes Java native assumptions about conflict resolution.
- The model does not allow for reproducible builds while simultaneously allowing range specification for dependencies
- The model exposes build time information that is irrelevant to consumers
The aim of the Project Dependency Trees model is to resolve these issues.
Model evolution
One of the top level elements of the Maven POM is the modelVersion
element that specifies the model version for the POM. To date there have been two model versions 3.0.0
and 4.0.0
. In both cases, a critical issue for changing the model version is that older clients cannot parse the newer model. This required the forking of Central (which is why central is repo.maven.org/maven2
because 4.0.0
was introduced with Maven 2 and Maven 1 clients could not parse the new model version)
Obviously, newer clients can always be written to parse older model versions, but given that Central is now a resource used by multiple build tools, not all of which run on the JVM or are maintained by the Apache Maven community, we need to ensure that any solution does not break the ability of other clients to consume the artifacts published to central.
NOTE: while we need to ensure that artifacts can be consumed by both older clients, we do not have to ensure that the older clients get the exactly correct dependency tree. Rather we should make the best effort possible to give older clients as good a dependency tree as we can give them.
Model evolution will be handled in two ways, based on the type of client:
- Legacy clients are clients that are not aware of the Project Dependency Trees model
- Modern clients are clients that are aware of the Project Dependency Trees model, but need not necessarily be aware of the latest
modelVersion
s deployed by the newest version of Maven.
Legacy clients
Legacy clients cannot be aware of the Project Dependency Trees model. For this reason, any project that deploys a Project Dependency Trees model will also deploy a modelVersion
4.0.0
POM which is the best-effort translation of the Project Dependency Trees model for the primary artifact of the project.
Modern clients
As part of the process of evolving a Project Dependency Trees schema, each new version of the model will be accompanied by an XSLT transformation(s) that will be published into Central at a defined set of coordinates. This will allow a modern client to convert a schema - that is newer than the highest modelVersion it was built against - into the newest modelVersion that it supports. In general the XSLT transformation will essentially strip out elements that are not understood by older clients, though it is possible that more adventurous transformations may be included.
The rationale for choosing XSLT as the transformation... and consequently forcing the Project Dependency Trees model to be expressed in XML is that XSLT is currently the only cross-platform transformation engine available across the JVM, Ruby, .NET, C/C++ native code and JavaScript runtimes.
TODO write the content and formalise the following example into a schema:
Schema
Here is a draft XML schema:
<xs:element name="project">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><project></code> element is the root of the project
dependency trees.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="modelVersion" type="xs:string"/>
<xs:attribute name="groupId" type="coordinate"/>
<xs:attribute name="artifactId" type="coordinate"/>
<xs:attribute name="version" type="coordinate"/>
<xs:attribute name="platformId" type="coordinate" use="optional"/>
<xs:all>
<xs:element ref="information" minOccurs="0" maxOccurs="1"/>
<xs:element ref="license" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="artifacts" minOccurs="1" maxOccurs="unbounded"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="license" xmlns:spdx="http://spdx.org/rdf/terms#">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><license></code> element defines one of the licenses
under which the artifacts are made available. Where a license is
attached to the <code><project></code> element this defines the
default licenses for all artifacts in the project. Where a license is
attached to an <code><artifact></code> element this signifies
that the specific artifact is covered by the
<code><license></code> elements defined within that
<code><artifact></code> element. Licenses are identified using
the <a href="http://spdx.org">SPDX</a> identifiers</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="spdxId" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="artifacts">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><artifacts></code> element is a container for
details of artifacts. When the <code><artifacts></code> attribute
is missing, then the artifacts listed are not platform specific.
The <code><artifacts></code> must be unique with respect to their
<code><platformId></code>, i.e. it cannot be repeated.
If the <code><project></code> element has a
<code><platformId></code> then there must be only one
<code><artifacts></code> element and it must have the matching
<code><platformId></code>.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="platformId" type="coordinate" use="optional"/>
<xs:sequence>
<xs:element ref="artifact" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="artifact">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><artifact></code> element represents an artifact
associated with the project.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="type" type="coordinate"/>
<xs:attribute name="classifier" type="coordinate" use="optional"/>
<xs:all>
<xs:element ref="information" minOccurs="0" maxOccurs="1"/>
<xs:element ref="license" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="component" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="provides" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="requires" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="supports" minOccurs="0" maxOccurs="unbounded"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="component">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><component></code> element represents a type specific
component that is present within the artifact. For example a "jar"
artifact might list the Java 9+ modules that are included within
the "jar". Other file types can use the component according to the
conventions of that file type. The component information is intended
to assist build time tools in conflict detection when resolving
the composite dependency tree according to the build tools
dependency resolution strategy.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="provides">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><provides></code> element represents a semantic
equivalence with another artifact. There are several ways the element
can be used.
<nl>
<li>
When an artifact directly includes the same content as another
project's artifacts, for example there are some "jar" files that
will embed other artifacts to produce a so-called "uber-jar".
</li>
<li>
When an artifact re-implements the API of another project's
artifact. For example: log4j-over-slf4j reimplements the log4j
API.
</li>
<li>
When a set of projects are co-operating to provide multiple
implementations of a "virtual" project artifact. For example:
slf4j-log4j, slf4j-jul, and logback could all be considered
as providing a slf4j-impl virtual project artifact. There would
be no actual project at the slf4j-impl coordinates, but
slf4j-api could declare a requirement on the "virtual" project
artifact in order to ensure that an implementation is available
to consumers of the API
</nl>
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="groupId" type="coordinate"/>
<xs:attribute name="artifactId" type="coordinate"/>
<xs:attribute name="platformId" type="coordinate" use="optional"/>
<xs:attribute name="version" type="coordinate"/>
<xs:attribute name="range" type="xs:string"/>
<xs:attribute name="type" type="coordinate"/>
<xs:attribute name="classifier" type="coordinate" use="optional"/>
</xs:complexType>
</xs:element>
<xs:element name="requires">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><requires></code> element represents a hard dependency
on another project's artifact. If the <code><version></code>
attribute is missing then this indicates that the dependency is
a virtual dependency, and there must be no child elements.
The <code><modelVersion></code> attribute must only be present
if the dependent project's <code><modelVersion></code> is newer
than the <code><modelVersion></code> specified on the root
<code><project></code> element. The presence of this element
indicates that the child information was the result of an XSLT
transformation of a newer <code><modelVersion></code> and
indicates that a build tool understanding the newer
<code><modelVersion></code> may want to fetch the dependencies
tree and process it directly in order to obtain the most correct
model of the dependency.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="groupId" type="coordinate"/>
<xs:attribute name="artifactId" type="coordinate"/>
<xs:attribute name="platformId" type="coordinate" use="optional"/>
<xs:attribute name="version" type="coordinate" use="optional"/>
<xs:attribute name="range" type="xs:string"/>
<xs:attribute name="type" type="coordinate"/>
<xs:attribute name="classifier" type="coordinate" use="optional"/>
<xs:attribute name="modelVersion" type="xs:string" use="optional"/>
<xs:all>
<xs:element ref="license" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="component" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="provides" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="requires" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="supports" minOccurs="0" maxOccurs="unbounded"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="supports">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code><supports></code> element represents a soft dependency
on another project's artifact. This element is provided in order to
allow build time tools to perform conflict resolution when determining
the effective tree from multiple dependencies.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="groupId" type="coordinate"/>
<xs:attribute name="artifactId" type="coordinate"/>
<xs:attribute name="platformId" type="coordinate" use="optional"/>
<xs:attribute name="version" type="coordinate"/>
<xs:attribute name="range" type="xs:string"/>
<xs:attribute name="type" type="coordinate"/>
<xs:attribute name="classifier" type="coordinate" use="optional"/>
</xs:complexType>
</xs:element>
</xs:schema>