Xavier Hanin wrote: > > On 10/30/07, John Rasmussen <[EMAIL PROTECTED]> wrote: >> >> I have extended org.apache.ivy.plugins.repository.AbstractRepository >> in order to build modules directly from source. > > Interesting use case, feel free to share more about that with the > community > if you can. > >
The following might be useful. Please notice that to build a module from source requires that all module are structured in a uniform way and that the implementation highly depends on that structure. Thus a lot of details regarding structure is skipped, because that depends on your structure. First of all, this will only work in Ivy 2.0+ For Ivy 1.4.1, see https://issues.apache.org/jira/browse/IVY-448 Some of the examples below is in "it works" state and not in "brilliant" state. Feel free to comment on that, if some can be changed or made more simple. First the ivysettings.xml is updated like: <ivysettings> <typedef name="my-resolver" classname="mypackage.MyResolver"/> <typedef name="my-latest-strategy" classname="mypackage.MyLatestStrategy"/> <latest-strategies> <my-latest-strategy/> </latest-strategies> <conflict-managers> <latest-cm name="my-latest-strategy" latest="my-latest-strategy"/> </conflict-managers> <resolvers> <my-resolver name="my-resolver" changingPattern="head" changingMatcher="exact" allownomd="false" checksums="" latest="my-latest-strategy"> <ivy pattern="svn://myhost/[organisation]/[module]/[revision]/ivy-[module]-[revision].xml"/> <artifact pattern="svn://myhost/[organisation]/[module]/[revision]/[artifact]/[artifact]-[revision].[type]"/> </my-resolver> </resolvers> <modules> <module organisation="myorg" name=".*" resolver="my-resolver" conflict-manager="my-latest-strategy"/> </modules> </ivysettings> Two classes are required. The mypackage.MyResolver class builds the module, and the mypackage.MyLatestStrategy class evicts other revisions than the "build-from-source" revisions. In the ivysettings that revision is called 'head'. In order to load these two classes, the classes are inserted into myIvyPlugin.jar, and the retrieve target in ant looks like: Notice my.classpath should contain whatever you are using. I have module revisions stored in Subversion which are retrieved from the repository using svnkit, which has to be included in my.classpath. <path id="my.classpath"> <pathelement location="myIvyPlugin.jar"/> <pathelement location="/path/to/ivy-2.0.0-alpha2-incubating.jar"/> </path> <taskdef name="ivy-retrieve" classname="org.apache.ivy.ant.IvyRetrieve" classpathref="my.classpath" loaderref="ivy.task.loader"/> <target name="retrieve"> <ivy-retrieve/> </target> Now for the java stuff First MyResolver, which is simple: import org.apache.ivy.plugins.resolver.RepositoryResolver; public class MyResolver extends RepositoryResolver { public MyResolver() { setRepository(new MyRepository()); } public String getTypeName() { return "myrepo"; } } MyRepository is the next java class. Please notice, that the put method is unimplemented. I'm not using Ivy to stored revisions - historical reasons. Notice the uri cache, otherwise the build process is repeated a couple of times. import org.apache.ivy.core.module.descriptor.Artifact; import org.apache.ivy.plugins.repository.AbstractRepository; import org.apache.ivy.plugins.repository.Resource; import org.apache.ivy.plugins.repository.TransferEvent; import org.apache.ivy.util.Message; public final class MyRepository extends AbstractRepository { private final Map uriMap = new HashMap(); public final void get(final String source, final File destination) throws IOException { try { final RepositoryResource repositoryResource = getRepositoryResource(source); fireTransferInitiated(repositoryResource, TransferEvent.REQUEST_GET); fireTransferStarted(); repositoryResource.save(destination); fireTransferCompleted(repositoryResource.getContentLength()); } catch (final MyException e) { Message.error("Cannot get " + source); fireTransferError(e); } } public final Resource getResource(final String source) throws IOException { try { final Resource resource = getRepositoryResource(source); return resource; } catch (final MyException e) { Message.error("Cannot get " + source); fireTransferError(e); throw new IOException("Failed"); } } public final List list(final String source) throws IOException { try { final List list = getRepositoryResource(source).list(); return list; } catch (final MyException e) { fireTransferError(e); return Collections.EMPTY_LIST; } } public final void put(final Artifact artifact, final File source, final String destination, final boolean overwrite) throws IOException { fireTransferError(new MyException("Put-operation not supported. Source " + source + ", destination " + destination)); } private final RepositoryResource getRepositoryResource(final String uri) throws MyException { RepositoryResource repositoryResource = ( RepositoryResource ) uriMap.get(uri); if (repositoryResource == null) { repositoryResource = MyResource(uri); uriMap.put(uri, repositoryResource); } return repositoryResource; } } A simple interface public interface RepositoryResource extends Resource { List list(); void save(final File destination) throws IOException; } Given an uri in a format defined in ivysettings, the task is to build the module from source. This is done in MyResource, where all module structure details are placed. Thus this java class does not compile as is. You need to add your details or return "null" if not used. I have listed a few "null" methods. Notice, if a module build more than one artifact, then some caching/"need to update" functionality must be implemented somewhere to avoid that the system builds everything for each artifact. The files generated from the build is returned to ivy in the save() method public final class MyResource implements RepositoryResource { public MyResource(final String uri { // Build source } public final long getContentLength() { return getFile().length(); } public final String getName() { return uri; } public final boolean isLocal() { return false; } public final List list() { return Collections.EMPTY_LIST; } public InputStream openStream() throws IOException { throw new IOException("openStream unsupported."); } public final void save(final File destination) throws IOException { final File source = getFile(); final InputStream is = new FileInputStream(source); final OutputStream os = new FileOutputStream(destination); final byte[] b = new byte[BUFFER_SIZE]; int size; while ((size = is.read(b)) != -1) { os.write(b, 0, size); } os.close(); is.close(); } private final File getFile() { // Return the file being build given the uri } } Other revisions than "head" should also be handled. That depends on how your revisions are stored. I have an svnkit implementation to retrieve from Subversion - historical reasons. Today such an implementation might already exists in Ivy?? Most artifacts can be returned "as is". One exception is the ivy.xml artifact. The revision in Ivy.xml must correspond to the revision in the dependency tag, otherwise Ivy complains. Of course, this depends on how you return your ivy.xml file. (You might build/deliver an ivy.xml with correct version) I did a simple OutputStream replace: And use a replaced os in the save() method BUT only for the ivy.xml artifact! new StringReplaceOutputStream(os, " revision *= *\"[^\"]*\"", " revision=\"head\""); public final class StringReplaceOutputStream extends OutputStream { private int index; private final OutputStream outputStream; private final String regex; private final String replacement; private final StringBuffer strBuf; public StringReplaceOutputStream(final OutputStream outputStream, final String regex, final String replacement) { this.outputStream = outputStream; strBuf = new StringBuffer(); this.regex = regex; this.replacement = replacement; index = 0; } public final void close() throws IOException { flush(); outputStream.close(); } public final synchronized void flush() throws IOException { writeFromStrBuf(strBuf.length()); outputStream.flush(); } public final void write(final int b) throws IOException { strBuf.append(( char ) (b & 0xFF)); update(); } public final void write(final byte[] b, final int off, final int len) throws IOException { strBuf.append(new String(b, off, len)); update(); } private final void update() throws IOException { while (index < (strBuf.length() - 1)) { if (strBuf.charAt(index) == 0x0D) { if (strBuf.charAt(index + 1) == 0x0A) { index++; } writeFromStrBuf(index + 1); } else { index++; } } } private final void writeFromStrBuf(final int index) throws IOException { final String str = strBuf.substring(0, index).toString(); final String strRep = str.replaceAll(regex, replacement); outputStream.write(strRep.getBytes()); strBuf.delete(0, index); this.index = 0; } } To evict other revisions than "head" add MyLatestStrategy.java import java.util.Comparator; import org.apache.ivy.plugins.latest.ArtifactInfo; import org.apache.ivy.plugins.latest.ComparatorLatestStrategy; import org.apache.ivy.plugins.latest.LatestRevisionStrategy; public class MyLatestStrategy extends ComparatorLatestStrategy { public MyLatestStrategy() { super(new Comparator() { private final Comparator comparator = new LatestRevisionStrategy().COMPARATOR; public int compare(Object o1, Object o2) { String rev1 = (( ArtifactInfo ) o1).getRevision(); String rev2 = (( ArtifactInfo ) o2).getRevision(); if (rev1.startsWith("head")) { return 1; } else if (rev2.startsWith("head")) { return -1; } else { return comparator.compare(o1, o2); } } }); setName("my-latest-strategy"); } } -- View this message in context: http://www.nabble.com/How-to-halt-on-an-unresolved-dependency-tf4717519.html#a13791968 Sent from the ivy-dev mailing list archive at Nabble.com.
