From 8c1397108997eb4a1fc51c1fdb67f1543172825f Mon Sep 17 00:00:00 2001 From: Xavier Hanin Date: Mon, 26 Dec 2005 08:57:28 +0000 Subject: [PATCH] add a conflict manager ("strict") making build fail when a diamond conflict is found (thanks to Christer Jonsson) (IVY-118) git-svn-id: https://svn.apache.org/repos/asf/incubator/ivy/trunk@484073 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 1 + src/java/fr/jayasoft/ivy/Ivy.java | 214 +++++++++--------- .../ivy/conflict/StrictConflictException.java | 26 +++ .../ivy/conflict/StrictConflictManager.java | 42 ++++ .../conflict/StrictConflictManagerTest.java | 43 ++++ .../fr/jayasoft/ivy/conflict/ivy-conflict.xml | 12 + .../jayasoft/ivy/conflict/ivy-noconflict.xml | 11 + .../ivy/conflict/ivyconf-strict-test.xml | 14 ++ 8 files changed, 257 insertions(+), 106 deletions(-) create mode 100644 src/java/fr/jayasoft/ivy/conflict/StrictConflictException.java create mode 100644 src/java/fr/jayasoft/ivy/conflict/StrictConflictManager.java create mode 100644 test/java/fr/jayasoft/ivy/conflict/StrictConflictManagerTest.java create mode 100644 test/java/fr/jayasoft/ivy/conflict/ivy-conflict.xml create mode 100644 test/java/fr/jayasoft/ivy/conflict/ivy-noconflict.xml create mode 100644 test/java/fr/jayasoft/ivy/conflict/ivyconf-strict-test.xml diff --git a/CHANGES.txt b/CHANGES.txt index b9df473c..d502be89 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,7 @@ - NEW: ability to output a path of dependencies in cache from the standalone mode (IVY-92) - NEW: it is now possible to reference existing resolver in resolver containers (IVY-35) - NEW: overwrite attribute in the publish task now let force overwrite of read only files (IVY-83) +- NEW: add a conflict manager ("strict") making build fail when a diamond conflict is found (thanks to Christer Jonsson) (IVY-118) - IMPROVE: allow optional parts in the patterns (IVY-102) (thanks to Maarten Coene) - IMPROVE: ability to define variable directly in ivyconf.xml (IVY-100) - IMPROVE: ability to use no revision in pattern with latest.integration dependency, artifacts being updated according to revision change in ivy file (if checkmodified is set to true) (IVY-95) diff --git a/src/java/fr/jayasoft/ivy/Ivy.java b/src/java/fr/jayasoft/ivy/Ivy.java index b7aa906c..a6c635d2 100644 --- a/src/java/fr/jayasoft/ivy/Ivy.java +++ b/src/java/fr/jayasoft/ivy/Ivy.java @@ -39,6 +39,7 @@ import org.xml.sax.SAXException; import fr.jayasoft.ivy.conflict.LatestConflictManager; import fr.jayasoft.ivy.conflict.NoConflictManager; +import fr.jayasoft.ivy.conflict.StrictConflictManager; import fr.jayasoft.ivy.filter.Filter; import fr.jayasoft.ivy.filter.FilterHelper; import fr.jayasoft.ivy.latest.LatestLexicographicStrategy; @@ -95,13 +96,13 @@ public class Ivy implements TransferListener { private DependencyResolver _dictatorResolver = null; private String _defaultResolverName; - private File _defaultCache; + private File _defaultCache; private boolean _checkUpToDate = true; - private Map _moduleConfigurations = new LinkedHashMap(); // Map (ModuleId -> String resolverName) + private Map _moduleConfigurations = new LinkedHashMap(); // Map (ModuleId -> String resolverName) private Map _conflictsManager = new HashMap(); // Map (String conflictManagerName -> ConflictManager) private Map _latestStrategies = new HashMap(); // Map (String latestStrategyName -> LatestStrategy) - + private Map _variables = new HashMap(); private ReportOutputter[] _reportOutputters = new ReportOutputter[] {new LogReportOutputter(), new XmlReportOutputter()}; @@ -122,25 +123,25 @@ public class Ivy implements TransferListener { private boolean _useRemoteConfig = false; public Ivy() { - String ivyTypeDefs = System.getProperty("ivy.typedef.files"); - if (ivyTypeDefs != null) { - String[] files = ivyTypeDefs.split("\\,"); - for (int i = 0; i < files.length; i++) { - try { - typeDefs(new FileInputStream(new File(files[i].trim()))); - } catch (FileNotFoundException e) { - Message.warn("typedefs file not found: "+files[i].trim()); - } catch (IOException e) { - Message.warn("problem with typedef file: "+files[i].trim()+": "+e.getMessage()); - } - } - } else { - try { - typeDefs(Ivy.class.getResourceAsStream("typedef.properties")); - } catch (IOException e) { - Message.warn("impossible to load default type defs"); - } - } + String ivyTypeDefs = System.getProperty("ivy.typedef.files"); + if (ivyTypeDefs != null) { + String[] files = ivyTypeDefs.split("\\,"); + for (int i = 0; i < files.length; i++) { + try { + typeDefs(new FileInputStream(new File(files[i].trim()))); + } catch (FileNotFoundException e) { + Message.warn("typedefs file not found: "+files[i].trim()); + } catch (IOException e) { + Message.warn("problem with typedef file: "+files[i].trim()+": "+e.getMessage()); + } + } + } else { + try { + typeDefs(Ivy.class.getResourceAsStream("typedef.properties")); + } catch (IOException e) { + Message.warn("impossible to load default type defs"); + } + } LatestLexicographicStrategy latestLexicographicStrategy = new LatestLexicographicStrategy(); LatestRevisionStrategy latestRevisionStrategy = new LatestRevisionStrategy(); LatestTimeStrategy latestTimeStrategy = new LatestTimeStrategy(); @@ -152,6 +153,7 @@ public class Ivy implements TransferListener { addConflictManager("latest-revision", new LatestConflictManager("latest-revision", latestRevisionStrategy)); addConflictManager("latest-time", new LatestConflictManager("latest-time", latestTimeStrategy)); addConflictManager("all", new NoConflictManager()); + addConflictManager("strict", new StrictConflictManager()); _listingIgnore.add(".cvsignore"); _listingIgnore.add("CVS"); @@ -203,7 +205,7 @@ public class Ivy implements TransferListener { } } - public void typeDefs(InputStream stream) throws IOException { + public void typeDefs(InputStream stream) throws IOException { try { Properties p = new Properties(); p.load(stream); @@ -211,17 +213,17 @@ public class Ivy implements TransferListener { } finally { stream.close(); } - } + } public void typeDefs(Properties p) { for (Iterator iter = p.keySet().iterator(); iter.hasNext();) { - String name = (String) iter.next(); - try { - typeDef(name, Class.forName(p.getProperty(name))); - } catch (ClassNotFoundException e) { - Message.warn("impossible to define resolver "+name+": class not found: "+p.getProperty(name)); - } - } + String name = (String) iter.next(); + try { + typeDef(name, Class.forName(p.getProperty(name))); + } catch (ClassNotFoundException e) { + Message.warn("impossible to define resolver "+name+": class not found: "+p.getProperty(name)); + } + } } @@ -406,7 +408,7 @@ public class Ivy implements TransferListener { */ public void addModuleConfiguration(ModuleId moduleId, String resolverName) { checkResolverName(resolverName); - _moduleConfigurations.put(moduleId, resolverName); + _moduleConfigurations.put(moduleId, resolverName); } public File getDefaultCache() { @@ -779,13 +781,13 @@ public class Ivy implements TransferListener { report.addReport(confs[i], confReport); } } - - ResolveData data = new ResolveData(this, cache, date, confReport, validate, dependenciesMap); + + ResolveData data = new ResolveData(this, cache, date, confReport, validate, dependenciesMap); IvyNode node = new IvyNode(data, md, confs[i], true); node.setRootModuleConf(confs[i]); fetchDependencies(node, confs[i]); } - } + } // prune and reverse sort fectched dependencies @@ -1136,80 +1138,80 @@ public class Ivy implements TransferListener { ///////////////////////////////////////////////////////////////////////// // PUBLISH ///////////////////////////////////////////////////////////////////////// - /** - * delivers a resolved ivy file based upon last resolve call status and - * the given PublishingDependencyRevisionResolver. - * If resolve report file cannot be found in cache, then it throws - * an IllegalStateException (maybe resolve has not been called before ?) - * Moreover, the given PublishingDependencyRevisionResolver is used for each - * dependency to get its published information. This can particularly useful - * when the publish is made for a delivery, and when we wish to deliver each - * dependency which is still in integration. The PublishingDependencyRevisionResolver - * can then do the delivering work for the dependency and return the new (delivered) - * dependency info (with the delivered revision). Note that - * PublishingDependencyRevisionResolver is only called for each direct dependency. - * + /** + * delivers a resolved ivy file based upon last resolve call status and + * the given PublishingDependencyRevisionResolver. + * If resolve report file cannot be found in cache, then it throws + * an IllegalStateException (maybe resolve has not been called before ?) + * Moreover, the given PublishingDependencyRevisionResolver is used for each + * dependency to get its published information. This can particularly useful + * when the publish is made for a delivery, and when we wish to deliver each + * dependency which is still in integration. The PublishingDependencyRevisionResolver + * can then do the delivering work for the dependency and return the new (delivered) + * dependency info (with the delivered revision). Note that + * PublishingDependencyRevisionResolver is only called for each direct dependency. + * * @param status the new status, null to keep the old one - * @throws ParseException - */ - public void deliver(ModuleRevisionId mrid, - String revision, - File cache, - String destIvyPattern, - String status, - Date pubdate, - PublishingDependencyRevisionResolver pdrResolver, + * @throws ParseException + */ + public void deliver(ModuleRevisionId mrid, + String revision, + File cache, + String destIvyPattern, + String status, + Date pubdate, + PublishingDependencyRevisionResolver pdrResolver, boolean validate) throws IOException, ParseException { - Message.info(":: delivering :: "+mrid+" :: "+revision+" :: "+status+" :: "+pubdate); + Message.info(":: delivering :: "+mrid+" :: "+revision+" :: "+status+" :: "+pubdate); Message.verbose("\tvalidate = "+validate); - long start = System.currentTimeMillis(); + long start = System.currentTimeMillis(); destIvyPattern = substitute(destIvyPattern); - // 1) find the resolved module descriptor in cache - File ivyFile = getResolvedIvyFileInCache(cache, mrid); - if (!ivyFile.exists()) { - throw new IllegalStateException("ivy file not found in cache for "+mrid+": please resolve dependencies before publishing ("+ivyFile+")"); - } - ModuleDescriptor md = null; - URL ivyFileURL = null; - try { - ivyFileURL = ivyFile.toURL(); - md = XmlModuleDescriptorParser.parseDescriptor(this, ivyFileURL, validate); - md.setResolvedModuleRevisionId(new ModuleRevisionId(mrid.getModuleId(), revision)); - md.setResolvedPublicationDate(pubdate); - } catch (MalformedURLException e) { - throw new RuntimeException("malformed url obtained for file "+ivyFile); - } catch (ParseException e) { - throw new IllegalStateException("bad ivy file in cache for "+mrid+": please clean and resolve again"); - } - // 2) use pdrResolver to resolve dependencies info - Map resolvedDependencies = new HashMap(); // Map (ModuleId -> String revision) - DependencyDescriptor[] dependencies = md.getDependencies(); - for (int i = 0; i < dependencies.length; i++) { + // 1) find the resolved module descriptor in cache + File ivyFile = getResolvedIvyFileInCache(cache, mrid); + if (!ivyFile.exists()) { + throw new IllegalStateException("ivy file not found in cache for "+mrid+": please resolve dependencies before publishing ("+ivyFile+")"); + } + ModuleDescriptor md = null; + URL ivyFileURL = null; + try { + ivyFileURL = ivyFile.toURL(); + md = XmlModuleDescriptorParser.parseDescriptor(this, ivyFileURL, validate); + md.setResolvedModuleRevisionId(new ModuleRevisionId(mrid.getModuleId(), revision)); + md.setResolvedPublicationDate(pubdate); + } catch (MalformedURLException e) { + throw new RuntimeException("malformed url obtained for file "+ivyFile); + } catch (ParseException e) { + throw new IllegalStateException("bad ivy file in cache for "+mrid+": please clean and resolve again"); + } + // 2) use pdrResolver to resolve dependencies info + Map resolvedDependencies = new HashMap(); // Map (ModuleId -> String revision) + DependencyDescriptor[] dependencies = md.getDependencies(); + for (int i = 0; i < dependencies.length; i++) { DependencyResolver resolver = getResolver(dependencies[i].getDependencyId()); ResolvedModuleRevision dependency = resolver.getDependency(dependencies[i], new ResolveData(this, cache, pubdate, null, validate)); if (dependency == null) { - Message.warn(resolver.getName()+": unresolved dependency while publishing: "+dependencies[i].getDependencyRevisionId()); + Message.warn(resolver.getName()+": unresolved dependency while publishing: "+dependencies[i].getDependencyRevisionId()); } else { - resolvedDependencies.put(dependencies[i].getDependencyId(), pdrResolver.resolve(md, status, dependency.getDescriptor())); + resolvedDependencies.put(dependencies[i].getDependencyId(), pdrResolver.resolve(md, status, dependency.getDescriptor())); } } - - // 3) copy the source resolved ivy to the destination specified, - // updating status, revision and dependency revisions obtained by - // PublishingDependencyRevisionResolver - String publishedIvy = IvyPatternHelper.substitute(destIvyPattern, md.getResolvedModuleRevisionId()); - Message.info("\tdelivering ivy file to "+publishedIvy); - try { - XmlModuleDescriptorUpdater.update(ivyFileURL, - new File(publishedIvy), - resolvedDependencies, status, revision, pubdate); - } catch (SAXException ex) { - throw new IllegalStateException("bad ivy file in cache for "+mrid+": please clean and resolve again"); - } - - Message.verbose("\tdeliver done ("+(System.currentTimeMillis()-start)+"ms)"); - } + + // 3) copy the source resolved ivy to the destination specified, + // updating status, revision and dependency revisions obtained by + // PublishingDependencyRevisionResolver + String publishedIvy = IvyPatternHelper.substitute(destIvyPattern, md.getResolvedModuleRevisionId()); + Message.info("\tdelivering ivy file to "+publishedIvy); + try { + XmlModuleDescriptorUpdater.update(ivyFileURL, + new File(publishedIvy), + resolvedDependencies, status, revision, pubdate); + } catch (SAXException ex) { + throw new IllegalStateException("bad ivy file in cache for "+mrid+": please clean and resolve again"); + } + + Message.verbose("\tdeliver done ("+(System.currentTimeMillis()-start)+"ms)"); + } /** * @@ -1262,17 +1264,17 @@ public class Ivy implements TransferListener { // collect all declared artifacts of this module Collection missing = new ArrayList(); Set artifactsSet = new HashSet(); - String[] confs = md.getConfigurationsNames(); - for (int i = 0; i < confs.length; i++) { + String[] confs = md.getConfigurationsNames(); + for (int i = 0; i < confs.length; i++) { Artifact[] artifacts = md.getArtifacts(confs[i]); for (int j = 0; j < artifacts.length; j++) { artifactsSet.add(artifacts[j]); } } // for each declared published artifact in this descriptor, do: - for (Iterator iter = artifactsSet.iterator(); iter.hasNext();) { + for (Iterator iter = artifactsSet.iterator(); iter.hasNext();) { Artifact artifact = (Artifact) iter.next(); - // 1) copy the artifact using src pattern and resolver + // 1) copy the artifact using src pattern and resolver if (!publish(artifact, srcArtifactPattern, resolver, overwrite)) { missing.add(artifact); } @@ -1353,10 +1355,10 @@ public class Ivy implements TransferListener { public static String getLocalHostName() { try { - return InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - return "localhost"; - } + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + return "localhost"; + } } diff --git a/src/java/fr/jayasoft/ivy/conflict/StrictConflictException.java b/src/java/fr/jayasoft/ivy/conflict/StrictConflictException.java new file mode 100644 index 00000000..ed2a5c59 --- /dev/null +++ b/src/java/fr/jayasoft/ivy/conflict/StrictConflictException.java @@ -0,0 +1,26 @@ +/* + * This file is subject to the license found in LICENCE.TXT in the root directory of the project. + * + * #SNAPSHOT# + */ +package fr.jayasoft.ivy.conflict; + +public class StrictConflictException extends RuntimeException { + + public StrictConflictException() { + super(); + } + + public StrictConflictException(String msg) { + super(msg); + } + + public StrictConflictException(Throwable t) { + super(t); + } + + public StrictConflictException(String msg, Throwable t) { + super(msg, t); + } + +} diff --git a/src/java/fr/jayasoft/ivy/conflict/StrictConflictManager.java b/src/java/fr/jayasoft/ivy/conflict/StrictConflictManager.java new file mode 100644 index 00000000..ceea2aca --- /dev/null +++ b/src/java/fr/jayasoft/ivy/conflict/StrictConflictManager.java @@ -0,0 +1,42 @@ +/* + * This file is subject to the license found in LICENCE.TXT in the root directory of the project. + * + * #SNAPSHOT# + */ +package fr.jayasoft.ivy.conflict; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import fr.jayasoft.ivy.IvyNode; +import fr.jayasoft.ivy.conflict.AbstractConflictManager; +import fr.jayasoft.ivy.util.Message; + + +public class StrictConflictManager extends AbstractConflictManager +{ + + public StrictConflictManager() { + } + + + public Collection resolveConflicts(IvyNode parent, Collection conflicts) + { + IvyNode lastNode = null; + for (Iterator iter = conflicts.iterator(); iter.hasNext();) { + IvyNode node = (IvyNode)iter.next(); + + if (lastNode != null && !lastNode.equals(node)) { + String msg = lastNode + " (needed by " + lastNode.getParent() + ") conflicts with " + node + " (needed by " + node.getParent() + ")"; + Message.error(msg); + Message.sumupProblems(); + throw new StrictConflictException(msg); + } + lastNode = node; + } + + return Collections.singleton(lastNode); + } + +} diff --git a/test/java/fr/jayasoft/ivy/conflict/StrictConflictManagerTest.java b/test/java/fr/jayasoft/ivy/conflict/StrictConflictManagerTest.java new file mode 100644 index 00000000..b7dc2f3b --- /dev/null +++ b/test/java/fr/jayasoft/ivy/conflict/StrictConflictManagerTest.java @@ -0,0 +1,43 @@ +/* + * This file is subject to the license found in LICENCE.TXT in the root directory of the project. + * + * #SNAPSHOT# + */ +package fr.jayasoft.ivy.conflict; + +import java.util.Date; + +import fr.jayasoft.ivy.ConflictManager; +import fr.jayasoft.ivy.Ivy; +import junit.framework.TestCase; + +public class StrictConflictManagerTest extends TestCase { + + public void testInitFromConf() throws Exception { + Ivy ivy = new Ivy(); + ivy.configure(StrictConflictManagerTest.class.getResource("ivyconf-strict-test.xml")); + ConflictManager cm = ivy.getDefaultConflictManager(); + assertTrue(cm instanceof StrictConflictManager); + } + + public void testNoConflictResolve() throws Exception { + Ivy ivy = new Ivy(); + ivy.configure(StrictConflictManagerTest.class.getResource("ivyconf-strict-test.xml")); + + ivy.resolve(StrictConflictManagerTest.class.getResource("ivy-noconflict.xml"), null, new String[] { "*" }, null, new Date(), false); + } + + public void testConflictResolve() throws Exception { + Ivy ivy = new Ivy(); + ivy.configure(StrictConflictManagerTest.class.getResource("ivyconf-strict-test.xml")); + + try { + ivy.resolve(StrictConflictManagerTest.class.getResource("ivy-conflict.xml"), null, new String[] { "*" }, null, new Date(), false); + + fail("Resolve should have failed with a conflict"); + } catch (StrictConflictException e) { + // this is expected + } + } + +} diff --git a/test/java/fr/jayasoft/ivy/conflict/ivy-conflict.xml b/test/java/fr/jayasoft/ivy/conflict/ivy-conflict.xml new file mode 100644 index 00000000..d685b981 --- /dev/null +++ b/test/java/fr/jayasoft/ivy/conflict/ivy-conflict.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/test/java/fr/jayasoft/ivy/conflict/ivy-noconflict.xml b/test/java/fr/jayasoft/ivy/conflict/ivy-noconflict.xml new file mode 100644 index 00000000..686747a2 --- /dev/null +++ b/test/java/fr/jayasoft/ivy/conflict/ivy-noconflict.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/test/java/fr/jayasoft/ivy/conflict/ivyconf-strict-test.xml b/test/java/fr/jayasoft/ivy/conflict/ivyconf-strict-test.xml new file mode 100644 index 00000000..189a405f --- /dev/null +++ b/test/java/fr/jayasoft/ivy/conflict/ivyconf-strict-test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + +