Repository: ant-ivy Updated Branches: refs/heads/master 0dbae4197 -> 4d1551a5d
IVY-1540 Fix infinite loop in dependencytree, caused to due circular dependencies (coming in via Maven module descriptors) Project: http://git-wip-us.apache.org/repos/asf/ant-ivy/repo Commit: http://git-wip-us.apache.org/repos/asf/ant-ivy/commit/2f757454 Tree: http://git-wip-us.apache.org/repos/asf/ant-ivy/tree/2f757454 Diff: http://git-wip-us.apache.org/repos/asf/ant-ivy/diff/2f757454 Branch: refs/heads/master Commit: 2f757454dc41f4d130234aea68b348da4f12a0fa Parents: 5601c44 Author: Jaikiran Pai <[email protected]> Authored: Tue May 23 13:45:39 2017 +0530 Committer: Jaikiran Pai <[email protected]> Committed: Tue May 30 09:48:48 2017 +0530 ---------------------------------------------------------------------- .../org/apache/ivy/ant/IvyDependencyTree.java | 73 ++++++++++++-------- .../apache/ivy/ant/IvyDependencyTreeTest.java | 30 +++++++- .../1/org/foo-bar/ivys/ivy-1.2.3.xml | 28 ++++++++ .../1/org/foo-bar/jars/foo-bar-1.2.3.jar | 0 test/repositories/1/org/mod1/ivys/ivy-2.0.xml | 27 ++++++++ test/repositories/1/org/mod1/jars/mod1-2.0.jar | 0 .../m2/org/circular/module1/1.0/module1-1.0.jar | 1 + .../m2/org/circular/module1/1.0/module1-1.0.pom | 35 ++++++++++ .../m2/org/circular/module2/2.0/module2-2.0.jar | 1 + .../m2/org/circular/module2/2.0/module2-2.0.pom | 35 ++++++++++ 10 files changed, 201 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/src/java/org/apache/ivy/ant/IvyDependencyTree.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/ivy/ant/IvyDependencyTree.java b/src/java/org/apache/ivy/ant/IvyDependencyTree.java index b95aa91..5df9548 100644 --- a/src/java/org/apache/ivy/ant/IvyDependencyTree.java +++ b/src/java/org/apache/ivy/ant/IvyDependencyTree.java @@ -17,12 +17,6 @@ */ package org.apache.ivy.ant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - import org.apache.ivy.core.module.id.ModuleRevisionId; import org.apache.ivy.core.report.ResolveReport; import org.apache.ivy.core.resolve.IvyNode; @@ -30,18 +24,26 @@ import org.apache.ivy.core.resolve.IvyNodeCallers.Caller; import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData; import org.apache.tools.ant.BuildException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + public class IvyDependencyTree extends IvyPostResolveTask { - private Map/* <ModuleRevisionId, List<IvyNode>> */dependencies = new HashMap/* - * <ModuleRevisionId, - * List<IvyNode>> - */(); + private final Map<ModuleRevisionId, List<IvyNode>> dependencies = new HashMap<ModuleRevisionId, List<IvyNode>>(); private boolean showEvicted = false; public void doExecute() throws BuildException { prepareAndCheck(); ResolveReport report = getResolvedReport(); + if (report == null) { + throw new BuildException("No resolution report was available to run the post-resolve task. Make sure resolve was done before this task"); + } log("Dependency tree for " + report.getResolveId()); ModuleRevisionId mrid = report.getModuleDescriptor().getModuleRevisionId(); // make dependency tree easier to fetch information @@ -49,20 +51,26 @@ public class IvyDependencyTree extends IvyPostResolveTask { IvyNode dependency = (IvyNode) iterator.next(); populateDependencyTree(dependency, mrid, report); } - List dependencyList = (List) dependencies.get(mrid); + final List<IvyNode> dependencyList = dependencies.get(mrid); if (dependencyList != null) { - printDependencies(dependencyList, 0); + printDependencies(mrid, dependencyList, 0, new HashSet<ModuleRevisionId>()); } } - private void printDependencies(List/* <IvyNode> */dependencyList, int indent) { - for (Iterator iterator = dependencyList.iterator(); iterator.hasNext();) { - IvyNode dependency = (IvyNode) iterator.next(); - boolean evicted = dependency.isEvicted(getConf()); + private void printDependencies(final ModuleRevisionId mrid, final List<IvyNode> dependencyList, final int indent, + final Set<ModuleRevisionId> ancestors) { + for (final Iterator iterator = dependencyList.iterator(); iterator.hasNext();) { + final Set<ModuleRevisionId> ancestorsForCurrentDep = new HashSet<ModuleRevisionId>(ancestors); + // previous ancestors plus the module to whom these dependencies belong to + ancestorsForCurrentDep.add(mrid); + final IvyNode dependency = (IvyNode) iterator.next(); + final ModuleRevisionId dependencyMrid = dependency.getId(); + final boolean circular = ancestorsForCurrentDep.contains(dependencyMrid); + final boolean evicted = dependency.isEvicted(getConf()); + final StringBuilder sb = new StringBuilder(); if (evicted && !showEvicted) { continue; } - StringBuffer sb = new StringBuffer(); if (indent > 0) { for (int i = 0; i < indent; i++) { if (i == indent - 1 && !iterator.hasNext() && !hasDependencies(dependency)) { @@ -78,7 +86,15 @@ public class IvyDependencyTree extends IvyPostResolveTask { } else { sb.append("\\- "); } - sb.append(dependency.getId().toString()); + if (!evicted && circular) { + // log and skip processing the (transitive) dependencies of this dependency + sb.append("(circularly depends on) ").append(dependencyMrid); + log(sb.toString()); + continue; + + } else { + sb.append(dependencyMrid.toString()); + } if (evicted && showEvicted) { EvictionData evictedData = dependency.getEvictedData(getConf()); if (evictedData.isTransitivelyEvicted()) { @@ -94,13 +110,16 @@ public class IvyDependencyTree extends IvyPostResolveTask { } log(sb.toString()); - printDependencies((List) dependencies.get(dependency.getId()), indent + 1); + printDependencies(dependencyMrid, dependencies.get(dependencyMrid), indent + 1, ancestorsForCurrentDep); } } - private boolean hasDependencies(IvyNode dependency) { - List dependencyList = (List) dependencies.get(dependency.getId()); - return dependencyList.size() > 0; + private boolean hasDependencies(final IvyNode module) { + if (module == null) { + return false; + } + final List<IvyNode> dependenciesForModule = dependencies.get(module.getId()); + return dependenciesForModule != null && !dependenciesForModule.isEmpty(); } private void populateDependencyTree(IvyNode dependency, ModuleRevisionId currentMrid, @@ -112,16 +131,16 @@ public class IvyDependencyTree extends IvyPostResolveTask { } } - private void registerNodeIfNecessary(ModuleRevisionId moduleRevisionId) { + private void registerNodeIfNecessary(final ModuleRevisionId moduleRevisionId) { if (!dependencies.containsKey(moduleRevisionId)) { - dependencies.put(moduleRevisionId, new ArrayList/* <IvyNode> */()); + dependencies.put(moduleRevisionId, new ArrayList<IvyNode>()); } } - private void addDependency(ModuleRevisionId moduleRevisionId, IvyNode dependency) { + private void addDependency(final ModuleRevisionId moduleRevisionId, final IvyNode dependency) { registerNodeIfNecessary(moduleRevisionId); - List/* <IvyNode> */list = (List) dependencies.get(moduleRevisionId); - list.add(dependency); + final List<IvyNode> dependencyList = dependencies.get(moduleRevisionId); + dependencyList.add(dependency); } public boolean isShowEvicted() { http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/java/org/apache/ivy/ant/IvyDependencyTreeTest.java ---------------------------------------------------------------------- diff --git a/test/java/org/apache/ivy/ant/IvyDependencyTreeTest.java b/test/java/org/apache/ivy/ant/IvyDependencyTreeTest.java index 23c1ed4..14437e9 100644 --- a/test/java/org/apache/ivy/ant/IvyDependencyTreeTest.java +++ b/test/java/org/apache/ivy/ant/IvyDependencyTreeTest.java @@ -17,8 +17,6 @@ */ package org.apache.ivy.ant; -import java.io.File; - import org.apache.ivy.TestHelper; import org.apache.ivy.ant.testutil.AntTaskTestCase; import org.apache.tools.ant.BuildException; @@ -27,6 +25,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.io.File; + + public class IvyDependencyTreeTest extends AntTaskTestCase { private IvyDependencyTree dependencyTree; @@ -117,4 +118,29 @@ public class IvyDependencyTreeTest extends AntTaskTestCase { assertLogContaining("\\- org1#mod1.2;2.2"); } + + /** + * Tests that dependency tree task doesn't run into an infinite loop due to circular dependencies + * + * @throws Exception + * @see <a href="https://issues.apache.org/jira/browse/IVY-1540">IVY-1540</a> for more details + */ + @Test + public void testCircularDep() throws Exception { + final String resolveId = "circular-dep-tree"; + // resolve + final IvyResolve ivyResolve = new IvyResolve(); + ivyResolve.setProject(project); + ivyResolve.setResolveId(resolveId); + ivyResolve.setFile(new File("test/repositories/1/org/foo-bar/ivys/ivy-1.2.3.xml")); + ivyResolve.execute(); + // use the resolveid to fetch the dependency tree from that previous resolution + dependencyTree.setResolveId(resolveId); + dependencyTree.execute(); + // check the logged message + assertLogContaining("Dependency tree for " + resolveId); + assertLogContaining("(circularly depends on) " + "org.circular#module1;1.0"); + assertLogNotContaining("(circularly depends on) " + "org.circular#module2;2.0"); + } + } http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/1/org/foo-bar/ivys/ivy-1.2.3.xml ---------------------------------------------------------------------- diff --git a/test/repositories/1/org/foo-bar/ivys/ivy-1.2.3.xml b/test/repositories/1/org/foo-bar/ivys/ivy-1.2.3.xml new file mode 100644 index 0000000..ab09e3e --- /dev/null +++ b/test/repositories/1/org/foo-bar/ivys/ivy-1.2.3.xml @@ -0,0 +1,28 @@ +<!-- + 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. +--> +<ivy-module version="1.0"> + <info organisation="org" + module="foo-bar" + revision="1.2.3" + /> + <dependencies> + <dependency org="org.circular" name="module1" rev="1.0"/> + <dependency org="org" name="mod1" rev="2.0" /> + </dependencies> +</ivy-module> http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/1/org/foo-bar/jars/foo-bar-1.2.3.jar ---------------------------------------------------------------------- diff --git a/test/repositories/1/org/foo-bar/jars/foo-bar-1.2.3.jar b/test/repositories/1/org/foo-bar/jars/foo-bar-1.2.3.jar new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/1/org/mod1/ivys/ivy-2.0.xml ---------------------------------------------------------------------- diff --git a/test/repositories/1/org/mod1/ivys/ivy-2.0.xml b/test/repositories/1/org/mod1/ivys/ivy-2.0.xml new file mode 100644 index 0000000..27a8cb4 --- /dev/null +++ b/test/repositories/1/org/mod1/ivys/ivy-2.0.xml @@ -0,0 +1,27 @@ +<!-- + 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. +--> +<ivy-module version="1.0"> + <info organisation="org" + module="mod1" + revision="2.0" + /> + <dependencies> + <dependency org="org" name="foo-bar" rev="1.2.3" /> + </dependencies> +</ivy-module> http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/1/org/mod1/jars/mod1-2.0.jar ---------------------------------------------------------------------- diff --git a/test/repositories/1/org/mod1/jars/mod1-2.0.jar b/test/repositories/1/org/mod1/jars/mod1-2.0.jar new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/m2/org/circular/module1/1.0/module1-1.0.jar ---------------------------------------------------------------------- diff --git a/test/repositories/m2/org/circular/module1/1.0/module1-1.0.jar b/test/repositories/m2/org/circular/module1/1.0/module1-1.0.jar new file mode 100644 index 0000000..56f3b36 --- /dev/null +++ b/test/repositories/m2/org/circular/module1/1.0/module1-1.0.jar @@ -0,0 +1 @@ + http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/m2/org/circular/module1/1.0/module1-1.0.pom ---------------------------------------------------------------------- diff --git a/test/repositories/m2/org/circular/module1/1.0/module1-1.0.pom b/test/repositories/m2/org/circular/module1/1.0/module1-1.0.pom new file mode 100644 index 0000000..4d7c8ff --- /dev/null +++ b/test/repositories/m2/org/circular/module1/1.0/module1-1.0.pom @@ -0,0 +1,35 @@ +<?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 + + 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. +--> +<project> + <modelVersion>4.0.0</modelVersion> + <groupId>org.circular</groupId> + <artifactId>module1</artifactId> + <name>Test Module for Ivy tests</name> + <version>1.0</version> + + <dependencies> + <dependency> + <groupId>org.circular</groupId> + <artifactId>module2</artifactId> + <version>2.0</version> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/m2/org/circular/module2/2.0/module2-2.0.jar ---------------------------------------------------------------------- diff --git a/test/repositories/m2/org/circular/module2/2.0/module2-2.0.jar b/test/repositories/m2/org/circular/module2/2.0/module2-2.0.jar new file mode 100644 index 0000000..56f3b36 --- /dev/null +++ b/test/repositories/m2/org/circular/module2/2.0/module2-2.0.jar @@ -0,0 +1 @@ + http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/2f757454/test/repositories/m2/org/circular/module2/2.0/module2-2.0.pom ---------------------------------------------------------------------- diff --git a/test/repositories/m2/org/circular/module2/2.0/module2-2.0.pom b/test/repositories/m2/org/circular/module2/2.0/module2-2.0.pom new file mode 100644 index 0000000..fd751b6 --- /dev/null +++ b/test/repositories/m2/org/circular/module2/2.0/module2-2.0.pom @@ -0,0 +1,35 @@ +<?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 + + 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. +--> +<project> + <modelVersion>4.0.0</modelVersion> + <groupId>org.circular</groupId> + <artifactId>module2</artifactId> + <name>Test Module for Ivy tests</name> + <version>2.0</version> + + <dependencies> + <dependency> + <groupId>org.circular</groupId> + <artifactId>module1</artifactId> + <version>1.0</version> + </dependency> + </dependencies> + +</project>
