This is an automated email from the ASF dual-hosted git repository. bodewig pushed a commit to branch ivy-1642 in repository https://gitbox.apache.org/repos/asf/ant-ivy.git
commit 6d28c2cfb662224c374fec21f2a2233caea667e0 Author: Stefan Bodewig <bode...@apache.org> AuthorDate: Fri Apr 14 07:05:28 2023 +0200 add the "default" artifact explicitly for dependency if POM contains more than one https://issues.apache.org/jira/browse/IVY-1642 --- asciidoc/release-notes.adoc | 5 +- .../org/apache/ivy/core/resolve/IvyNodeUsage.java | 50 +++------------- .../parser/m2/PomModuleDescriptorBuilder.java | 20 ++++++- .../parser/m2/PomModuleDescriptorParserTest.java | 60 +++++++++++++++++++ ...st-dependencies-with-and-without-classifier.pom | 67 ++++++++++++++++++++++ 5 files changed, 157 insertions(+), 45 deletions(-) diff --git a/asciidoc/release-notes.adoc b/asciidoc/release-notes.adoc index 84379627..c04489ab 100644 --- a/asciidoc/release-notes.adoc +++ b/asciidoc/release-notes.adoc @@ -36,7 +36,8 @@ More information about the project can be found on the website link:https://ant. Key features of this 2.5.2 release are: -- FIX: ivy:retrieve could fail because of a `NullPointerException` (jira:IVY-1641[]) +- FIX: reading POMs may loose dependencies when multiple Maven + dependencies only differ in `classifier` (jira:IVY-1642[]) == List of Changes in this Release @@ -53,6 +54,8 @@ For details about the following changes, check our JIRA install at link:https:// //// - FIX: ivy:retrieve could fail because of a `NullPointerException` (jira:IVY-1641[]) +- FIX: reading POMs may loose dependencies when multiple Maven + dependencies only differ in `classifier` (jira:IVY-1642[]) == Committers and Contributors diff --git a/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java b/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java index f96beb91..c9676dcc 100644 --- a/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java +++ b/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java @@ -24,15 +24,10 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.apache.ivy.core.module.descriptor.DefaultIncludeRule; import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.IncludeRule; import org.apache.ivy.core.module.descriptor.WorkspaceModuleDescriptor; -import org.apache.ivy.core.module.id.ArtifactId; -import org.apache.ivy.core.module.id.ModuleId; -import org.apache.ivy.plugins.matcher.ExactPatternMatcher; -import org.apache.ivy.plugins.matcher.PatternMatcher; /** * Class collecting usage data for an IvyNode. @@ -243,35 +238,15 @@ public class IvyNodeUsage { if (dependersInConf == null) { return null; } - final Set<IncludeRule> dependencyIncludes = new HashSet<>(); - // true if the depedency descriptor of any of the depender *doesn't* have an explicit - // "<artifact>" or an "<include>". false otherwise - boolean atLeastOneDependerNeedsAllArtifacts = false; - // true if the dependency descriptor of any of the depender either has an explicit "<artifact>" - // or an "<include>". false otherwise - boolean atLeastOneDependerHasSpecificArtifactSelection = false; - for (final Depender depender : dependersInConf) { - final DependencyArtifactDescriptor dads[] = depender.dd.getDependencyArtifacts(depender.dd.getModuleConfigurations()); - final boolean declaresArtifacts = dads != null && dads.length > 0; - final IncludeRule[] rules = depender.dd.getIncludeRules(depender.dependerConf); - final boolean hasIncludeRule = rules != null && rules.length > 0; - if (hasIncludeRule) { - dependencyIncludes.addAll(Arrays.asList(rules)); - } - if (declaresArtifacts || hasIncludeRule) { - atLeastOneDependerHasSpecificArtifactSelection = true; - } - if (!hasIncludeRule && !declaresArtifacts) { - atLeastOneDependerNeedsAllArtifacts = true; + Set<IncludeRule> dependencyIncludes = new HashSet<>(); + for (Depender depender : dependersInConf) { + IncludeRule[] rules = depender.dd.getIncludeRules(depender.dependerConf); + if (rules == null || rules.length == 0) { + // no include rule in at least one depender -> we must include everything, + // and so return no include rule at all + return null; } - } - // so there's at least one depender D1 which has a specific artifact dependency and at the - // same time there's a depender D2 which doesn't have any explicit artifact/includes. - // so it is expected that an implicit "include all artifacts" is applied so that dependencies - // such as D2 get (all) the artifacts that are published by the dependency's module - if (atLeastOneDependerHasSpecificArtifactSelection && atLeastOneDependerNeedsAllArtifacts) { - // add a "include all artifacts" rule - dependencyIncludes.add(includeAllArtifacts()); + dependencyIncludes.addAll(Arrays.asList(rules)); } return dependencyIncludes; } @@ -338,13 +313,4 @@ public class IvyNodeUsage { return false; } - private static IncludeRule includeAllArtifacts() { - final ArtifactId aid = new ArtifactId( - new ModuleId(PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION), - PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION, - PatternMatcher.ANY_EXPRESSION); - return new DefaultIncludeRule(aid, ExactPatternMatcher.INSTANCE, null); - } - - } diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java index 87690f00..ba28af4e 100644 --- a/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java +++ b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java @@ -314,7 +314,12 @@ public class PomModuleDescriptorBuilder { ConfMapper mapping = MAVEN2_CONF_MAPPING.get(scope); mapping.addMappingConfs(dd, dep.isOptional()); Map<String, String> extraAtt = new HashMap<>(); - if (dep.getClassifier() != null || dep.getType() != null && !"jar".equals(dep.getType())) { + final String optionalizedScope = dep.isOptional() ? "optional" : scope; + if (isNonDefaultArtifact(dep)) { + if (existing != null && existing.getAllDependencyArtifacts().length == 0) { + // previously added dependency has been the "default artifact" + dd.addDependencyArtifact(optionalizedScope, createDefaultArtifact(dd)); + } String type = "jar"; if (dep.getType() != null) { type = dep.getType(); @@ -339,9 +344,11 @@ public class PomModuleDescriptorBuilder { dd, dd.getDependencyId().getName(), type, ext, null, extraAtt); // here we have to assume a type and ext for the artifact, so this is a limitation // compared to how m2 behave with classifiers - final String optionalizedScope = dep.isOptional() ? "optional" : scope; depArtifact.addConfiguration(optionalizedScope); dd.addDependencyArtifact(optionalizedScope, depArtifact); + } else if (existing != null) { + // this is the "default" artifact and some non-default artifact has already been added + dd.addDependencyArtifact(optionalizedScope, createDefaultArtifact(dd)); } for (ModuleId excludedModule : excluded) { @@ -362,6 +369,15 @@ public class PomModuleDescriptorBuilder { } } + private boolean isNonDefaultArtifact(PomDependencyData dep) { + return dep.getClassifier() != null || dep.getType() != null && !"jar".equals(dep.getType()); + } + + private DefaultDependencyArtifactDescriptor createDefaultArtifact(DefaultDependencyDescriptor dd) { + return new DefaultDependencyArtifactDescriptor(dd, dd.getDependencyId().getName(), + "jar", "jar", null, null); + } + private static boolean shouldExcludeAllTransitiveDeps(final List<ModuleId> exclusions) { if (exclusions == null || exclusions.isEmpty()) { return false; diff --git a/test/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParserTest.java b/test/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParserTest.java index 100fad18..310b21f5 100644 --- a/test/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParserTest.java +++ b/test/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParserTest.java @@ -343,6 +343,66 @@ public class PomModuleDescriptorParserTest extends AbstractModuleDescriptorParse assertEquals(extraAtt, dds[0].getAllDependencyArtifacts()[0].getExtraAttributes()); } + /** + * @see "https://issues.apache.org/jira/browse/IVY-1642" + */ + @Test + public void testDependenciesWithAndWithoutClassifier() throws Exception { + ModuleDescriptor md = PomModuleDescriptorParser.getInstance().parseDescriptor(settings, + getClass().getResource("test-dependencies-with-and-without-classifier.pom"), true); + assertNotNull(md); + + assertEquals(ModuleRevisionId.newInstance("org.apache", "test", "1.0"), + md.getModuleRevisionId()); + + DependencyDescriptor[] dds = md.getDependencies(); + assertNotNull(dds); + assertEquals(3, dds.length); + assertEquals(ModuleRevisionId.newInstance("commons-logging", "commons-logging", "1.0.4"), + dds[0].getDependencyRevisionId()); + Map<String, String> extraAtt = Collections.singletonMap("classifier", "asl"); + assertEquals(2, dds[0].getAllDependencyArtifacts().length); + assertEquals(Collections.emptyMap(), dds[0].getAllDependencyArtifacts()[0].getExtraAttributes()); + assertEquals(extraAtt, dds[0].getAllDependencyArtifacts()[1].getExtraAttributes()); + + assertEquals(ModuleRevisionId.newInstance("commons-logging", "commons-logging2", "1.0.4"), + dds[1].getDependencyRevisionId()); + + assertEquals(2, dds[1].getAllDependencyArtifacts().length); + assertEquals(Collections.emptyMap(), dds[1].getAllDependencyArtifacts()[1].getExtraAttributes()); + assertEquals(extraAtt, dds[1].getAllDependencyArtifacts()[0].getExtraAttributes()); + + assertEquals(ModuleRevisionId.newInstance("commons-logging", "commons-logging3", "1.0.4"), + dds[2].getDependencyRevisionId()); + assertEquals(2, dds[2].getAllDependencyArtifacts().length); + assertEquals(extraAtt, dds[2].getAllDependencyArtifacts()[0].getExtraAttributes()); + assertEquals(Collections.singletonMap("classifier", "foo"), + dds[2].getAllDependencyArtifacts()[1].getExtraAttributes()); + + // now we verify the conversion to an Ivy file + PomModuleDescriptorParser.getInstance().toIvyFile( + getClass().getResource("test-dependencies-with-and-without-classifier.pom").openStream(), + new URLResource(getClass().getResource("test-dependencies-with-and-without-classifier.pom")), dest, + md); + + assertTrue(dest.exists()); + + // the converted Ivy file should be parsable with validate=true + ModuleDescriptor md2 = XmlModuleDescriptorParser.getInstance().parseDescriptor( + new IvySettings(), dest.toURI().toURL(), true); + + // and the parsed module descriptor should be similar to the original + assertNotNull(md2); + assertEquals(md.getModuleRevisionId(), md2.getModuleRevisionId()); + dds = md2.getDependencies(); + assertEquals(3, dds.length); + for (int i = 0; i < dds.length; i++) { + assertEquals(2, dds[i].getAllDependencyArtifacts().length); + int withExt = i == 0 ? 1: 0; + assertEquals(extraAtt, dds[i].getAllDependencyArtifacts()[withExt].getExtraAttributes()); + } + } + @Test public void testDependenciesWithType() throws Exception { ModuleDescriptor md = PomModuleDescriptorParser.getInstance().parseDescriptor(settings, diff --git a/test/java/org/apache/ivy/plugins/parser/m2/test-dependencies-with-and-without-classifier.pom b/test/java/org/apache/ivy/plugins/parser/m2/test-dependencies-with-and-without-classifier.pom new file mode 100644 index 00000000..dce9c431 --- /dev/null +++ b/test/java/org/apache/ivy/plugins/parser/m2/test-dependencies-with-and-without-classifier.pom @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<!-- + 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 + + https://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. +--> +<project> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache</groupId> + <artifactId>test</artifactId> + <name>Test Module for Ivy M2 parsing</name> + <version>1.0</version> + <url>http://ant.apache.org/ivy</url> + <organization> + <name>Apache</name> + <url>http://ant.apache.org/ivy</url> + </organization> + <dependencies> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.0.4</version> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.0.4</version> + <classifier>asl</classifier> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging2</artifactId> + <version>1.0.4</version> + <classifier>asl</classifier> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging2</artifactId> + <version>1.0.4</version> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging3</artifactId> + <version>1.0.4</version> + <classifier>asl</classifier> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging3</artifactId> + <version>1.0.4</version> + <classifier>foo</classifier> + </dependency> + </dependencies> +</project>