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
This commit is contained in:
Xavier Hanin 2005-12-26 08:57:28 +00:00
parent cc87a94db5
commit 8c13971089
8 changed files with 257 additions and 106 deletions

View File

@ -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)

View File

@ -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 <b>direct</b> 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 <b>direct</b> 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";
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
}
}
}

View File

@ -0,0 +1,12 @@
<ivy-module version="1.0">
<info organisation="jayasoft"
module="resolve-noconflict"
revision="1.0"
status="release"
/>
<dependencies>
<dependency org="org1" name="mod1.2" rev="2.0"/>
<dependency org="org1" name="mod1.2" rev="2.1"/>
<dependency org="org2" name="mod2.1" rev="0.5"/>
</dependencies>
</ivy-module>

View File

@ -0,0 +1,11 @@
<ivy-module version="1.0">
<info organisation="jayasoft"
module="resolve-noconflict"
revision="1.0"
status="release"
/>
<dependencies>
<dependency org="org1" name="mod1.2" rev="2.0"/>
<dependency org="org2" name="mod2.2" rev="0.5"/>
</dependencies>
</ivy-module>

View File

@ -0,0 +1,14 @@
<ivyconf>
<conf defaultResolver="test"
defaultConflictManager="strict"
/>
<resolvers>
<filesystem name="test" >
<artifact pattern="test/repositories/1/[organisation]/[module]/[type]s/[artifact]-[revision].[type]" />
<ivy pattern="test/repositories/1/[organisation]/[module]/ivys/ivy-[revision].xml" />
</filesystem>
</resolvers>
</ivyconf>