From f6745b24f59bcc6796529c9b43bd37a0d4dd2846 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Mon, 11 Sep 2000 11:47:12 +0000 Subject: [PATCH] New task . Many thanks to Patrick C. Beard for allowing his original jlink code to be placed under the Apache Software License. Submitted by: Matthew Kuperus Heun git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267982 13f79535-47bb-0310-9956-ffa450edef68 --- docs/index.html | 1 + docs/jlink.html | 138 ++++++ .../tools/ant/taskdefs/defaults.properties | 1 + .../optional/jlink/ClassNameReader.java | 161 +++++++ .../taskdefs/optional/jlink/JlinkTask.java | 211 +++++++++ .../ant/taskdefs/optional/jlink/jlink.java | 420 ++++++++++++++++++ 6 files changed, 932 insertions(+) create mode 100644 docs/jlink.html create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java diff --git a/docs/index.html b/docs/index.html index 2c1e5652f..3ff6ddac7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3865,6 +3865,7 @@ it had been located at htdocs/manual/ChangeLog.txt.

  • Cab
  • FTP
  • +
  • Jlink
  • JUnit
  • NetRexxC
  • RenameExtensions
  • diff --git a/docs/jlink.html b/docs/jlink.html new file mode 100644 index 000000000..ba249c26c --- /dev/null +++ b/docs/jlink.html @@ -0,0 +1,138 @@ + + + + + +

    Jlink

    +

    Description:

    +

    Links entries from sub-builds and libraries.

    + +

    The jlink task can be used to build jar and zip files, similar to +the jar task. +However, jlink provides options for controlling the way entries from +input files +are added to the output file. Specifically, capabilities for merging +entries from +multiple zip or jar files is available.

    + +

    If a mergefile is specified directly (eg. at the top level of a +mergefiles +pathelement) and the mergefile ends in ".zip" or +".jar", +entries in the mergefile will be merged into the outfile. A file with +any other extension +will be added to the output file, even if it is specified in the +mergefiles element. +Directories specified in either the mergefiles or addfiles element +are added to the +output file as you would expect: all files in subdirectories are +recursively added to +the output file with appropriate prefixes in the output file +(without merging). +

    + +

    +In the case where duplicate entries and/or files are found among the +files to be merged or +added, jlink merges or adds the first entry and ignores all subsequent entries. +

    + +

    +jlink ignores META-INF directories in mergefiles. Users should supply their +own manifest information for the output file. +

    + +

    It is possible to refine the set of files that are being jlinked. +This can be +done with the includes, includesfile, excludes, +excludesfile, +and defaultexcludes attributes on the addfiles and +mergefiles +nested elements. With the includes or includesfile +attribute you specify the files you want to have included by using patterns. +The exclude or excludesfile attribute is used to specify +the files you want to have excluded. This is also done with patterns. And +finally with the defaultexcludes attribute, you can specify whether you +want to use default exclusions or not. See the section on directory based tasks, on how the +inclusion/exclusion of files works, and how to write patterns. The patterns are +relative to the base directory.

    + + + +

    Parameters:

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescriptionRequired
    outfilethe path of the output file.Yes
    compresswhether or not the output should be compressed. +true, + yes, or on result in compressed output. + If omitted, output will be uncompressed (inflated).No
    mergefilesfiles to be merged into the output, if possible.At least one of +mergefiles or addfiles
    addfilesfiles to be added to the output.
    + +

    Examples

    + +The following will merge the entries in mergefoo.jar and mergebar.jar +into out.jar. +mac.jar and pc.jar will be added as single entries to out.jar. +
    +<jlink compress="false" outfile="out.jar"/>
    +   <mergefiles>
    +     <pathelement path="${build.dir}/mergefoo.jar"/>
    +     <pathelement path="${build.dir}/mergebar.jar"/>
    +   </mergefiles>
    +   <addfiles>
    +     <pathelement path="${build.dir}/mac.jar"/>
    +     <pathelement path="${build.dir}/pc.zip"/>
    +   </addfiles>
    +</jlink>
    +
    + +Suppose the file foo.jar contains two entries: bar.class and +barnone/myClass.zip. +Suppose the path for file foo.jar is build/tempbuild/foo.jar. The +following example +will provide the entry tempbuild/foo.jar in the out.jar. +
    +<jlink compress="false" outfile="out.jar"/>
    +   <mergefiles>
    +     <pathelement path="build/tempbuild"/>
    +   </mergefiles>
    +</jlink>
    +
    + +However, the next example would result in two top-level entries in out.jar, +namely bar.class and barnone/myClass.zip +
    +<jlink compress="false" outfile="out.jar"/>
    +   <mergefiles>
    +     <pathelement path="build/tempbuild/foo.jar"/>
    +   </mergefiles>
    +</jlink>
    +
    + + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index 182294e88..a8f381a52 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -60,6 +60,7 @@ ftp=org.apache.tools.ant.taskdefs.optional.FTP javacc=org.apache.tools.ant.taskdefs.optional.javacc.JavaCC jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut +jlink=org.apache.tools.ant.taskdefs.optional.jlink.JlinkTask # deprecated ant tasks (kept for back compatibility) javadoc2=org.apache.tools.ant.taskdefs.Javadoc diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java b/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java new file mode 100644 index 000000000..0cc96afea --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java @@ -0,0 +1,161 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.taskdefs.optional.jlink; + +import java.io .*; + +/** + * Reads just enough of a class file to determine the class' full name. + * + *

    Extremely minimal constant pool implementation, mainly to support extracting + * strings from a class file. + * @author Patrick C. Beard. + */ +class ConstantPool extends Object{ + + static final + byte UTF8 = 1, UNUSED = 2, INTEGER = 3, FLOAT = 4, LONG = 5, DOUBLE = 6, + CLASS = 7, STRING = 8, FIELDREF = 9, METHODREF = 10, + INTERFACEMETHODREF = 11, NAMEANDTYPE = 12; + + byte[] types; + + Object[] values; + + ConstantPool( DataInput data ) throws IOException { + super(); + + int count = data .readUnsignedShort(); + types = new byte [ count ]; + values = new Object [ count ]; + // read in all constant pool entries. + for ( int i = 1; i < count; i++ ) { + byte type = data .readByte(); + types[i] = type; + switch (type) + { + case UTF8 : + values[i] = data .readUTF(); + break; + + case UNUSED : + break; + + case INTEGER : + values[i] = new Integer( data .readInt() ); + break; + + case FLOAT : + values[i] = new Float( data .readFloat() ); + break; + + case LONG : + values[i] = new Long( data .readLong() ); + ++i; + break; + + case DOUBLE : + values[i] = new Double( data .readDouble() ); + ++i; + break; + + case CLASS : + case STRING : + values[i] = new Integer( data .readUnsignedShort() ); + break; + + case FIELDREF : + case METHODREF : + case INTERFACEMETHODREF : + case NAMEANDTYPE : + values[i] = new Integer( data .readInt() ); + break; + } + } + } + + +} +/** + * Provides a quick and dirty way to determine the true name of a class + * given just an InputStream. Reads in just enough to perform this + * minimal task only. + */ +public class ClassNameReader extends Object{ + + public static + String getClassName( InputStream input ) throws IOException { + DataInputStream data = new DataInputStream( input ); + // verify this is a valid class file. + int cookie = data .readInt(); + if ( cookie != 0xCAFEBABE ) { + return null; + } + int version = data .readInt(); + // read the constant pool. + ConstantPool constants = new ConstantPool( data ); + Object[] values = constants .values; + // read access flags and class index. + int accessFlags = data .readUnsignedShort(); + int classIndex = data .readUnsignedShort(); + Integer stringIndex = (Integer) values[classIndex]; + String className = (String) values[stringIndex .intValue()]; + return className; + } + + +} + + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java new file mode 100644 index 000000000..8afeb146a --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java @@ -0,0 +1,211 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.taskdefs.optional.jlink; + +import org.apache.tools.ant .*; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types .*; +import java.io.File; + +/** + * This class defines objects that can link together various jar and + * zip files. + * + *

    It is basically a wrapper for the jlink code written originally + * by Patrick Beard. The + * classes org.apache.tools.ant.taskdefs.optional.jlink.Jlink and + * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader + * support this class.

    + * + *

    For example: + * + *

    + * <jlink compress="false" outfile="out.jar"/>
    + *   <mergefiles>
    + *     <pathelement path="${build.dir}/mergefoo.jar"/>
    + *     <pathelement path="${build.dir}/mergebar.jar"/>
    + *   </mergefiles>
    + *   <addfiles>
    + *     <pathelement path="${build.dir}/mac.jar"/>
    + *     <pathelement path="${build.dir}/pc.zip"/>
    + *   </addfiles>
    + * </jlink>
    + * 
    + * + * + * @author Matthew Kuperus Heun */ +public class JlinkTask extends MatchingTask { + + /** + * The output file for this run of jlink. Usually a jar or zip file. + */ + public void setOutfile( File outfile ) { + this.outfile = outfile; + } + + /** + * Establishes the object that contains the files to + * be merged into the output. + */ + public Path createMergefiles() { + if ( this .mergefiles == null ) { + this .mergefiles = new Path(getProject()); + } + return this .mergefiles.createPath(); + } + + /** + * Sets the files to be merged into the output. + */ + public void setMergefiles( Path mergefiles ) { + if ( this .mergefiles == null ) { + this .mergefiles = mergefiles; + } + else { + this .mergefiles .append( mergefiles ); + } + } + + /** + * Establishes the object that contains the files to + * be added to the output. + */ + public Path createAddfiles() { + if ( this .addfiles == null ) { + this .addfiles = new Path(getProject()); + } + return this .addfiles .createPath(); + } + + /** + * Sets the files to be added into the output. + */ + public void setAddfiles( Path addfiles ) { + if ( this .addfiles == null ) { + this .addfiles = addfiles; + } + else { + this .addfiles .append( addfiles ); + } + } + + /** + * Defines whether or not the output should be compacted. + */ + public void setCompress( boolean compress ) { + this .compress = compress; + } + + /** + * Does the adding and merging. + */ + public void execute() throws BuildException { + //Be sure everything has been set. + if ( outfile == null ) { + throw new BuildException( "outfile attribute is required! Please set." ); + } + if (!haveAddFiles() && !haveMergeFiles()) { + throw new BuildException( "addfiles or mergefiles required! Please set." ); + } + log( "linking: " + outfile.getPath() ); + log( "compression: " + compress, Project.MSG_VERBOSE ); + jlink linker = new jlink(); + linker .setOutfile( outfile.getPath() ); + linker .setCompression( compress ); + if (haveMergeFiles()){ + log( "merge files: " + mergefiles .toString(), Project .MSG_VERBOSE ); + linker .addMergeFiles( mergefiles .list() ); + } + if (haveAddFiles()){ + log( "add files: " + addfiles .toString(), Project .MSG_VERBOSE ); + linker .addAddFiles( addfiles .list() ); + } + try { + linker .link(); + } catch( Exception ex ) { + throw new BuildException( ex, location ); + } + } + + private boolean haveAddFiles(){ + return haveEntries(addfiles); + } + + private boolean haveMergeFiles(){ + return haveEntries(mergefiles); + } + + private boolean haveEntries(Path p){ + if (p == null){ + return false; + } + if (p.size() > 0){ + return true; + } + return false; + } + + private File outfile = null; + + private Path mergefiles = null; + + private Path addfiles = null; + + private boolean compress = false; + + private String ps = System .getProperty( "path.separator" ); + +} + + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java b/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java new file mode 100644 index 000000000..899127b62 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java @@ -0,0 +1,420 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +/** + * jlink.java + * links together multiple .jar files + * + * Original code by Patrick Beard. Modifications to work + * with ANT by Matthew Kuperus Heun. + * + * @author Matthew Kuperus Heun + */ +package org.apache.tools.ant.taskdefs.optional.jlink; + +import java.io .*; +import java.util.zip .*; +import java.util .Vector; +import java.util .Enumeration; + +public class jlink extends Object{ + + /** + * The file that will be created by this instance of jlink. + */ + public void setOutfile( String outfile ) { + if ( outfile == null ) { + return ; + } + this .outfile = outfile; + } + + /** + * Adds a file to be merged into the output. + */ + public void addMergeFile( String mergefile ) { + if ( mergefile == null ) { + return ; + } + mergefiles .addElement( mergefile ); + } + + /** + * Adds a file to be added into the output. + */ + public void addAddFile( String addfile ) { + if ( addfile == null ) { + return ; + } + addfiles .addElement( addfile ); + } + + /** + * Adds several files to be merged into the output. + */ + public void addMergeFiles( String[] mergefiles ) { + if ( mergefiles == null ) { + return ; + } + for ( int i = 0; i < mergefiles .length; i++ ) { + addMergeFile( mergefiles[i] ); + } + } + + /** + * Adds several file to be added into the output. + */ + public void addAddFiles( String[] addfiles ) { + if ( addfiles == null ) { + return ; + } + for ( int i = 0; i < addfiles .length; i++ ) { + addAddFile( addfiles[i] ); + } + } + + /** + * Determines whether output will be compressed. + */ + public void setCompression( boolean compress ) { + this .compression = compress; + } + + /** + * Performs the linking of files. + * Addfiles are added to the output as-is. For example, a + * jar file is added to the output as a jar file. + * However, mergefiles are first examined for their type. + * If it is a jar or zip file, the contents will be extracted + * from the mergefile and entered into the output. + * If a zip or jar file is encountered in a subdirectory + * it will be added, not merged. + * If a directory is encountered, it becomes the root + * entry of all the files below it. Thus, you can + * provide multiple, disjoint directories, as + * addfiles: they will all be added in a rational + * manner to outfile. + */ + public void link() throws Exception { + ZipOutputStream output = new ZipOutputStream( new FileOutputStream( outfile ) ); + if ( compression ) { + output .setMethod( ZipOutputStream .DEFLATED ); + output .setLevel( Deflater .DEFAULT_COMPRESSION ); + } else { + output .setMethod( ZipOutputStream .STORED ); + } + Enumeration merges = mergefiles .elements(); + while ( merges .hasMoreElements() ) { + String path = (String) merges .nextElement(); + File f = new File( path ); + if ( f.getName().endsWith( ".jar" ) || f.getName().endsWith( ".zip" ) ) { + //Do the merge + mergeZipJarContents( output, f ); + } + else { + //Add this file to the addfiles Vector and add it later at the top level of the output file. + addAddFile( path ); + } + } + Enumeration adds = addfiles .elements(); + while ( adds .hasMoreElements() ) { + String name = (String) adds .nextElement(); + File f = new File( name ); + if ( f .isDirectory() ) { + //System.out.println("in jlink: adding directory contents of " + f.getPath()); + addDirContents( output, f, f.getName() + '/', compression ); + } + else { + addFile( output, f, "", compression ); + } + } + if ( output != null ) { + try { + output .close(); + } catch( IOException ioe ) {} + } + } + + public static void main( String[] args ) { + // jlink output input1 ... inputN + if ( args .length < 2 ) { + System .out .println( "usage: jlink output input1 ... inputN" ); + System .exit( 1 ); + } + jlink linker = new jlink(); + linker .setOutfile( args[0] ); + //To maintain compatibility with the command-line version, we will only add files to be merged. + for ( int i = 1; i < args .length; i++ ) { + linker .addMergeFile( args[i] ); + } + try { + linker .link(); + } catch( Exception ex ) { + System .err .print( ex .getMessage() ); + } + } + + /* + * Actually performs the merging of f into the output. + * f should be a zip or jar file. + */ + private void mergeZipJarContents( ZipOutputStream output, File f ) throws IOException { + //Check to see that the file with name "name" exists. + if ( ! f .exists() ) { + return ; + } + ZipFile zipf = new ZipFile( f ); + Enumeration entries = zipf.entries(); + while (entries.hasMoreElements()){ + ZipEntry inputEntry = (ZipEntry) entries.nextElement(); + //Ignore manifest entries. They're bound to cause conflicts between + //files that are being merged. User should supply their own + //manifest file when doing the merge. + String inputEntryName = inputEntry.getName(); + int index = inputEntryName.indexOf("META-INF"); + if (index < 0){ + //META-INF not found in the name of the entry. Go ahead and process it. + try { + output.putNextEntry(processEntry(zipf, inputEntry)); + } catch (ZipException ex){ + //If we get here, it could be because we are trying to put a + //directory entry that already exists. + //For example, we're trying to write "com", but a previous + //entry from another mergefile was called "com". + //In that case, just ignore the error and go on to the + //next entry. + String mess = ex.getMessage(); + if (mess.indexOf("duplicate") > 0){ + //It was the duplicate entry. + continue; + } else { + //I hate to admit it, but we don't know what happened here. Throw the Exception. + throw ex; + } + } + InputStream in = zipf.getInputStream(inputEntry); + int len = buffer.length; + int count = -1; + while ((count = in.read(buffer, 0, len)) > 0){ + output.write(buffer, 0, count); + } + in.close(); + output.closeEntry(); + } + } + zipf .close(); + } + + /* + * Adds contents of a directory to the output. + */ + private void addDirContents( ZipOutputStream output, File dir, String prefix, boolean compress ) throws IOException { + String[] contents = dir .list(); + for ( int i = 0; i < contents .length; ++i ) { + String name = contents[i]; + File file = new File( dir, name ); + if ( file .isDirectory() ) { + addDirContents( output, file, prefix + name + '/', compress ); + } + else { + addFile( output, file, prefix, compress ); + } + } + } + + /* + * Gets the name of an entry in the file. This is the real name + * which for a class is the name of the package with the class + * name appended. + */ + private String getEntryName( File file, String prefix ) { + String name = file .getName(); + if ( ! name .endsWith( ".class" ) ) { + // see if the file is in fact a .class file, and determine its actual name. + try { + InputStream input = new FileInputStream( file ); + String className = ClassNameReader .getClassName( input ); + input .close(); + if ( className != null ) { + return className .replace( '.', '/' ) + ".class"; + } + } catch( IOException ioe ) {} + } + System.out.println("From " + file.getPath() + " and prefix " + prefix + ", creating entry " + prefix+name); + return (prefix + name); + } + + /* + * Adds a file to the output stream. + */ + private void addFile( ZipOutputStream output, File file, String prefix, boolean compress) throws IOException { + //Make sure file exists + long checksum = 0; + if ( ! file .exists() ) { + return ; + } + ZipEntry entry = new ZipEntry( getEntryName( file, prefix ) ); + entry .setTime( file .lastModified() ); + entry .setSize( file .length() ); + if (! compress){ + entry.setCrc(calcChecksum(file)); + } + FileInputStream input = new FileInputStream( file ); + addToOutputStream(output, input, entry); + } + + /* + * A convenience method that several other methods might call. + */ + private void addToOutputStream(ZipOutputStream output, InputStream input, ZipEntry ze) throws IOException{ + try { + output.putNextEntry(ze); + } catch (ZipException zipEx) { + //This entry already exists. So, go with the first one. + input.close(); + return; + } + int numBytes = -1; + while((numBytes = input.read(buffer)) > 0){ + output.write(buffer, 0, numBytes); + } + output.closeEntry(); + input.close(); + } + + /* + * A method that does the work on a given entry in a mergefile. + * The big deal is to set the right parameters in the ZipEntry + * on the output stream. + */ + private ZipEntry processEntry( ZipFile zip, ZipEntry inputEntry ) throws IOException{ + /* + First, some notes. + On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the + ZipInputStream does not work for compressed (deflated) files. Those calls return -1. + For uncompressed (stored) files, those calls do work. + However, using ZipFile.getEntries() works for both compressed and + uncompressed files. + + Now, from some simple testing I did, it seems that the value of CRC-32 is + independent of the compression setting. So, it should be easy to pass this + information on to the output entry. + */ + String name = inputEntry .getName(); + if ( ! (inputEntry .isDirectory() || name .endsWith( ".class" )) ) { + try { + InputStream input = zip.getInputStream( zip .getEntry( name ) ); + String className = ClassNameReader .getClassName( input ); + input .close(); + if ( className != null ) { + name = className .replace( '.', '/' ) + ".class"; + } + } catch( IOException ioe ) {} + } + ZipEntry outputEntry = new ZipEntry( name ); + outputEntry.setTime(inputEntry .getTime() ); + outputEntry.setExtra(inputEntry.getExtra()); + outputEntry.setComment(inputEntry.getComment()); + outputEntry.setTime(inputEntry.getTime()); + if (compression){ + outputEntry.setMethod(ZipEntry.DEFLATED); + //Note, don't need to specify size or crc for compressed files. + } else { + outputEntry.setMethod(ZipEntry.STORED); + outputEntry.setCrc(inputEntry.getCrc()); + outputEntry.setSize(inputEntry.getSize()); + } + return outputEntry; + } + + /* + * Necessary in the case where you add a entry that + * is not compressed. + */ + private long calcChecksum(File f) throws IOException { + BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); + return calcChecksum(in, f.length()); + } + + /* + * Necessary in the case where you add a entry that + * is not compressed. + */ + private long calcChecksum(InputStream in, long size) throws IOException{ + CRC32 crc = new CRC32(); + int len = buffer.length; + int count = -1; + int haveRead = 0; + while((count=in.read(buffer, 0, len)) > 0){ + haveRead += count; + crc.update(buffer, 0, count); + } + in.close(); + return crc.getValue(); + } + + private String outfile = null; + + private Vector mergefiles = new Vector( 10 ); + + private Vector addfiles = new Vector( 10 ); + + private boolean compression = false; + + byte[] buffer = new byte[8192]; + +} + +