This is an automated email from the ASF dual-hosted git repository.
bodewig pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant-ivy.git
The following commit(s) were added to refs/heads/master by this push:
new 5f11f05f IVY-1642 fix POM translation for multiple artifacts in one
module
5f11f05f is described below
commit 5f11f05fe38b2fc3779d5bec6287505798bc701c
Author: Stefan Bodewig <[email protected]>
AuthorDate: Fri Apr 14 07:05:28 2023 +0200
IVY-1642 fix POM translation for multiple artifacts in one module
---
asciidoc/release-notes.adoc | 5 +-
src/java/org/apache/ivy/core/resolve/IvyNode.java | 6 ++
.../org/apache/ivy/core/resolve/IvyNodeUsage.java | 50 ++----------
.../parser/m2/PomModuleDescriptorBuilder.java | 24 +++++-
.../parser/m2/PomModuleDescriptorParserTest.java | 89 +++++++++++++++++++++
...st-dependencies-with-and-without-classifier.pom | 91 ++++++++++++++++++++++
6 files changed, 220 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/IvyNode.java
b/src/java/org/apache/ivy/core/resolve/IvyNode.java
index 273488ef..c7872c89 100644
--- a/src/java/org/apache/ivy/core/resolve/IvyNode.java
+++ b/src/java/org/apache/ivy/core/resolve/IvyNode.java
@@ -49,6 +49,7 @@ import org.apache.ivy.core.module.descriptor.IncludeRule;
import org.apache.ivy.core.module.descriptor.MDArtifact;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.resolve.IvyNodeCallers.Caller;
@@ -873,6 +874,7 @@ public class IvyNode implements Comparable<IvyNode> {
// now exclude artifacts that aren't accepted by any caller
Iterator<Artifact> iter = artifacts.iterator();
+ Set<ArtifactRevisionId> artifactRevisionsSeen = new
HashSet<ArtifactRevisionId>();
while (iter.hasNext()) {
Artifact artifact = iter.next();
boolean excluded = callers.doesCallersExclude(rootModuleConf,
artifact);
@@ -880,6 +882,10 @@ public class IvyNode implements Comparable<IvyNode> {
Message.debug(this + " in " + rootModuleConf + ": excluding "
+ artifact);
iter.remove();
}
+ if (!artifactRevisionsSeen.add(artifact.getId())) {
+ Message.debug(this + " in " + rootModuleConf + ": skipping
duplicate " + artifact);
+ iter.remove();
+ }
}
return artifacts.toArray(new Artifact[artifacts.size()]);
}
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..3c6eff88 100644
--- a/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java
@@ -305,6 +305,8 @@ public class PomModuleDescriptorBuilder {
// the same dependency mrid could appear twice in the module
descriptor,
// so we check if we already have created a dependency descriptor for
the dependency mrid
final DependencyDescriptor existing =
this.ivyModuleDescriptor.depDescriptors.get(moduleRevId);
+ final String[] existingConfigurations = existing == null ? new
String[0]
+ : existing.getModuleConfigurations();
final DefaultDependencyDescriptor dd = (existing != null && existing
instanceof DefaultDependencyDescriptor)
? (DefaultDependencyDescriptor) existing
: new PomDependencyDescriptor(dep, ivyModuleDescriptor,
moduleRevId, !excludeAllTransitiveDeps);
@@ -314,7 +316,14 @@ 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) {
+ String moduleConfiguration = existingConfigurations.length == 1
+ ? existingConfigurations[0] : optionalizedScope;
+ // previously added dependency has been the "default artifact"
+ dd.addDependencyArtifact(moduleConfiguration,
createDefaultArtifact(dd));
+ }
String type = "jar";
if (dep.getType() != null) {
type = dep.getType();
@@ -339,9 +348,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 +373,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..a9673cc0 100644
---
a/test/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParserTest.java
+++
b/test/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParserTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import java.io.File;
@@ -343,6 +344,94 @@ 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(5, dds.length);
+ Map<String, String> extraAtt = Collections.singletonMap("classifier",
"asl");
+ assertEquals(ModuleRevisionId.newInstance("commons-logging",
"commons-logging", "1.0.4"),
+ dds[0].getDependencyRevisionId());
+ DependencyArtifactDescriptor[] dads =
dds[0].getAllDependencyArtifacts();
+ assertEquals(2, dads.length);
+ assertEquals(Collections.emptyMap(), dads[0].getExtraAttributes());
+ assertEquals(extraAtt, dads[1].getExtraAttributes());
+
+ assertEquals(ModuleRevisionId.newInstance("commons-logging",
"commons-logging2", "1.0.4"),
+ dds[1].getDependencyRevisionId());
+ dads = dds[1].getAllDependencyArtifacts();
+ assertEquals(2, dads.length);
+ assertEquals(Collections.emptyMap(), dads[1].getExtraAttributes());
+ assertEquals(extraAtt, dads[0].getExtraAttributes());
+
+ assertEquals(ModuleRevisionId.newInstance("commons-logging",
"commons-logging3", "1.0.4"),
+ dds[2].getDependencyRevisionId());
+ dads = dds[2].getAllDependencyArtifacts();
+ assertEquals(2, dads.length);
+ assertEquals(extraAtt, dads[0].getExtraAttributes());
+ assertEquals(Collections.singletonMap("classifier", "foo"),
+ dads[1].getExtraAttributes());
+
+ assertEquals(ModuleRevisionId.newInstance("commons-logging",
"commons-logging4", "1.0.4"),
+ dds[3].getDependencyRevisionId());
+ dads = dds[3].getAllDependencyArtifacts();
+ assertEquals(2, dads.length);
+ assertEquals(Collections.emptyMap(), dads[0].getExtraAttributes());
+ assertEquals(extraAtt, dads[1].getExtraAttributes());
+ DependencyArtifactDescriptor[] providedDads =
dds[3].getDependencyArtifacts("provided");
+ assertEquals(1, providedDads.length);
+ assertSame(dads[0], providedDads[0]);
+ DependencyArtifactDescriptor[] compileDads =
dds[3].getDependencyArtifacts("compile");
+ assertEquals(1, compileDads.length);
+ assertSame(dads[1], compileDads[0]);
+
+ assertEquals(ModuleRevisionId.newInstance("commons-logging",
"commons-logging5", "1.0.4"),
+ dds[4].getDependencyRevisionId());
+ dads = dds[4].getAllDependencyArtifacts();
+ assertEquals(2, dads.length);
+ assertEquals(extraAtt, dads[0].getExtraAttributes());
+ assertEquals(Collections.emptyMap(), dads[1].getExtraAttributes());
+ providedDads = dds[4].getDependencyArtifacts("provided");
+ assertEquals(1, providedDads.length);
+ assertSame(dads[1], providedDads[0]);
+ compileDads = dds[4].getDependencyArtifacts("compile");
+ assertEquals(1, compileDads.length);
+ assertSame(dads[0], compileDads[0]);
+
+ // 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(5, dds.length);
+ for (int i = 0; i < dds.length; i++) {
+ assertEquals(2, dds[i].getAllDependencyArtifacts().length);
+ int withExt = i == 0 || i == 3 ? 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..7cf2ff81
--- /dev/null
+++
b/test/java/org/apache/ivy/plugins/parser/m2/test-dependencies-with-and-without-classifier.pom
@@ -0,0 +1,91 @@
+<?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>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging4</artifactId>
+ <version>1.0.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging4</artifactId>
+ <version>1.0.4</version>
+ <classifier>asl</classifier>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging5</artifactId>
+ <version>1.0.4</version>
+ <classifier>asl</classifier>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging5</artifactId>
+ <version>1.0.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>