1 | /*
|
---|
2 | * Copyright 2000-2005 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 |
|
---|
18 | package org.apache.tools.ant.taskdefs.optional;
|
---|
19 |
|
---|
20 | import java.io.File;
|
---|
21 | import java.io.FileOutputStream;
|
---|
22 | import java.io.IOException;
|
---|
23 | import java.io.OutputStream;
|
---|
24 | import java.io.PrintWriter;
|
---|
25 | import java.util.Enumeration;
|
---|
26 | import java.util.Vector;
|
---|
27 | import org.apache.tools.ant.BuildException;
|
---|
28 | import org.apache.tools.ant.DirectoryScanner;
|
---|
29 | import org.apache.tools.ant.Project;
|
---|
30 | import org.apache.tools.ant.taskdefs.ExecTask;
|
---|
31 | import org.apache.tools.ant.taskdefs.Execute;
|
---|
32 | import org.apache.tools.ant.taskdefs.LogOutputStream;
|
---|
33 | import org.apache.tools.ant.taskdefs.MatchingTask;
|
---|
34 | import org.apache.tools.ant.taskdefs.StreamPumper;
|
---|
35 | import org.apache.tools.ant.taskdefs.condition.Os;
|
---|
36 | import org.apache.tools.ant.types.FileSet;
|
---|
37 | import org.apache.tools.ant.util.FileUtils;
|
---|
38 |
|
---|
39 |
|
---|
40 | /**
|
---|
41 | * Create a CAB archive.
|
---|
42 | *
|
---|
43 | */
|
---|
44 |
|
---|
45 | public class Cab extends MatchingTask {
|
---|
46 |
|
---|
47 | private File cabFile;
|
---|
48 | private File baseDir;
|
---|
49 | private Vector filesets = new Vector();
|
---|
50 | private boolean doCompress = true;
|
---|
51 | private boolean doVerbose = false;
|
---|
52 | private String cmdOptions;
|
---|
53 |
|
---|
54 | protected String archiveType = "cab";
|
---|
55 |
|
---|
56 | private FileUtils fileUtils = FileUtils.newFileUtils();
|
---|
57 |
|
---|
58 | /**
|
---|
59 | * The name/location of where to create the .cab file.
|
---|
60 | */
|
---|
61 | public void setCabfile(File cabFile) {
|
---|
62 | this.cabFile = cabFile;
|
---|
63 | }
|
---|
64 |
|
---|
65 | /**
|
---|
66 | * Base directory to look in for files to CAB.
|
---|
67 | */
|
---|
68 | public void setBasedir(File baseDir) {
|
---|
69 | this.baseDir = baseDir;
|
---|
70 | }
|
---|
71 |
|
---|
72 | /**
|
---|
73 | * If true, compress the files otherwise only store them.
|
---|
74 | */
|
---|
75 | public void setCompress(boolean compress) {
|
---|
76 | doCompress = compress;
|
---|
77 | }
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * If true, display cabarc output.
|
---|
81 | */
|
---|
82 | public void setVerbose(boolean verbose) {
|
---|
83 | doVerbose = verbose;
|
---|
84 | }
|
---|
85 |
|
---|
86 | /**
|
---|
87 | * Sets additional cabarc options that are not supported directly.
|
---|
88 | */
|
---|
89 | public void setOptions(String options) {
|
---|
90 | cmdOptions = options;
|
---|
91 | }
|
---|
92 |
|
---|
93 | /**
|
---|
94 | * Adds a set of files to archive.
|
---|
95 | */
|
---|
96 | public void addFileset(FileSet set) {
|
---|
97 | filesets.addElement(set);
|
---|
98 | }
|
---|
99 |
|
---|
100 | /*
|
---|
101 | * I'm not fond of this pattern: "sub-method expected to throw
|
---|
102 | * task-cancelling exceptions". It feels too much like programming
|
---|
103 | * for side-effects to me...
|
---|
104 | */
|
---|
105 | protected void checkConfiguration() throws BuildException {
|
---|
106 | if (baseDir == null && filesets.size() == 0) {
|
---|
107 | throw new BuildException("basedir attribute or at least one "
|
---|
108 | + "nested filest is required!",
|
---|
109 | getLocation());
|
---|
110 | }
|
---|
111 | if (baseDir != null && !baseDir.exists()) {
|
---|
112 | throw new BuildException("basedir does not exist!", getLocation());
|
---|
113 | }
|
---|
114 | if (cabFile == null) {
|
---|
115 | throw new BuildException("cabfile attribute must be set!",
|
---|
116 | getLocation());
|
---|
117 | }
|
---|
118 | }
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Create a new exec delegate. The delegate task is populated so that
|
---|
122 | * it appears in the logs to be the same task as this one.
|
---|
123 | */
|
---|
124 | protected ExecTask createExec() throws BuildException {
|
---|
125 | ExecTask exec = (ExecTask) getProject().createTask("exec");
|
---|
126 | exec.setOwningTarget(this.getOwningTarget());
|
---|
127 | exec.setTaskName(this.getTaskName());
|
---|
128 | exec.setDescription(this.getDescription());
|
---|
129 |
|
---|
130 | return exec;
|
---|
131 | }
|
---|
132 |
|
---|
133 | /**
|
---|
134 | * Check to see if the target is up to date with respect to input files.
|
---|
135 | * @return true if the cab file is newer than its dependents.
|
---|
136 | */
|
---|
137 | protected boolean isUpToDate(Vector files) {
|
---|
138 | boolean upToDate = true;
|
---|
139 | for (int i = 0; i < files.size() && upToDate; i++) {
|
---|
140 | String file = files.elementAt(i).toString();
|
---|
141 | if (fileUtils.resolveFile(baseDir, file).lastModified()
|
---|
142 | > cabFile.lastModified()) {
|
---|
143 | upToDate = false;
|
---|
144 | }
|
---|
145 | }
|
---|
146 | return upToDate;
|
---|
147 | }
|
---|
148 |
|
---|
149 | /**
|
---|
150 | * Creates a list file. This temporary file contains a list of all files
|
---|
151 | * to be included in the cab, one file per line.
|
---|
152 | *
|
---|
153 | * <p>This method expects to only be called on Windows and thus
|
---|
154 | * quotes the file names.</p>
|
---|
155 | */
|
---|
156 | protected File createListFile(Vector files)
|
---|
157 | throws IOException {
|
---|
158 | File listFile = fileUtils.createTempFile("ant", "", null);
|
---|
159 | listFile.deleteOnExit();
|
---|
160 |
|
---|
161 | PrintWriter writer = new PrintWriter(new FileOutputStream(listFile));
|
---|
162 |
|
---|
163 | int size = files.size();
|
---|
164 | for (int i = 0; i < size; i++) {
|
---|
165 | writer.println('\"' + files.elementAt(i).toString() + '\"');
|
---|
166 | }
|
---|
167 | writer.close();
|
---|
168 |
|
---|
169 | return listFile;
|
---|
170 | }
|
---|
171 |
|
---|
172 | /**
|
---|
173 | * Append all files found by a directory scanner to a vector.
|
---|
174 | */
|
---|
175 | protected void appendFiles(Vector files, DirectoryScanner ds) {
|
---|
176 | String[] dsfiles = ds.getIncludedFiles();
|
---|
177 |
|
---|
178 | for (int i = 0; i < dsfiles.length; i++) {
|
---|
179 | files.addElement(dsfiles[i]);
|
---|
180 | }
|
---|
181 | }
|
---|
182 |
|
---|
183 | /**
|
---|
184 | * Get the complete list of files to be included in the cab. Filenames
|
---|
185 | * are gathered from filesets if any have been added, otherwise from the
|
---|
186 | * traditional include parameters.
|
---|
187 | */
|
---|
188 | protected Vector getFileList() throws BuildException {
|
---|
189 | Vector files = new Vector();
|
---|
190 |
|
---|
191 | if (baseDir != null) {
|
---|
192 | // get files from old methods - includes and nested include
|
---|
193 | appendFiles(files, super.getDirectoryScanner(baseDir));
|
---|
194 | }
|
---|
195 |
|
---|
196 | // get files from filesets
|
---|
197 | for (int i = 0; i < filesets.size(); i++) {
|
---|
198 | FileSet fs = (FileSet) filesets.elementAt(i);
|
---|
199 | if (fs != null) {
|
---|
200 | appendFiles(files, fs.getDirectoryScanner(getProject()));
|
---|
201 | }
|
---|
202 | }
|
---|
203 |
|
---|
204 | return files;
|
---|
205 | }
|
---|
206 |
|
---|
207 | public void execute() throws BuildException {
|
---|
208 |
|
---|
209 | checkConfiguration();
|
---|
210 |
|
---|
211 | Vector files = getFileList();
|
---|
212 |
|
---|
213 | // quick exit if the target is up to date
|
---|
214 | if (isUpToDate(files)) {
|
---|
215 | return;
|
---|
216 | }
|
---|
217 |
|
---|
218 | log("Building " + archiveType + ": " + cabFile.getAbsolutePath());
|
---|
219 |
|
---|
220 | if (!Os.isFamily("windows")) {
|
---|
221 | log("Using listcab/libcabinet", Project.MSG_VERBOSE);
|
---|
222 |
|
---|
223 | StringBuffer sb = new StringBuffer();
|
---|
224 |
|
---|
225 | Enumeration fileEnum = files.elements();
|
---|
226 |
|
---|
227 | while (fileEnum.hasMoreElements()) {
|
---|
228 | sb.append(fileEnum.nextElement()).append("\n");
|
---|
229 | }
|
---|
230 | sb.append("\n").append(cabFile.getAbsolutePath()).append("\n");
|
---|
231 |
|
---|
232 | try {
|
---|
233 | Process p = Execute.launch(getProject(),
|
---|
234 | new String[] {"listcab"}, null,
|
---|
235 | baseDir != null ? baseDir
|
---|
236 | : getProject().getBaseDir(),
|
---|
237 | true);
|
---|
238 | OutputStream out = p.getOutputStream();
|
---|
239 |
|
---|
240 | // Create the stream pumpers to forward listcab's stdout and stderr to the log
|
---|
241 | // note: listcab is an interactive program, and issues prompts for every new line.
|
---|
242 | // Therefore, make it show only with verbose logging turned on.
|
---|
243 | LogOutputStream outLog = new LogOutputStream(this, Project.MSG_VERBOSE);
|
---|
244 | LogOutputStream errLog = new LogOutputStream(this, Project.MSG_ERR);
|
---|
245 | StreamPumper outPump = new StreamPumper(p.getInputStream(), outLog);
|
---|
246 | StreamPumper errPump = new StreamPumper(p.getErrorStream(), errLog);
|
---|
247 |
|
---|
248 | // Pump streams asynchronously
|
---|
249 | (new Thread(outPump)).start();
|
---|
250 | (new Thread(errPump)).start();
|
---|
251 |
|
---|
252 | out.write(sb.toString().getBytes());
|
---|
253 | out.flush();
|
---|
254 | out.close();
|
---|
255 |
|
---|
256 | int result = -99; // A wild default for when the thread is interrupted
|
---|
257 |
|
---|
258 | try {
|
---|
259 | // Wait for the process to finish
|
---|
260 | result = p.waitFor();
|
---|
261 |
|
---|
262 | // Wait for the end of output and error streams
|
---|
263 | outPump.waitFor();
|
---|
264 | outLog.close();
|
---|
265 | errPump.waitFor();
|
---|
266 | errLog.close();
|
---|
267 | } catch (InterruptedException ie) {
|
---|
268 | log("Thread interrupted: " + ie);
|
---|
269 | }
|
---|
270 |
|
---|
271 | // Informative summary message in case of errors
|
---|
272 | if (Execute.isFailure(result)) {
|
---|
273 | log("Error executing listcab; error code: " + result);
|
---|
274 | }
|
---|
275 | } catch (IOException ex) {
|
---|
276 | String msg = "Problem creating " + cabFile + " " + ex.getMessage();
|
---|
277 | throw new BuildException(msg, getLocation());
|
---|
278 | }
|
---|
279 | } else {
|
---|
280 | try {
|
---|
281 | File listFile = createListFile(files);
|
---|
282 | ExecTask exec = createExec();
|
---|
283 | File outFile = null;
|
---|
284 |
|
---|
285 | // die if cabarc fails
|
---|
286 | exec.setFailonerror(true);
|
---|
287 | exec.setDir(baseDir);
|
---|
288 |
|
---|
289 | if (!doVerbose) {
|
---|
290 | outFile = fileUtils.createTempFile("ant", "", null);
|
---|
291 | outFile.deleteOnExit();
|
---|
292 | exec.setOutput(outFile);
|
---|
293 | }
|
---|
294 |
|
---|
295 | exec.setExecutable("cabarc");
|
---|
296 | exec.createArg().setValue("-r");
|
---|
297 | exec.createArg().setValue("-p");
|
---|
298 |
|
---|
299 | if (!doCompress) {
|
---|
300 | exec.createArg().setValue("-m");
|
---|
301 | exec.createArg().setValue("none");
|
---|
302 | }
|
---|
303 |
|
---|
304 | if (cmdOptions != null) {
|
---|
305 | exec.createArg().setLine(cmdOptions);
|
---|
306 | }
|
---|
307 |
|
---|
308 | exec.createArg().setValue("n");
|
---|
309 | exec.createArg().setFile(cabFile);
|
---|
310 | exec.createArg().setValue("@" + listFile.getAbsolutePath());
|
---|
311 |
|
---|
312 | exec.execute();
|
---|
313 |
|
---|
314 | if (outFile != null) {
|
---|
315 | outFile.delete();
|
---|
316 | }
|
---|
317 |
|
---|
318 | listFile.delete();
|
---|
319 | } catch (IOException ioe) {
|
---|
320 | String msg = "Problem creating " + cabFile + " " + ioe.getMessage();
|
---|
321 | throw new BuildException(msg, getLocation());
|
---|
322 | }
|
---|
323 | }
|
---|
324 | }
|
---|
325 | }
|
---|