Status 
Version 
Issue(s) 
Sources 
Developer(s) Stephen Connolly

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:

The aim of the Project Dependency Trees model is to resolve these issues.

Project Dependency Trees documents are intended to be machine generated based on build time information. Build tools not able to generate these documents solely from build information are considered in need for corresponding enhancements.

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 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

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.

Artifact Dependency differentiation

The current POM provides a single scoped dependency tree that is then universally applied to all artifacts produced by the project. This does not align correctly with what the artifacts produced by a single project actually require.

To illustrate by example:

Consider a project that builds a Java Web application that can be run standalone or as part of an EAR. Under current best-practice we would advise separating the project into multiple modules:

There are other ways to skin this cat, but what we really want to have is that there is a single project that produces:

Each of these artifacts will have different effective dependencies, for example the test JAR will have a dependency on the main JAR and then a dependency on the test framework, etc.

The way the modelVersion 4.0.0 POM handled these different dependencies was via <scope> tags. The issue with scope tags is that the valid scopes become part of the model version and the information about which scopes apply to which artifacts has been lost to the build process by the time the artifacts is consumed by a consumer.

To solve this issue, the Project Dependency Trees will list the effective consumption required dependencies of each artifact produced by the project. 

NOTE the Project Dependency Trees will have no concept of scope. This may cause issues for artifact types where for example a different dependency is transitive during compilation compared with execution. The current thinking is that such situations will be exceedingly rare if they ever occur, and that as such, for least surprise, the consumer will have to configure their build tool to address this issue if it ever arises.

Non-atomic deployment

The primary driver of non-atomic deployment is the production of platform specific artifacts for the project. In this regard, the Project Dependency Trees model assumes that deployments will be at least atomic per platform. "At least atomic per platform" means that the initial deployment may include multiple platforms and subsequent additional deployments will be atomic per platform. For example:

The foo project produces some non-platform specific artifacts as well as artifacts for the os-xwindows and linux platforms. A build on a say os-x may be able to use cross-compiling tooling to produce artifacts for the linux platform (e.g. you can use rpmbuild on a mac, so you could create the RPM installer when building the project on some macs). Thus if we perform the release from a mac, our initial deployment will include the non-platform specific artifacts (e.g. the standalone WAR file for the web application) as well as some platform specific artifacts (e.g. the OS-X installer and perhaps the RPM & DEB installers for linux systems). At the com.example:foo::1.0 coordinates we would deploy:

Later, we perform a checkout of the tag from SCM on a windows machine and perform the build and deployment of the windows specific artifacts.

NOTE: as the platformId is the unit that separates atomically deployable components, it will be up to the tooling providers to agree on what values of individual platformIds mean for that specific tooling. The example above used high-level operating systems as platformIds, but without prejudice, we could equally have os-x-10.10os-x-10.9linux-fedora-25, linux-fedora-24, linux-rhel-6linux-centos-6, linux-ubuntu-12.04windows-server-2012, etc. Similarly we could have the platform differentiate in other ways, e.g. java7java8android, etc. Or perhaps the platform could differentiate artifacts that target different runtimes, such as tomcatjettyweblogicgeronimo, etc where the major difference in those platform specific artifacts is the dependency trees.

NOTE: while different projects can follow different conventions for what the different platformIds are used for, as the dependency's platformId is part of the dependency tree, the project can perform the appropriate mapping of its transitive dependencies platform identifiers into its own convention, so deviations in conventions between projects should not prove fatal.

Conflict resolution

The modelVersion 4.0.0 POM mixes build time dependency specification with consumption time dependency specification. This has the effect of significantly complicating the dependency model within the POM:

The above set of "rules" make it hard for other toolings to process the dependencies of a POM correctly, and consequently there are many many examples of real world POMs where various hacks have been used to tame the effective dependency tree in order to produce the required transitive tree for consumers.

The Project Dependency Trees simplifies the work of consumers by explicitly providing the fully intended resolved tree to be used by consumers. There is no requirement for a consumer of a project's artifacts to consult any transitive dependencies (though if the consumer has a better understanding of a specific transitive dependency modelVersion the consumer may want to consult, it does not have to).

The consumer is then free to decide how to resolve conflicts, and because the tree has been provided, in the event that conflict resolution requires dependency substitution, the tree can be pruned safely (whereas with a flattened list, safe substitution would not be possible as we could end up retaining orphaned transitive dependencies)

The consumer is also free to decide if conflicts need to be resolved at all. For example, an OSGi container can correctly manage multiple versions of the same module whereas the Java 9 modulepath can only have one version of any specific module. When a project produces a JAR artifact that contains both the OSGi module metadata as well as the Java 9 module info, it has no way of knowing whether the consumer will want to apply a "single version per module" rule or "all versions of each module" rule and nor should it, only the consumer can know how conflicts should be resolved.

Conflict resolution is also related to the next issue.

Version ranges and reproducible builds

One of the main issues with version ranges in the modelVersion 4.0.0 POM is that they produce an irreproducible build, as the consumer will re-resolve the version range every time it builds the dependency tree, and as such may resolve a different version.

The utility of version ranges comes into play when performing conflict resolution. If a consumer has to pick a single version of each dependency, the range information allows that version selection to be performed safely... i.e. if I have transitive dependencies on com.example:foo::[1.0,)com.example:foo::[1.2,2.0) and com.example:foo::[1.1,1.4.5],[1.4.7,) then I can construct the effective safe range of [1.2,1.4.5],[1.4.7,2.0) and select the appropriate single version.

The irreproducibility of version ranges is still somewhat of an issue though. We can resolve the irreproducibility of builds by recognising that it is really a trade-off choice that the consumer should make.

The consumer should be able to choose between:

 

The Project Dependency Trees model enabled consumers to make this choice by providing not only the version range but also the resolved version of each dependency. The version range can then be used to guide conflict resolution and the resolved version information can be used as hints to pre-select the exact version to use if the consumer wants a reproducible build.

Build time information

The Project Dependency Trees model removes all the build time information that was previously exposed from the modelVersion 4.0.0 POM, thus there is no <build><profiles> or <reporting> sections. 

This points to a legitimate concern about how to handle project inheritance while moving the POM beyond modelVersion 4.0.0. The solution here is to define two classes of compatibility.

In other words:

Thus only projects that are intended to be consumed as either parent projects or as mix-in projects would deploy their newer modelVersion POM.

OPEN QUESTION: do we deploy the newer modelVersion POM as the groupId:artifactId::version::pom or as groupId:artifactId::version:build:pom? The first form ensures that the POM cannot be used as a parent by modelVersion 4.0.0 projects as they will blow up immediately, however there has been an established practice of using <packaging>pom</packaging> for projects that produce non-standard artifacts and want to opt-out of the standard lifecycle binding, and thus we would break consumption of those "side" artifacts by legacy clients. Perhaps the solution is to follow the second form (i.e. it gets deployed with <classifier>build</classifier> and either put a Maven enforcer execution into the modelVersion 4.0.0 POM or use the <prerequisites> tag to try and at least alert that the parent is invalid.

Open Questions

Project Dependency Trees

<project> element

The project dependency trees model consists of a top level <project> tag and three types of immediate children elements:

<project modelVersion="..." groupId="..." artifactId="..." [platformId="..."] version="...">
  <generator .../>
  <information .../>
  <license .../>
  <artifacts .../>
</project>

The following are mandatory elements:

The following are optional elements:

<generator> element

The generator element identifies the build tool that created the document.

<generator name="..." version="..." url="..."/>

The following are mandatory elements

There are no optional elements

<information> element

The information element consists of optional information about the project and its artifacts.

TODO: decide what, if any, additional content can go in here, SCM, Issue trackers, URLs, Mailing Lists, etc.

<information>
  <name .../>
  <description .../>
</information>

There are no mandatory elements

The following are optional elements:

<license> element

The license element consists of information about one set of licensing terms that the project and its artifacts is made available under.

<license spdx="..."/> 

There is one mandatory element

There are no optional elements

<artifacts> element

The artifacts element consists of details of all the artifacts produced by the project. The artifacts are partitioned by platformId.

<artifacts [platformId="..."]>
  <artifact .../>
</artifacts>

There is one mandatory element:

There is one optional element:

Uniqueness constraints apply to the artifacts element.

<artifact> element

The artifact element consists of the details of a specific artifact produced by the project.

<artifact type="..." [classifier="..."]>
  <information .../>
  <license .../>
  <component .../>
  <provides .../>
  <requires .../>
  <supports .../>
</artifact>

There is one mandatory element:

The following are optional elements:

<component> element

The component element consists of hints to the consumer of type specific components that are present within the artifact for consideration during conflict resolution.

<component id="..."/>

There is one mandatory element:

There are no optional elements:

One anticipated usage of the component element is for JAR artifacts that the id would correspond to the Java 9+ module identifiers as in Java 9+ the module identifier must be unique on the module path and hence conflict resolution will be required to process the dependency tree into a flattened modulepath with validation of uniqueness of component identifiers enforced.

<provides> element

The provides element indicates that this artifact embeds equivalent content to the named dependency

<provides groupId="..." artifactId="..." [platformId="..."] [version="..."] range="..." type="..." [classifier="..."]/>

The following are mandatory elements

The following are optional elements

Some examples may assist in the relative use-cases of the range and version attributes.

<requires> element

The requires element indicates a mandatory transitive dependency.

<requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."] [modelVersion="..."]>
  <component .../>
  <license .../>
  <provides .../>
  <requires .../>
  <supports .../>
  <including>
    ...
  </including>
  <excluding>
    ...
  </excluding>
</requires>

The following are mandatory elements:

The following are optional elements:

<supports> element

The supports element indicates an optional transitive dependency.

<supports groupId="..." artifactId="..." [platformId="..."] [version="..."] range="..." type="..." [classifier="..."]/>

The following are mandatory elements

The following are optional attributes

<including> element

The including element indicates that a dependency has been augmented by its immediate consumer.

<including>
  <component .../>
  <provides .../>
  <requires .../>
  <supports .../>
</including>

There are no mandatory elements

The following are optional elements:

<excluding> element

The including element indicates that a dependency has been augmented by its immediate consumer.

<excluding>
  <component .../>
  <provides .../>
  <requires .../>
  <supports .../>
</excluding>

There are no mandatory elements

The following are optional elements:

Example

The following is a pseudo-example of a Project Dependency Tree

 <project modelVersion="..." groupId="..." artifactId="..." [platformId="..."] version="...">
    <generator name="Apache Maven" version="5.0.0" url="http://maven.apache.org"/>
    <information>
        <!-- container for descriptive information -->
        [<name>...</name>]
        [<description>...</description>]
        ...
    </information>
    <license spdx="..."/>
    <license spdx="..."/>
    ...
    <license spdx="..."/>
    <artifacts [platformId="..."]>
        <artifact type="..." [classifier="..."]>
            <information>
                <!-- optional element if need to override root level information for specific artifacts -->
            </information>
            <!-- 
              components are internal packaging constructs used by the packaging type but requiring more general validation
              e.g. for Java 9+ the ids could be the module ids if we wanted to validate that the module ids were unique in the
              effective tree.
            -->
            <component id="..."/>
            <component id="..."/>
            ...
            <component id="..."/>
            <!--
              If the artifact has a different set of licenses from those defined at the project level, we define the licenses
              of this artifact here. Otherwise we defer to the licenses defined at the top level of the project.
              licensing is a top level concern, and legitimately can vary per artifact. Let's not solve license compatibility, 
              rather leverage https://spdx.org/
            -->
            <license spdx="..."/>
            <license spdx="..."/>
            ...
            <license spdx="..."/>
            <!--
              provides is a marker that we have duplication of content. This could be because we are much like the many servlet-api jar
              files where there are many GAV's of the same javax.servlet:servlet-api:3.0 thus we could have the case where
 
              org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:jar:1.0.2.Final PROVIDES javax.servlet:servlet-api:3.0
              org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:jar:1.0.1.Final PROVIDES javax.servlet:servlet-api:3.0
              org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:jar:1.0.0.Final PROVIDES javax.servlet:servlet-api:3.0
              org.mortbay.jetty:servlet-api-3.0:jar:7.0.0pre2 PROVIDES javax.servlet:servlet-api:3.0
 
              similarly
 
              org.slf4j:log4j-over-slf4j:jar:1.7.21 PROVIDES log4j:log4j:[1.0,2)
 
              The consumer of the tree can then decide if/when/how to collapse redundant nodes as they see fit.
 
              TODO: decide optionality of version and range attributes
            -->
            <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]>
                <!-- no elements here as we have "rebundled" hence implicitly promoted up one level-->
            </provides>
            <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            ...
            <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <!--
              requires are the mandatory dependencies. This is effectively a recursive artifact where the GAV is not inherited and
              where we have discarded the information section. If you want those details, fetch that project's dependencies trees.
            -->
            <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                <component id="..."/>
                <license spdx:id="..."/>
                <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
                <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                    ...
                </requires>
                <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            </requires>
            <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                ...
            </requires>
            ...
            <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                ...
            </requires>
            <!--
              supports are the optional dependencies. We list them here to aid in conflict resolution. We do not include a nested tree
              as a consumer would only pull them in if the consumer already has its own a requires for them, so we really only
              need to validate the range. 
 
              TODO: decide optionality of range attribute
              TODO: decide if we want a version attribute 
            -->
            <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <including>
                <component id="..."/>
                <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
                <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                    ...
                </requires>
                <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            </including>
            <excluding>
                <component id="..."/>
                <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
                <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]/>
                <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            </excluding>
        </artifact>
        <artifact ...>
            ...
        </artifact>
        ...
        <artifact ...>
            ...
        </artifact>
    </artifacts>
    <!-- if the project does not specify a platformId then we can include additional platform details that were part of the atomic deployment -->
    <artifacts platformId="...">
        ...
    </artifacts>
        ...
    <artifacts platformId="...">
        ...
    </artifacts>
</project>

Constructing a Project Dependency Trees model

The following process will be used to construct a Project Dependency Trees model:

  1. For each artifact, construct the list of direct mandatory dependencies
  2. For each artifact, construct the list of direct optional dependencies
  3. For each artifact, construct the list of embedded dependencies
  4. For each artifact, construct the list of licenses
  5. For each artifact, construct the list of components
  6. Construct the Project Dependency Trees of the embedded dependencies
  7. Process the embedded dependency trees. 
    1. Any provides elements should be appended to the list of embedded dependencies. 
    2. Any requires elements should be appended to the list of direct mandatory dependencies. 
    3. Any supports elements should be appended to the list of direct optional dependencies.
    4. Any component elements should be appended to the list of components
    5. Licensing will be assumed to have been correctly defined by the project when the decision was made to embed dependencies
  8. Process the list of direct optional dependencies, removing any duplicates with the list of embedded dependencies (i.e. if in promoting an embedded dependency's supports tags we support it and have also embedded it, then we just have embedded it)
  9. Process the list of direct mandatory dependencies, removing any duplicates with the list of embedded dependencies (i.e. if in promoting an embedded dependency's provides tags we provide it and have also required it, then we just have provided it)
  10. Process the list of direct optional dependencies, removing any duplicates with the list of mandatory dependencies (i.e. if in promoting an embedded dependency's supports tags we support it and have also required it, then we just have required it)
  11. Construct the Project Dependency Trees of the mandatory dependencies

The principle is that 

If a dependency is missing a Project Dependency Trees model, then the same process can be used to construct that model from the modelVersion 4.0.0 POM

TODO define the process for constructing the effective modelVersion 4.0.0 POM

TODO decide if we should include some "well known" conventions, e.g. that:

The above would probably be generally useful but would complicate the specification of the process for other consumers

Test data set

TODO: We need a cohort of Project Dependency Tree documents for implementations to validate their parsers against and to validate their convertion into modelVersion 4.0.0 POMs (though we may do this using a provided XSLT file, implementations will still need to validate that their have configured their XSLT engine correctly)

TODO: We need a cohort of modelVersion 4.0.0 POMs for implementations to validate their generation of effective Project Dependency Trees in the absence of a deployed Project Dependency Trees document along with the expected effective Project Dependency Tree documents

TODO: We need a cohort of sample transformations of trees to ensure that implementations can correctly aggregate Project Dependency Trees when building new projects that depend on other projects.

Schema

Here is a draft XML schema:

TODO: Add the including and excluding elements to the schema

 <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" >
  <xsd:simpleType name=”coordinate”>
    <xsd:restriction base=”xsd:string”>
      <!-- TODO add pattern for groupId/artifactId/platformId/version/type/classifier valid values -->
    </xsd:restriction>
  </xsd:simpleType>
  <xs:element name="project">
    <xs:annotation>
      <xs:documentation source="version">5.0.0+</xs:documentation>
      <xs:documentation source="description">
        The <code>&lt;project&gt;</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="generator" minOccurs="1" maxOccurs="1"/>
        <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="generator">
    <xs:annotation>
      <xs:documentation source="version">5.0.0+</xs:documentation>
      <xs:documentation source="description">
        The <code>&lt;generator&gt;</code> element identifies the build tool
        responsible for creating this document
      </xs:documentation>
    </xs:annotation>
    <xs:complexType>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="version" type="xs:string"/>
      <xs:attribute name="url" type="xs:string"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="information">
    <xs:annotation>
      <xs:documentation source="version">5.0.0+</xs:documentation>
      <xs:documentation source="description">
        The <code>&lt;information&gt;</code> element is a container for 
        descriptive information about either all the artifacts in a project or 
        a specific artifact.
      </xs:documentation>
    </xs:annotation>
    <xs:complexType>
      <xs:all>
        <xs:element name="name" type="xs:string" maxOccurs="1"/>
        <xs:element name="description" type="xs:string" maxOccurs="1"/>
        <!-- TODO add additional elements -->
      </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>&lt;license&gt;</code> element defines one of the licenses
        under which the artifacts are made available. Where a license is 
        attached to the <code>&lt;project&gt;</code> element this defines the 
        default licenses for all artifacts in the project. Where a license is 
        attached to an <code>&lt;artifact&gt;</code> element this signifies 
        that the specific artifact is covered by the 
        <code>&lt;license&gt;</code> elements defined within that 
        <code>&lt;artifact&gt;</code> element. Licenses are identified using 
        the <a href="http://spdx.org">SPDX</a> identifiers
     </xs:documentation>
    </xs:annotation>
    <xs:complexType>
      <xs:attribute name="spdx" 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>&lt;artifacts&gt;</code> element is a container for 
        details of artifacts. When the <code>&lt;artifacts&gt;</code> attribute
        is missing, then the artifacts listed are not platform specific.
        The <code>&lt;artifacts&gt;</code> must be unique with respect to their
        <code>&lt;platformId&gt;</code>, i.e. it cannot be repeated.
        If the <code>&lt;project&gt;</code> element has a 
        <code>&lt;platformId&gt;</code> then there must be only one
        <code>&lt;artifacts&gt;</code> element and it must have the matching
        <code>&lt;platformId&gt;</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>&lt;artifact&gt;</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>&lt;component&gt;</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>&lt;provides&gt;</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
          </li>
        </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>&lt;requires&gt;</code> element represents a hard dependency
        on another project's artifact. If the <code>&lt;version&gt;</code>
        attribute is missing then this indicates that the dependency is
        a virtual dependency, and there must be no child elements.
        The <code>&lt;modelVersion&gt;</code> attribute must only be present
        if the dependent project's <code>&lt;modelVersion&gt;</code> is newer
        than the <code>&lt;modelVersion&gt;</code> specified on the root 
        <code>&lt;project&gt;</code> element. The presence of this element
        indicates that the child information was the result of an XSLT
        transformation of a newer <code>&lt;modelVersion&gt;</code> and
        indicates that a build tool understanding the newer 
        <code>&lt;modelVersion&gt;</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>&lt;supports&gt;</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>