Make all files in our JAR have the same timestamp
[egit.git] / org.spearce.jgit.pgm / src / org / spearce / jgit / pgm / build / JarLinkUtil.java
blob929ee55639b42735c9ddb4f7df01cf7e03e24420
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.pgm.build;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.util.ArrayList;
45 import java.util.Enumeration;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.zip.ZipEntry;
50 import java.util.zip.ZipFile;
51 import java.util.zip.ZipOutputStream;
53 import org.kohsuke.args4j.CmdLineException;
54 import org.kohsuke.args4j.CmdLineParser;
55 import org.kohsuke.args4j.Option;
56 import org.kohsuke.args4j.spi.MapOptionHandler;
58 /**
59 * Combines multiple JAR and directory sources into a single JAR file.
60 * <p>
61 * This is a crude command line utility to combine multiple JAR files into a
62 * single JAR file, without first needing to unpack the individual JARs.
63 * <p>
64 * The output ZIP stream is sent to standard out and can be redirected onto the
65 * end of a shell script which starts the JRE.
67 public class JarLinkUtil {
68 /**
69 * Combine multiple JARs.
71 * @param argv
72 * the command line arguments indicating the files to pack.
73 * @throws IOException
74 * a source file could not be read.
76 public static void main(final String[] argv) throws IOException {
77 final JarLinkUtil util = new JarLinkUtil();
78 final CmdLineParser clp = new CmdLineParser(util);
79 try {
80 clp.parseArgument(argv);
81 } catch (CmdLineException e) {
82 clp.printSingleLineUsage(System.err);
83 System.exit(1);
85 util.run();
88 @Option(name = "-include", required = true)
89 private List<File> includes = new ArrayList<File>();
91 @Option(name = "-file", handler = MapOptionHandler.class)
92 private Map<String, String> files = new HashMap<String, String>();
94 private final Map<String, File> chosenSources = new HashMap<String, File>();
96 private long creationTime;
98 private ZipOutputStream zos;
100 private JarLinkUtil() {
101 // Command line utility only.
104 private void run() throws IOException {
105 for (final File src : includes) {
106 if (src.isFile())
107 scanJar(src);
108 else
109 scanDirectory(src, src, "");
111 for (final Map.Entry<String, String> e : files.entrySet())
112 chosenSources.put(e.getKey(), new File(e.getValue()));
114 creationTime = System.currentTimeMillis();
115 zos = new ZipOutputStream(System.out);
116 zos.setLevel(9);
118 for (final File src : includes) {
119 if (src.isFile())
120 appendJar(src);
121 else
122 appendDirectory(src, src, "");
124 for (final String name : files.keySet())
125 appendFile(chosenSources.get(name), name);
127 zos.close();
130 private void scanJar(final File jarPath) throws IOException {
131 final ZipFile zf = new ZipFile(jarPath);
132 final Enumeration<? extends ZipEntry> e = zf.entries();
133 while (e.hasMoreElements())
134 chosenSources.put(e.nextElement().getName(), jarPath);
135 zf.close();
138 private void scanDirectory(final File rootPath, final File dirPath,
139 final String pfx) throws IOException {
140 final File[] entries = dirPath.listFiles();
141 if (entries == null)
142 return;
143 for (final File e : entries) {
144 if (e.getName().equals(".") || e.getName().equals(".."))
145 continue;
147 if (e.isDirectory())
148 scanDirectory(rootPath, e, pfx + e.getName() + "/");
149 else
150 chosenSources.put(pfx + e.getName(), rootPath);
154 private void appendJar(final File jarPath) throws IOException {
155 final ZipFile zf = new ZipFile(jarPath);
156 final Enumeration<? extends ZipEntry> e = zf.entries();
157 while (e.hasMoreElements()) {
158 final ZipEntry ze = e.nextElement();
159 final String name = ze.getName();
160 if (chosenSources.get(name) == jarPath)
161 appendEntry(name, ze.getSize(), ze.getTime(), zf
162 .getInputStream(ze));
164 zf.close();
167 private void appendDirectory(final File rootDir, final File dirPath,
168 final String pfx) throws IOException {
169 final File[] entries = dirPath.listFiles();
170 if (entries == null)
171 return;
172 for (final File e : entries) {
173 if (e.getName().equals(".") || e.getName().equals(".."))
174 continue;
176 if (e.isDirectory())
177 appendDirectory(rootDir, e, pfx + e.getName() + "/");
178 else if (chosenSources.get(pfx + e.getName()) == rootDir)
179 appendFile(e, pfx + e.getName());
183 private void appendFile(final File path, final String name)
184 throws IOException {
185 final long len = path.length();
186 final InputStream is = new FileInputStream(path);
187 appendEntry(name, len, creationTime, is);
190 private void appendEntry(final String name, final long len,
191 final long time, final InputStream is) throws IOException {
192 final ZipEntry ze = new ZipEntry(name);
193 ze.setSize(len);
194 ze.setTime(time);
195 zos.putNextEntry(ze);
196 try {
197 final byte[] buf = new byte[4096];
198 int n;
199 while ((n = is.read(buf)) >= 0)
200 zos.write(buf, 0, n);
201 } finally {
202 is.close();
204 zos.closeEntry();