diff --git a/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java b/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java index 6c1c22011..07c7e4615 100644 --- a/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java +++ b/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java @@ -17,109 +17,153 @@ */ package org.apache.ivy.ant; -import java.io.IOException; -import java.text.ParseException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.apache.ivy.core.module.descriptor.Configuration; import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; import org.apache.ivy.core.module.id.ModuleRevisionId; +import org.apache.ivy.core.report.ConfigurationResolveReport; import org.apache.ivy.core.report.ResolveReport; import org.apache.ivy.core.resolve.IvyNode; import org.apache.ivy.core.resolve.ResolveOptions; + import org.apache.tools.ant.BuildException; import static org.apache.ivy.util.StringUtils.splitToArray; public class IvyDependencyUpdateChecker extends IvyPostResolveTask { - private String revisionToCheck = "latest.integration"; - private boolean download = false; private boolean checkIfChanged = false; private boolean showTransitive = false; + private String revisionToCheck = "latest.integration"; + + public boolean isDownload() { + return download; + } + + public void setDownload(boolean download) { + this.download = download; + } + + public boolean isCheckIfChanged() { + return checkIfChanged; + } + + public void setCheckIfChanged(boolean checkIfChanged) { + this.checkIfChanged = checkIfChanged; + } + + public boolean isShowTransitive() { + return showTransitive; + } + + public void setShowTransitive(boolean showTransitive) { + this.showTransitive = showTransitive; + } + + public String getRevisionToCheck() { + return revisionToCheck; + } + + public void setRevisionToCheck(String revisionToCheck) { + this.revisionToCheck = revisionToCheck; + } + + //-------------------------------------------------------------------------- + + @Override public void doExecute() throws BuildException { prepareAndCheck(); - ModuleDescriptor originalModuleDescriptor = getResolvedReport().getModuleDescriptor(); - // clone module descriptor - DefaultModuleDescriptor latestModuleDescriptor = new DefaultModuleDescriptor( - originalModuleDescriptor.getModuleRevisionId(), - originalModuleDescriptor.getStatus(), originalModuleDescriptor.getPublicationDate()); - // copy configurations - for (Configuration configuration : originalModuleDescriptor.getConfigurations()) { - latestModuleDescriptor.addConfiguration(configuration); - } - // clone dependency and add new one with the requested revisionToCheck - for (DependencyDescriptor dependencyDescriptor : originalModuleDescriptor.getDependencies()) { - ModuleRevisionId upToDateMrid = ModuleRevisionId.newInstance( - dependencyDescriptor.getDependencyRevisionId(), revisionToCheck); - latestModuleDescriptor.addDependency(dependencyDescriptor.clone(upToDateMrid)); - } - - // resolve ResolveOptions resolveOptions = new ResolveOptions(); + resolveOptions.setCheckIfChanged(checkIfChanged); + resolveOptions.setConfs(splitToArray(getConf())); resolveOptions.setDownload(isDownload()); resolveOptions.setLog(getLog()); - resolveOptions.setConfs(splitToArray(getConf())); - resolveOptions.setCheckIfChanged(checkIfChanged); - - ResolveReport latestReport; try { - latestReport = getIvyInstance().getResolveEngine().resolve(latestModuleDescriptor, - resolveOptions); + ResolveReport latestReport = getIvyInstance().getResolveEngine() + .resolve(createModuleDescriptorForRevisionToCheck(), resolveOptions); displayDependencyUpdates(getResolvedReport(), latestReport); if (showTransitive) { displayNewDependencyOnLatest(getResolvedReport(), latestReport); displayMissingDependencyOnLatest(getResolvedReport(), latestReport); } - - } catch (ParseException | IOException e) { + } catch (Exception e) { throw new BuildException("impossible to resolve dependencies:\n\t" + e, e); } + } + + private ModuleDescriptor createModuleDescriptorForRevisionToCheck() { + ModuleDescriptor moduleDescriptor = getResolvedReport().getModuleDescriptor(); + + // clone module descriptor + DefaultModuleDescriptor latestModuleDescriptor = new DefaultModuleDescriptor( + moduleDescriptor.getModuleRevisionId(), moduleDescriptor.getStatus(), moduleDescriptor.getPublicationDate()); + + // copy configurations + for (Configuration configuration : moduleDescriptor.getConfigurations()) { + latestModuleDescriptor.addConfiguration(configuration); + } + + // clone direct dependencies with the requested revisionToCheck + for (DependencyDescriptor dependencyDescriptor : moduleDescriptor.getDependencies()) { + ModuleRevisionId upToDateMrid = ModuleRevisionId.newInstance( + dependencyDescriptor.getDependencyRevisionId(), revisionToCheck); + latestModuleDescriptor.addDependency(dependencyDescriptor.clone(upToDateMrid)); + } + return latestModuleDescriptor; } private void displayDependencyUpdates(ResolveReport originalReport, ResolveReport latestReport) { - log("Dependencies updates available :"); - boolean dependencyUpdateDetected = false; - for (IvyNode latest : latestReport.getDependencies()) { - for (IvyNode originalDependency : originalReport.getDependencies()) { - if (originalDependency.getModuleId().equals(latest.getModuleId())) { - if (!originalDependency.getResolvedId().getRevision() - .equals(latest.getResolvedId().getRevision())) { - // is this dependency a transitive or a direct dependency? - // (unfortunately .isTransitive() methods do not have the same meaning) - boolean isTransitiveDependency = latest.getDependencyDescriptor(latest - .getRoot()) == null; - if (!isTransitiveDependency || showTransitive) { - log(String.format("\t%s#%s%s\t%s -> %s", - originalDependency.getResolvedId().getOrganisation(), - originalDependency.getResolvedId().getName(), - isTransitiveDependency ? " (transitive)" : "", - originalDependency.getResolvedId().getRevision(), - latest.getResolvedId().getRevision())); - dependencyUpdateDetected = true; - } + Map> updatesByConf = new LinkedHashMap<>(); + + for (String conf : latestReport.getConfigurations()) { + ConfigurationResolveReport newReport = latestReport.getConfigurationReport(conf); + ConfigurationResolveReport oldReport = originalReport.getConfigurationReport(conf); + + // NOTE: getModuleRevisionIds() filters evicted and problem deps + for (ModuleRevisionId latest : newReport.getModuleRevisionIds()) { + Iterable iter = oldReport.getNodes(latest.getModuleId()); + if (iter == null) continue; + for (IvyNode node : iter) { + boolean revisionNE = !node.getResolvedId().getRevision().equals(latest.getRevision()); + boolean transitive = (node.getDependencyDescriptor(node.getRoot()) == null); + if (revisionNE && (!transitive || showTransitive)) { + String update = String.format("\t%s#%s%s\t%s -> %s", + node.getResolvedId().getOrganisation(), + node.getResolvedId().getName(), + transitive ? " (transitive)" : "", + node.getResolvedId().getRevision(), + latest.getRevision()); + updatesByConf.computeIfAbsent(conf, k -> new ArrayList<>()).add(update); } - } } } - if (!dependencyUpdateDetected) { - log("\tAll dependencies are up to date"); + + log("Dependencies updates available :"); + if (updatesByConf.isEmpty()) { + log("All dependencies are up to date"); + } else { + updatesByConf.forEach((conf, updates) -> { + log("\tconf: " + conf); + updates.forEach(this::log); + }); } } - private void displayMissingDependencyOnLatest(ResolveReport originalReport, - ResolveReport latestReport) { + private void displayMissingDependencyOnLatest(ResolveReport originalReport, ResolveReport latestReport) { List listOfMissingDependencyOnLatest = new ArrayList<>(); for (IvyNode originalDependency : originalReport.getDependencies()) { boolean dependencyFound = false; @@ -141,8 +185,7 @@ private void displayMissingDependencyOnLatest(ResolveReport originalReport, } } - private void displayNewDependencyOnLatest(ResolveReport originalReport, - ResolveReport latestReport) { + private void displayNewDependencyOnLatest(ResolveReport originalReport, ResolveReport latestReport) { List listOfNewDependencyOnLatest = new ArrayList<>(); for (IvyNode latest : latestReport.getDependencies()) { boolean dependencyFound = false; @@ -162,36 +205,4 @@ private void displayNewDependencyOnLatest(ResolveReport originalReport, } } } - - public String getRevisionToCheck() { - return revisionToCheck; - } - - public void setRevisionToCheck(String revisionToCheck) { - this.revisionToCheck = revisionToCheck; - } - - public boolean isDownload() { - return download; - } - - public void setDownload(boolean download) { - this.download = download; - } - - public boolean isShowTransitive() { - return showTransitive; - } - - public void setShowTransitive(boolean showTransitive) { - this.showTransitive = showTransitive; - } - - public boolean isCheckIfChanged() { - return checkIfChanged; - } - - public void setCheckIfChanged(boolean checkIfChanged) { - this.checkIfChanged = checkIfChanged; - } } diff --git a/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java b/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java index 230c9c988..ea848ee3a 100644 --- a/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java +++ b/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java @@ -54,8 +54,8 @@ public void setUp() { dependencyUpdateChecker.setProject(project); } - @After - public void tearDown() { + @After + public void tearDown() { TestHelper.cleanCache(); } @@ -137,8 +137,7 @@ public void testInlineForNonExistingModule() { */ @Test(expected = BuildException.class) public void testFailure() { - dependencyUpdateChecker - .setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml")); + dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml")); dependencyUpdateChecker.execute(); } @@ -150,8 +149,7 @@ public void testFailureWithMissingConfigurations() { expExc.expect(BuildException.class); expExc.expectMessage("unknown"); - dependencyUpdateChecker - .setFile(new File("test/java/org/apache/ivy/ant/ivy-simple.xml")); + dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-simple.xml")); dependencyUpdateChecker.setConf("default,unknown"); dependencyUpdateChecker.execute(); } @@ -161,8 +159,7 @@ public void testFailureWithMissingConfigurations() { */ @Test(expected = BuildException.class) public void testFailureOnBadDependencyIvyFile() { - dependencyUpdateChecker.setFile(new File( - "test/java/org/apache/ivy/ant/ivy-failure2.xml")); + dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure2.xml")); dependencyUpdateChecker.execute(); } @@ -171,15 +168,13 @@ public void testFailureOnBadDependencyIvyFile() { */ @Test(expected = BuildException.class) public void testFailureOnBadStatusInDependencyIvyFile() { - dependencyUpdateChecker.setFile(new File( - "test/java/org/apache/ivy/ant/ivy-failure3.xml")); + dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure3.xml")); dependencyUpdateChecker.execute(); } @Test public void testHaltOnFailure() { - dependencyUpdateChecker - .setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml")); + dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml")); dependencyUpdateChecker.setHaltonfailure(false); dependencyUpdateChecker.execute(); } @@ -190,10 +185,8 @@ public void testExcludedConf() { dependencyUpdateChecker.setConf("*,!default"); dependencyUpdateChecker.execute(); - // assertTrue(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.1", "2.0")) - // .exists()); - // assertFalse(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.2", "2.0")) - // .exists()); + // assertTrue(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.1", "2.0")).exists()); + // assertFalse(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.2", "2.0")).exists()); assertLogContaining("Dependencies updates available :"); assertLogContaining("All dependencies are up to date"); @@ -213,8 +206,7 @@ public void testResolveWithAbsoluteFile() { dependencyUpdateChecker.getProject().setProperty("ivy.dep.file", ivyFile.getAbsolutePath()); dependencyUpdateChecker.execute(); - // assertTrue(getResolvedIvyFileInCache( - // ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists()); + // assertTrue(getResolvedIvyFileInCache(ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists()); } /** @@ -228,8 +220,7 @@ public void testResolveWithRelativeFile() { "test/java/org/apache/ivy/ant/ivy-simple.xml"); dependencyUpdateChecker.execute(); - // assertTrue(getResolvedIvyFileInCache( - // ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists()); + // assertTrue(getResolvedIvyFileInCache(ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists()); assertLogContaining("Dependencies updates available :"); assertLogContaining("org1#mod1.2\t2.0 -> 2.2"); @@ -237,26 +228,30 @@ public void testResolveWithRelativeFile() { @Test public void testSimpleExtends() { - dependencyUpdateChecker.setFile(new File( - "test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml")); + dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml")); dependencyUpdateChecker.execute(); - assertEquals("1", dependencyUpdateChecker.getProject().getProperty("ivy.parents.count")); - assertEquals("apache", - dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].organisation")); - assertEquals("resolve-simple", - dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].module")); - assertEquals("1.0", - dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].revision")); - assertNull(dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].branch")); - assertLogContaining("Dependencies updates available :"); + Project project = dependencyUpdateChecker.getProject(); + assertEquals("1", project.getProperty("ivy.parents.count")); + assertEquals("apache", project.getProperty("ivy.parent[0].organisation")); + assertEquals("resolve-simple", project.getProperty("ivy.parent[0].module")); + assertEquals("1.0", project.getProperty("ivy.parent[0].revision")); + assertNull(project.getProperty("ivy.parent[0].branch")); + + assertLogContaining("Dependencies updates available"); + // ivy-extends-multiconf.xml declares org1:mod1.1:1.1 assertLogContaining("org1#mod1.1\t1.1 -> 2.0"); - assertLogContaining("org1#mod1.1\t1.0 -> 2.0"); - assertLogContaining("org1#mod1.2\t2.1 -> 2.2"); - assertLogContaining("org2#mod2.1\t0.3 -> 0.7"); - - // inherited from parent + // ivy-multiconf.xml declares org1:mod1.1:2.0 -- no update available + assertLogNotContaining("org1#mod1.1\t2.0"); + // ivy-multiconf.xml declares org1:mod1.2:2.0 assertLogContaining("org1#mod1.2\t2.0 -> 2.2"); + // ivy-extends-multiconf.xml declares org2:mod2.1:0.3 + assertLogContaining("org2#mod2.1\t0.3 -> 0.7"); + // org2:mod2.1:0.3 ivy.xml declares org1:mod1.1:1.0 -- but showTransitives is false + assertLogNotContaining("org1#mod1.1\t1.0 -> 2.0"); + assertLogNotContaining("org1#mod1.1 (transitive)\t1.0 -> 2.0"); + // org1:mod1.1:2.0 ivy.xml declares org1:mod1.2:2.1 -- it evicted direct dependency + assertLogNotContaining("org1#mod1.2\t2.1 -> 2.2"); + assertLogNotContaining("org1#mod1.2 (transitive)\t2.1 -> 2.2"); } - } diff --git a/test/java/org/apache/ivy/ant/ivy-extends-merged.xml b/test/java/org/apache/ivy/ant/ivy-extends-merged.xml index 62fdd9940..00f32aa7f 100644 --- a/test/java/org/apache/ivy/ant/ivy-extends-merged.xml +++ b/test/java/org/apache/ivy/ant/ivy-extends-merged.xml @@ -15,13 +15,13 @@ "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. + under the License. --> - + - + Demonstrates configuration-specific dependencies @@ -37,10 +37,10 @@ - + - + @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml b/test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml index 9548eeeb5..aa61dced0 100644 --- a/test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml +++ b/test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml @@ -14,9 +14,9 @@ "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. + under the License. --> - + @@ -33,9 +33,9 @@ - + - \ No newline at end of file +