diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java index 22127c5ae48a..4f53a7bf6cfc 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java @@ -59,7 +59,11 @@ import org.eclipse.aether.util.filter.ScopeDependencyFilter; import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; import org.eclipse.aether.util.graph.selector.AndDependencySelector; +import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer; import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.VersionRange; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,16 +80,33 @@ public class DefaultPluginDependenciesResolver implements PluginDependenciesResolver { + private static final String MAVENCORE_COMPATIBILITY_VERSIONS = "(,)"; + private static final String REPOSITORY_CONTEXT = "plugin"; private final Logger logger = LoggerFactory.getLogger( getClass() ); private final RepositorySystem repoSystem; + private final VersionRange mavenCompatibilityVersionRange; + @Inject public DefaultPluginDependenciesResolver( RepositorySystem repoSystem ) { this.repoSystem = repoSystem; + + try + { + // o.a.m.plugins:maven-compiler-plugin:jar:3.1 depends on o.a.m:maven-toolchain:jar:1.0 + // maven-its:mng-4666 depends on o.a.m:maven-model:0.1-stub + this.mavenCompatibilityVersionRange = + new GenericVersionScheme().parseVersionRange( MAVENCORE_COMPATIBILITY_VERSIONS ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RuntimeException( e ); + } + } private Artifact toArtifact( Plugin plugin, RepositorySystemSession session ) @@ -182,7 +203,10 @@ private DependencyNode resolveInternal( Plugin plugin, Artifact pluginArtifact, DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session ); pluginSession.setDependencySelector( selector ); - pluginSession.setDependencyGraphTransformer( session.getDependencyGraphTransformer() ); + pluginSession.setDependencyGraphTransformer( + ChainedDependencyGraphTransformer.newInstance( + session.getDependencyGraphTransformer(), + new MavenCompatibilityChecker( mavenCompatibilityVersionRange ) ) ); CollectRequest request = new CollectRequest(); request.setRequestContext( REPOSITORY_CONTEXT ); diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/IncompatibleDependencyException.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/IncompatibleDependencyException.java new file mode 100644 index 000000000000..6a1d07427db2 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/IncompatibleDependencyException.java @@ -0,0 +1,37 @@ +package org.apache.maven.plugin.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.eclipse.aether.graph.DependencyNode; + +class IncompatibleDependencyException extends Exception +{ + private final DependencyNode node; + + IncompatibleDependencyException( DependencyNode node ) + { + this.node = node; + } + + DependencyNode getNode() + { + return node; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenCompatibilityChecker.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenCompatibilityChecker.java new file mode 100644 index 000000000000..44b3c5ac99b3 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenCompatibilityChecker.java @@ -0,0 +1,80 @@ +package org.apache.maven.plugin.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.collection.DependencyGraphTransformationContext; +import org.eclipse.aether.collection.DependencyGraphTransformer; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.version.VersionRange; + +/** + * Ensure that Maven core dependencies fit within the versionRange + * + * @since 4.0.0 + */ +class MavenCompatibilityChecker implements DependencyGraphTransformer +{ + private final VersionRange versionRange; + + MavenCompatibilityChecker( VersionRange versionRange ) + { + this.versionRange = versionRange; + } + + @Override + public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context ) + throws RepositoryException + { + try + { + validateNode( node ); + } + catch ( IncompatibleDependencyException e ) + { + throw new RepositoryException( + String.format( "%s depends on %s, which does not match the required Maven versionrange of %s", + node.getArtifact(), e.getNode().getArtifact(), versionRange ) ); + } + + return node; + } + + private void validateNode( DependencyNode node ) + throws IncompatibleDependencyException + { + if ( isCoreArtifact( node.getArtifact() ) && !versionRange.containsVersion( node.getVersion() ) ) + { + throw new IncompatibleDependencyException( node ); + } + + for ( DependencyNode child : node.getChildren() ) + { + validateNode( child ); + } + } + + private static boolean isCoreArtifact( Artifact artifact ) + { + return artifact.getArtifactId().startsWith( "maven-" ) + && artifact.getGroupId().equals( "org.apache.maven" ); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenCompatibilityCheckerTest.java b/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenCompatibilityCheckerTest.java new file mode 100644 index 000000000000..5b4bc29d903f --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenCompatibilityCheckerTest.java @@ -0,0 +1,85 @@ +package org.apache.maven.plugin.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Collections; + +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.DefaultDependencyNode; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.VersionScheme; +import org.junit.jupiter.api.Test; + +class MavenCompatibilityCheckerTest +{ + private final VersionScheme versionScheme = new GenericVersionScheme(); + + @Test + void compatible() throws Exception + { + MavenCompatibilityChecker checker = + new MavenCompatibilityChecker( versionScheme.parseVersionRange( "[3.0,)" ) ); + + Artifact pluginArtifact = new DefaultArtifact( "o.a.m.p:plugin:1.0" ); + Dependency plugin = new Dependency( pluginArtifact, "compile" ); + DefaultDependencyNode node = new DefaultDependencyNode( plugin ); + node.setVersion( versionScheme.parseVersion( "1.0" ) ); + + Artifact coreArtifact = new DefaultArtifact( "org.apache.maven:maven-core:3.0" ); + Dependency core = new Dependency( coreArtifact, "compile" ); + DefaultDependencyNode coreNode = new DefaultDependencyNode( core ); + coreNode.setVersion( versionScheme.parseVersion( "3.0" ) ); + + node.setChildren(Collections.singletonList( coreNode ) ); + + assertThat( checker.transformGraph( node, null ), is(node)); + } + + @Test + void incompatible() throws Exception + { + MavenCompatibilityChecker checker = + new MavenCompatibilityChecker( versionScheme.parseVersionRange( "[3.0,)" ) ); + + Artifact pluginArtifact = new DefaultArtifact( "o.a.m.p:plugin:1.0" ); + Dependency plugin = new Dependency( pluginArtifact, "compile" ); + DefaultDependencyNode node = new DefaultDependencyNode( plugin ); + node.setVersion( versionScheme.parseVersion( "1.0" ) ); + + Artifact coreArtifact = new DefaultArtifact( "org.apache.maven:maven-core:2.0" ); + Dependency core = new Dependency( coreArtifact, "compile" ); + DefaultDependencyNode coreNode = new DefaultDependencyNode( core ); + coreNode.setVersion( versionScheme.parseVersion( "2.0" ) ); + + node.setChildren(Collections.singletonList( coreNode ) ); + + RepositoryException exception = assertThrows( RepositoryException.class, () -> checker.transformGraph( node, null ) ); + assertThat( exception.getMessage(), is( "o.a.m.p:plugin:jar:1.0 depends on org.apache.maven:maven-core:jar:2.0, " + + "which does not match the required Maven versionrange of [3.0,)" ) ); + } + +}