| 1 |
/*
|
| 2 |
* Licensed to the Apache Software Foundation (ASF) under one or more
|
| 3 |
* contributor license agreements. See the NOTICE file distributed with
|
| 4 |
* this work for additional information regarding copyright ownership.
|
| 5 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
| 6 |
* (the "License"); you may not use this file except in compliance with
|
| 7 |
* the License. You may obtain a copy of the License at
|
| 8 |
*
|
| 9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
| 10 |
*
|
| 11 |
* Unless required by applicable law or agreed to in writing, software
|
| 12 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 |
* See the License for the specific language governing permissions and
|
| 15 |
* limitations under the License.
|
| 16 |
*
|
| 17 |
*/
|
| 18 |
|
| 19 |
package org.apache.tools.ant.taskdefs;
|
| 20 |
|
| 21 |
import java.io.BufferedReader;
|
| 22 |
import java.io.BufferedWriter;
|
| 23 |
import java.io.ByteArrayOutputStream;
|
| 24 |
import java.io.File;
|
| 25 |
import java.io.FileWriter;
|
| 26 |
import java.io.IOException;
|
| 27 |
import java.io.OutputStream;
|
| 28 |
import java.io.StringReader;
|
| 29 |
import java.util.HashMap;
|
| 30 |
import java.util.Iterator;
|
| 31 |
import java.util.Vector;
|
| 32 |
|
| 33 |
import org.apache.tools.ant.BuildException;
|
| 34 |
import org.apache.tools.ant.MagicNames;
|
| 35 |
import org.apache.tools.ant.Project;
|
| 36 |
import org.apache.tools.ant.Task;
|
| 37 |
import org.apache.tools.ant.taskdefs.condition.Os;
|
| 38 |
import org.apache.tools.ant.types.Commandline;
|
| 39 |
import org.apache.tools.ant.util.FileUtils;
|
| 40 |
import org.apache.tools.ant.util.StringUtils;
|
| 41 |
|
| 42 |
/**
|
| 43 |
* Runs an external program.
|
| 44 |
*
|
| 45 |
* @since Ant 1.2
|
| 46 |
*
|
| 47 |
*/
|
| 48 |
public class Execute {
|
| 49 |
|
| 50 |
private static final int ONE_SECOND = 1000;
|
| 51 |
|
| 52 |
/** Invalid exit code.
|
| 53 |
* set to {@link Integer#MAX_VALUE}
|
| 54 |
*/
|
| 55 |
public static final int INVALID = Integer.MAX_VALUE;
|
| 56 |
|
| 57 |
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
|
| 58 |
|
| 59 |
private String[] cmdl = null;
|
| 60 |
private String[] env = null;
|
| 61 |
private int exitValue = INVALID;
|
| 62 |
private ExecuteStreamHandler streamHandler;
|
| 63 |
private ExecuteWatchdog watchdog;
|
| 64 |
private File workingDirectory = null;
|
| 65 |
private Project project = null;
|
| 66 |
private boolean newEnvironment = false;
|
| 67 |
|
| 68 |
/** Controls whether the VM is used to launch commands, where possible. */
|
| 69 |
private boolean useVMLauncher = true;
|
| 70 |
|
| 71 |
private static String antWorkingDirectory = System.getProperty("user.dir");
|
| 72 |
private static CommandLauncher vmLauncher = null;
|
| 73 |
private static CommandLauncher shellLauncher = null;
|
| 74 |
private static Vector procEnvironment = null;
|
| 75 |
|
| 76 |
/** Used to destroy processes when the VM exits. */
|
| 77 |
private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
|
| 78 |
|
| 79 |
/** Used for replacing env variables */
|
| 80 |
private static boolean environmentCaseInSensitive = false;
|
| 81 |
|
| 82 |
/*
|
| 83 |
* Builds a command launcher for the OS and JVM we are running under.
|
| 84 |
*/
|
| 85 |
static {
|
| 86 |
// Try using a JDK 1.3 launcher
|
| 87 |
try {
|
| 88 |
if (!Os.isFamily("os/2")) {
|
| 89 |
vmLauncher = new Java13CommandLauncher();
|
| 90 |
}
|
| 91 |
} catch (NoSuchMethodException exc) {
|
| 92 |
// Ignore and keep trying
|
| 93 |
}
|
| 94 |
if (Os.isFamily("mac") && !Os.isFamily("unix")) {
|
| 95 |
// Mac
|
| 96 |
shellLauncher = new MacCommandLauncher(new CommandLauncher());
|
| 97 |
} else if (Os.isFamily("os/2")) {
|
| 98 |
// OS/2
|
| 99 |
shellLauncher = new OS2CommandLauncher(new CommandLauncher());
|
| 100 |
} else if (Os.isFamily("windows")) {
|
| 101 |
environmentCaseInSensitive = true;
|
| 102 |
CommandLauncher baseLauncher = new CommandLauncher();
|
| 103 |
|
| 104 |
if (!Os.isFamily("win9x")) {
|
| 105 |
// Windows XP/2000/NT
|
| 106 |
shellLauncher = new WinNTCommandLauncher(baseLauncher);
|
| 107 |
} else {
|
| 108 |
// Windows 98/95 - need to use an auxiliary script
|
| 109 |
shellLauncher
|
| 110 |
= new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
|
| 111 |
}
|
| 112 |
} else if (Os.isFamily("netware")) {
|
| 113 |
|
| 114 |
CommandLauncher baseLauncher = new CommandLauncher();
|
| 115 |
|
| 116 |
shellLauncher
|
| 117 |
= new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
|
| 118 |
} else if (Os.isFamily("openvms")) {
|
| 119 |
// OpenVMS
|
| 120 |
try {
|
| 121 |
shellLauncher = new VmsCommandLauncher();
|
| 122 |
} catch (NoSuchMethodException exc) {
|
| 123 |
// Ignore and keep trying
|
| 124 |
}
|
| 125 |
} else {
|
| 126 |
// Generic
|
| 127 |
shellLauncher = new ScriptCommandLauncher("bin/antRun",
|
| 128 |
new CommandLauncher());
|
| 129 |
}
|
| 130 |
}
|
| 131 |
|
| 132 |
/**
|
| 133 |
* Set whether or not you want the process to be spawned.
|
| 134 |
* Default is not spawned.
|
| 135 |
*
|
| 136 |
* @param spawn if true you do not want Ant
|
| 137 |
* to wait for the end of the process.
|
| 138 |
* Has no influence in here, the calling task contains
|
| 139 |
* and acts accordingly
|
| 140 |
*
|
| 141 |
* @since Ant 1.6
|
| 142 |
* @deprecated
|
| 143 |
*/
|
| 144 |
public void setSpawn(boolean spawn) {
|
| 145 |
// Method did not do anything to begin with
|
| 146 |
}
|
| 147 |
|
| 148 |
/**
|
| 149 |
* Find the list of environment variables for this process.
|
| 150 |
*
|
| 151 |
* @return a vector containing the environment variables.
|
| 152 |
* The vector elements are strings formatted like variable = value.
|
| 153 |
*/
|
| 154 |
public static synchronized Vector getProcEnvironment() {
|
| 155 |
if (procEnvironment != null) {
|
| 156 |
return procEnvironment;
|
| 157 |
}
|
| 158 |
procEnvironment = new Vector();
|
| 159 |
try {
|
| 160 |
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
| 161 |
Execute exe = new Execute(new PumpStreamHandler(out));
|
| 162 |
exe.setCommandline(getProcEnvCommand());
|
| 163 |
// Make sure we do not recurse forever
|
| 164 |
exe.setNewenvironment(true);
|
| 165 |
int retval = exe.execute();
|
| 166 |
if (retval != 0) {
|
| 167 |
// Just try to use what we got
|
| 168 |
}
|
| 169 |
BufferedReader in =
|
| 170 |
new BufferedReader(new StringReader(toString(out)));
|
| 171 |
|
| 172 |
if (Os.isFamily("openvms")) {
|
| 173 |
procEnvironment = addVMSLogicals(procEnvironment, in);
|
| 174 |
return procEnvironment;
|
| 175 |
}
|
| 176 |
String var = null;
|
| 177 |
String line, lineSep = StringUtils.LINE_SEP;
|
| 178 |
while ((line = in.readLine()) != null) {
|
| 179 |
if (line.indexOf('=') == -1) {
|
| 180 |
// Chunk part of previous env var (UNIX env vars can
|
| 181 |
// contain embedded new lines).
|
| 182 |
if (var == null) {
|
| 183 |
var = lineSep + line;
|
| 184 |
} else {
|
| 185 |
var += lineSep + line;
|
| 186 |
}
|
| 187 |
} else {
|
| 188 |
// New env var...append the previous one if we have it.
|
| 189 |
if (var != null) {
|
| 190 |
procEnvironment.addElement(var);
|
| 191 |
}
|
| 192 |
var = line;
|
| 193 |
}
|
| 194 |
}
|
| 195 |
// Since we "look ahead" before adding, there's one last env var.
|
| 196 |
if (var != null) {
|
| 197 |
procEnvironment.addElement(var);
|
| 198 |
}
|
| 199 |
} catch (java.io.IOException exc) {
|
| 200 |
exc.printStackTrace();
|
| 201 |
// Just try to see how much we got
|
| 202 |
}
|
| 203 |
return procEnvironment;
|
| 204 |
}
|
| 205 |
|
| 206 |
/**
|
| 207 |
* This is the operation to get our environment.
|
| 208 |
* It is a notorious troublespot pre-Java1.5, and should be approached
|
| 209 |
* with extreme caution.
|
| 210 |
* @return
|
| 211 |
*/
|
| 212 |
private static String[] getProcEnvCommand() {
|
| 213 |
if (Os.isFamily("os/2")) {
|
| 214 |
// OS/2 - use same mechanism as Windows 2000
|
| 215 |
return new String[] {"cmd", "/c", "set" };
|
| 216 |
} else if (Os.isFamily("windows")) {
|
| 217 |
// Determine if we're running under XP/2000/NT or 98/95
|
| 218 |
if (Os.isFamily("win9x")) {
|
| 219 |
// Windows 98/95
|
| 220 |
return new String[] {"command.com", "/c", "set" };
|
| 221 |
} else {
|
| 222 |
// Windows XP/2000/NT/2003
|
| 223 |
return new String[] {"cmd", "/c", "set" };
|
| 224 |
}
|
| 225 |
} else if (Os.isFamily("z/os") || Os.isFamily("unix")) {
|
| 226 |
// On most systems one could use: /bin/sh -c env
|
| 227 |
|
| 228 |
// Some systems have /bin/env, others /usr/bin/env, just try
|
| 229 |
String[] cmd = new String[1];
|
| 230 |
if (new File("/bin/env").canRead()) {
|
| 231 |
cmd[0] = "/bin/env";
|
| 232 |
} else if (new File("/usr/bin/env").canRead()) {
|
| 233 |
cmd[0] = "/usr/bin/env";
|
| 234 |
} else {
|
| 235 |
// rely on PATH
|
| 236 |
cmd[0] = "env";
|
| 237 |
}
|
| 238 |
return cmd;
|
| 239 |
} else if (Os.isFamily("netware") || Os.isFamily("os/400")) {
|
| 240 |
// rely on PATH
|
| 241 |
return new String[] {"env"};
|
| 242 |
} else if (Os.isFamily("openvms")) {
|
| 243 |
return new String[] {"show", "logical"};
|
| 244 |
} else {
|
| 245 |
// MAC OS 9 and previous
|
| 246 |
//TODO: I have no idea how to get it, someone must fix it
|
| 247 |
return null;
|
| 248 |
}
|
| 249 |
}
|
| 250 |
|
| 251 |
/**
|
| 252 |
* ByteArrayOutputStream#toString doesn't seem to work reliably on
|
| 253 |
* OS/390, at least not the way we use it in the execution
|
| 254 |
* context.
|
| 255 |
*
|
| 256 |
* @param bos the output stream that one wants to read.
|
| 257 |
* @return the output stream as a string, read with
|
| 258 |
* special encodings in the case of z/os and os/400.
|
| 259 |
*
|
| 260 |
* @since Ant 1.5
|
| 261 |
*/
|
| 262 |
public static String toString(ByteArrayOutputStream bos) {
|
| 263 |
if (Os.isFamily("z/os")) {
|
| 264 |
try {
|
| 265 |
return bos.toString("Cp1047");
|
| 266 |
} catch (java.io.UnsupportedEncodingException e) {
|
| 267 |
//noop default encoding used
|
| 268 |
}
|
| 269 |
} else if (Os.isFamily("os/400")) {
|
| 270 |
try {
|
| 271 |
return bos.toString("Cp500");
|
| 272 |
} catch (java.io.UnsupportedEncodingException e) {
|
| 273 |
//noop default encoding used
|
| 274 |
}
|
| 275 |
}
|
| 276 |
return bos.toString();
|
| 277 |
}
|
| 278 |
|
| 279 |
/**
|
| 280 |
* Creates a new execute object using <code>PumpStreamHandler</code> for
|
| 281 |
* stream handling.
|
| 282 |
*/
|
| 283 |
public Execute() {
|
| 284 |
this(new PumpStreamHandler(), null);
|
| 285 |
}
|
| 286 |
|
| 287 |
/**
|
| 288 |
* Creates a new execute object.
|
| 289 |
*
|
| 290 |
* @param streamHandler the stream handler used to handle the input and
|
| 291 |
* output streams of the subprocess.
|
| 292 |
*/
|
| 293 |
public Execute(ExecuteStreamHandler streamHandler) {
|
| 294 |
this(streamHandler, null);
|
| 295 |
}
|
| 296 |
|
| 297 |
/**
|
| 298 |
* Creates a new execute object.
|
| 299 |
*
|
| 300 |
* @param streamHandler the stream handler used to handle the input and
|
| 301 |
* output streams of the subprocess.
|
| 302 |
* @param watchdog a watchdog for the subprocess or <code>null</code> to
|
| 303 |
* to disable a timeout for the subprocess.
|
| 304 |
*/
|
| 305 |
public Execute(ExecuteStreamHandler streamHandler,
|
| 306 |
ExecuteWatchdog watchdog) {
|
| 307 |
setStreamHandler(streamHandler);
|
| 308 |
this.watchdog = watchdog;
|
| 309 |
//By default, use the shell launcher for VMS
|
| 310 |
//
|
| 311 |
if (Os.isFamily("openvms")) {
|
| 312 |
useVMLauncher = false;
|
| 313 |
}
|
| 314 |
}
|
| 315 |
|
| 316 |
/**
|
| 317 |
* Set the stream handler to use.
|
| 318 |
* @param streamHandler ExecuteStreamHandler.
|
| 319 |
* @since Ant 1.6
|
| 320 |
*/
|
| 321 |
public void setStreamHandler(ExecuteStreamHandler streamHandler) {
|
| 322 |
this.streamHandler = streamHandler;
|
| 323 |
}
|
| 324 |
|
| 325 |
/**
|
| 326 |
* Returns the commandline used to create a subprocess.
|
| 327 |
*
|
| 328 |
* @return the commandline used to create a subprocess.
|
| 329 |
*/
|
| 330 |
public String[] getCommandline() {
|
| 331 |
return cmdl;
|
| 332 |
}
|
| 333 |
|
| 334 |
/**
|
| 335 |
* Sets the commandline of the subprocess to launch.
|
| 336 |
*
|
| 337 |
* @param commandline the commandline of the subprocess to launch.
|
| 338 |
*/
|
| 339 |
public void setCommandline(String[] commandline) {
|
| 340 |
cmdl = commandline;
|
| 341 |
}
|
| 342 |
|
| 343 |
/**
|
| 344 |
* Set whether to propagate the default environment or not.
|
| 345 |
*
|
| 346 |
* @param newenv whether to propagate the process environment.
|
| 347 |
*/
|
| 348 |
public void setNewenvironment(boolean newenv) {
|
| 349 |
newEnvironment = newenv;
|
| 350 |
}
|
| 351 |
|
| 352 |
/**
|
| 353 |
* Returns the environment used to create a subprocess.
|
| 354 |
*
|
| 355 |
* @return the environment used to create a subprocess.
|
| 356 |
*/
|
| 357 |
public String[] getEnvironment() {
|
| 358 |
return (env == null || newEnvironment)
|
| 359 |
? env : patchEnvironment();
|
| 360 |
}
|
| 361 |
|
| 362 |
/**
|
| 363 |
* Sets the environment variables for the subprocess to launch.
|
| 364 |
*
|
| 365 |
* @param env array of Strings, each element of which has
|
| 366 |
* an environment variable settings in format <em>key=value</em>.
|
| 367 |
*/
|
| 368 |
public void setEnvironment(String[] env) {
|
| 369 |
this.env = env;
|
| 370 |
}
|
| 371 |
|
| 372 |
/**
|
| 373 |
* Sets the working directory of the process to execute.
|
| 374 |
*
|
| 375 |
* <p>This is emulated using the antRun scripts unless the OS is
|
| 376 |
* Windows NT in which case a cmd.exe is spawned,
|
| 377 |
* or MRJ and setting user.dir works, or JDK 1.3 and there is
|
| 378 |
* official support in java.lang.Runtime.
|
| 379 |
*
|
| 380 |
* @param wd the working directory of the process.
|
| 381 |
*/
|
| 382 |
public void setWorkingDirectory(File wd) {
|
| 383 |
workingDirectory =
|
| 384 |
(wd == null || wd.getAbsolutePath().equals(antWorkingDirectory))
|
| 385 |
? null : wd;
|
| 386 |
}
|
| 387 |
|
| 388 |
/**
|
| 389 |
* Return the working directory.
|
| 390 |
* @return the directory as a File.
|
| 391 |
* @since Ant 1.7
|
| 392 |
*/
|
| 393 |
public File getWorkingDirectory() {
|
| 394 |
return workingDirectory == null ? new File(antWorkingDirectory)
|
| 395 |
: workingDirectory;
|
| 396 |
}
|
| 397 |
|
| 398 |
/**
|
| 399 |
* Set the name of the antRun script using the project's value.
|
| 400 |
*
|
| 401 |
* @param project the current project.
|
| 402 |
*
|
| 403 |
* @throws BuildException not clear when it is going to throw an exception, but
|
| 404 |
* it is the method's signature.
|
| 405 |
*/
|
| 406 |
public void setAntRun(Project project) throws BuildException {
|
| 407 |
this.project = project;
|
| 408 |
}
|
| 409 |
|
| 410 |
/**
|
| 411 |
* Launch this execution through the VM, where possible, rather than through
|
| 412 |
* the OS's shell. In some cases and operating systems using the shell will
|
| 413 |
* allow the shell to perform additional processing such as associating an
|
| 414 |
* executable with a script, etc.
|
| 415 |
*
|
| 416 |
* @param useVMLauncher true if exec should launch through the VM,
|
| 417 |
* false if the shell should be used to launch the
|
| 418 |
* command.
|
| 419 |
*/
|
| 420 |
public void setVMLauncher(boolean useVMLauncher) {
|
| 421 |
this.useVMLauncher = useVMLauncher;
|
| 422 |
}
|
| 423 |
|
| 424 |
/**
|
| 425 |
* Creates a process that runs a command.
|
| 426 |
*
|
| 427 |
* @param project the Project, only used for logging purposes, may be null.
|
| 428 |
* @param command the command to run.
|
| 429 |
* @param env the environment for the command.
|
| 430 |
* @param dir the working directory for the command.
|
| 431 |
* @param useVM use the built-in exec command for JDK 1.3 if available.
|
| 432 |
* @return the process started.
|
| 433 |
* @throws IOException forwarded from the particular launcher used.
|
| 434 |
*
|
| 435 |
* @since Ant 1.5
|
| 436 |
*/
|
| 437 |
public static Process launch(Project project, String[] command,
|
| 438 |
String[] env, File dir, boolean useVM)
|
| 439 |
throws IOException {
|
| 440 |
if (dir != null && !dir.exists()) {
|
| 441 |
throw new BuildException(dir + " doesn't exist.");
|
| 442 |
}
|
| 443 |
CommandLauncher launcher
|
| 444 |
= ((useVM && vmLauncher != null) ? vmLauncher : shellLauncher);
|
| 445 |
return launcher.exec(project, command, env, dir);
|
| 446 |
}
|
| 447 |
|
| 448 |
/**
|
| 449 |
* Runs a process defined by the command line and returns its exit status.
|
| 450 |
*
|
| 451 |
* @return the exit status of the subprocess or <code>INVALID</code>.
|
| 452 |
* @exception java.io.IOException The exception is thrown, if launching
|
| 453 |
* of the subprocess failed.
|
| 454 |
*/
|
| 455 |
public int execute() throws IOException {
|
| 456 |
if (workingDirectory != null && !workingDirectory.exists()) {
|
| 457 |
throw new BuildException(workingDirectory + " doesn't exist.");
|
| 458 |
}
|
| 459 |
final Process process = launch(project, getCommandline(),
|
| 460 |
getEnvironment(), workingDirectory,
|
| 461 |
useVMLauncher);
|
| 462 |
try {
|
| 463 |
streamHandler.setProcessInputStream(process.getOutputStream());
|
| 464 |
streamHandler.setProcessOutputStream(process.getInputStream());
|
| 465 |
streamHandler.setProcessErrorStream(process.getErrorStream());
|
| 466 |
} catch (IOException e) {
|
| 467 |
process.destroy();
|
| 468 |
throw e;
|
| 469 |
}
|
| 470 |
streamHandler.start();
|
| 471 |
|
| 472 |
try {
|
| 473 |
// add the process to the list of those to destroy if the VM exits
|
| 474 |
//
|
| 475 |
processDestroyer.add(process);
|
| 476 |
|
| 477 |
if (watchdog != null) {
|
| 478 |
watchdog.start(process);
|
| 479 |
}
|
| 480 |
waitFor(process);
|
| 481 |
|
| 482 |
if (watchdog != null) {
|
| 483 |
watchdog.stop();
|
| 484 |
}
|
| 485 |
streamHandler.stop();
|
| 486 |
closeStreams(process);
|
| 487 |
|
| 488 |
if (watchdog != null) {
|
| 489 |
watchdog.checkException();
|
| 490 |
}
|
| 491 |
return getExitValue();
|
| 492 |
} catch (ThreadDeath t) {
|
| 493 |
// #31928: forcibly kill it before continuing.
|
| 494 |
process.destroy();
|
| 495 |
throw t;
|
| 496 |
} finally {
|
| 497 |
// remove the process to the list of those to destroy if
|
| 498 |
// the VM exits
|
| 499 |
//
|
| 500 |
processDestroyer.remove(process);
|
| 501 |
}
|
| 502 |
}
|
| 503 |
|
| 504 |
/**
|
| 505 |
* Starts a process defined by the command line.
|
| 506 |
* Ant will not wait for this process, nor log its output.
|
| 507 |
*
|
| 508 |
* @throws java.io.IOException The exception is thrown, if launching
|
| 509 |
* of the subprocess failed.
|
| 510 |
* @since Ant 1.6
|
| 511 |
*/
|
| 512 |
public void spawn() throws IOException {
|
| 513 |
if (workingDirectory != null && !workingDirectory.exists()) {
|
| 514 |
throw new BuildException(workingDirectory + " doesn't exist.");
|
| 515 |
}
|
| 516 |
final Process process = launch(project, getCommandline(),
|
| 517 |
getEnvironment(), workingDirectory,
|
| 518 |
useVMLauncher);
|
| 519 |
if (Os.isFamily("windows")) {
|
| 520 |
try {
|
| 521 |
Thread.sleep(ONE_SECOND);
|
| 522 |
} catch (InterruptedException e) {
|
| 523 |
project.log("interruption in the sleep after having spawned a"
|
| 524 |
+ " process", Project.MSG_VERBOSE);
|
| 525 |
}
|
| 526 |
}
|
| 527 |
OutputStream dummyOut = new OutputStream() {
|
| 528 |
public void write(int b) throws IOException {
|
| 529 |
// Method intended to swallow whatever comes at it
|
| 530 |
}
|
| 531 |
};
|
| 532 |
|
| 533 |
ExecuteStreamHandler handler = new PumpStreamHandler(dummyOut);
|
| 534 |
handler.setProcessErrorStream(process.getErrorStream());
|
| 535 |
handler.setProcessOutputStream(process.getInputStream());
|
| 536 |
handler.start();
|
| 537 |
process.getOutputStream().close();
|
| 538 |
|
| 539 |
project.log("spawned process " + process.toString(),
|
| 540 |
Project.MSG_VERBOSE);
|
| 541 |
}
|
| 542 |
|
| 543 |
/**
|
| 544 |
* Wait for a given process.
|
| 545 |
*
|
| 546 |
* @param process the process one wants to wait for.
|
| 547 |
*/
|
| 548 |
protected void waitFor(Process process) {
|
| 549 |
try {
|
| 550 |
process.waitFor();
|
| 551 |
setExitValue(process.exitValue());
|
| 552 |
} catch (InterruptedException e) {
|
| 553 |
process.destroy();
|
| 554 |
}
|
| 555 |
}
|
| 556 |
|
| 557 |
/**
|
| 558 |
* Set the exit value.
|
| 559 |
*
|
| 560 |
* @param value exit value of the process.
|
| 561 |
*/
|
| 562 |
protected void setExitValue(int value) {
|
| 563 |
exitValue = value;
|
| 564 |
}
|
| 565 |
|
| 566 |
/**
|
| 567 |
* Query the exit value of the process.
|
| 568 |
* @return the exit value or Execute.INVALID if no exit value has
|
| 569 |
* been received.
|
| 570 |
*/
|
| 571 |
public int getExitValue() {
|
| 572 |
return exitValue;
|
| 573 |
}
|
| 574 |
|
| 575 |
/**
|
| 576 |
* Checks whether <code>exitValue</code> signals a failure on the current
|
| 577 |
* system (OS specific).
|
| 578 |
*
|
| 579 |
* <p><b>Note</b> that this method relies on the conventions of
|
| 580 |
* the OS, it will return false results if the application you are
|
| 581 |
* running doesn't follow these conventions. One notable
|
| 582 |
* exception is the Java VM provided by HP for OpenVMS - it will
|
| 583 |
* return 0 if successful (like on any other platform), but this
|
| 584 |
* signals a failure on OpenVMS. So if you execute a new Java VM
|
| 585 |
* on OpenVMS, you cannot trust this method.</p>
|
| 586 |
*
|
| 587 |
* @param exitValue the exit value (return code) to be checked.
|
| 588 |
* @return <code>true</code> if <code>exitValue</code> signals a failure.
|
| 589 |
*/
|
| 590 |
public static boolean isFailure(int exitValue) {
|
| 591 |
//on openvms even exit value signals failure;
|
| 592 |
// for other platforms nonzero exit value signals failure
|
| 593 |
return Os.isFamily("openvms")
|
| 594 |
? (exitValue % 2 == 0) : (exitValue != 0);
|
| 595 |
}
|
| 596 |
|
| 597 |
/**
|
| 598 |
* Did this execute return in a failure.
|
| 599 |
* @see #isFailure(int)
|
| 600 |
* @return true if and only if the exit code is interpreted as a failure
|
| 601 |
* @since Ant1.7
|
| 602 |
*/
|
| 603 |
public boolean isFailure() {
|
| 604 |
return isFailure(getExitValue());
|
| 605 |
}
|
| 606 |
|
| 607 |
/**
|
| 608 |
* Test for an untimely death of the process.
|
| 609 |
* @return true if a watchdog had to kill the process.
|
| 610 |
* @since Ant 1.5
|
| 611 |
*/
|
| 612 |
public boolean killedProcess() {
|
| 613 |
return watchdog != null && watchdog.killedProcess();
|
| 614 |
}
|
| 615 |
|
| 616 |
/**
|
| 617 |
* Patch the current environment with the new values from the user.
|
| 618 |
* @return the patched environment.
|
| 619 |
*/
|
| 620 |
private String[] patchEnvironment() {
|
| 621 |
// On OpenVMS Runtime#exec() doesn't support the environment array,
|
| 622 |
// so we only return the new values which then will be set in
|
| 623 |
// the generated DCL script, inheriting the parent process environment
|
| 624 |
if (Os.isFamily("openvms")) {
|
| 625 |
return env;
|
| 626 |
}
|
| 627 |
Vector osEnv = (Vector) getProcEnvironment().clone();
|
| 628 |
for (int i = 0; i < env.length; i++) {
|
| 629 |
String keyValue = env[i];
|
| 630 |
// Get key including "="
|
| 631 |
String key = keyValue.substring(0, keyValue.indexOf('=') + 1);
|
| 632 |
if (environmentCaseInSensitive) {
|
| 633 |
// Nb: using default locale as key is a env name
|
| 634 |
key = key.toLowerCase();
|
| 635 |
}
|
| 636 |
int size = osEnv.size();
|
| 637 |
// Find the key in the current enviroment copy
|
| 638 |
// and remove it.
|
| 639 |
for (int j = 0; j < size; j++) {
|
| 640 |
String osEnvItem = (String) osEnv.elementAt(j);
|
| 641 |
String convertedItem = environmentCaseInSensitive
|
| 642 |
? osEnvItem.toLowerCase() : osEnvItem;
|
| 643 |
if (convertedItem.startsWith(key)) {
|
| 644 |
osEnv.removeElementAt(j);
|
| 645 |
if (environmentCaseInSensitive) {
|
| 646 |
// Use the original casiness of the key
|
| 647 |
keyValue = osEnvItem.substring(0, key.length())
|
| 648 |
+ keyValue.substring(key.length());
|
| 649 |
}
|
| 650 |
break;
|
| 651 |
}
|
| 652 |
}
|
| 653 |
// Add the key to the enviromnent copy
|
| 654 |
osEnv.addElement(keyValue);
|
| 655 |
}
|
| 656 |
return (String[]) (osEnv.toArray(new String[osEnv.size()]));
|
| 657 |
}
|
| 658 |
|
| 659 |
/**
|
| 660 |
* A utility method that runs an external command. Writes the output and
|
| 661 |
* error streams of the command to the project log.
|
| 662 |
*
|
| 663 |
* @param task The task that the command is part of. Used for logging
|
| 664 |
* @param cmdline The command to execute.
|
| 665 |
*
|
| 666 |
* @throws BuildException if the command does not exit successfully.
|
| 667 |
*/
|
| 668 |
public static void runCommand(Task task, String[] cmdline)
|
| 669 |
throws BuildException {
|
| 670 |
try {
|
| 671 |
task.log(Commandline.describeCommand(cmdline),
|
| 672 |
Project.MSG_VERBOSE);
|
| 673 |
Execute exe = new Execute(
|
| 674 |
new LogStreamHandler(task, Project.MSG_INFO, Project.MSG_ERR));
|
| 675 |
exe.setAntRun(task.getProject());
|
| 676 |
exe.setCommandline(cmdline);
|
| 677 |
int retval = exe.execute();
|
| 678 |
if (isFailure(retval)) {
|
| 679 |
throw new BuildException(cmdline[0]
|
| 680 |
+ " failed with return code " + retval, task.getLocation());
|
| 681 |
}
|
| 682 |
} catch (java.io.IOException exc) {
|
| 683 |
throw new BuildException("Could not launch " + cmdline[0] + ": "
|
| 684 |
+ exc, task.getLocation());
|
| 685 |
}
|
| 686 |
}
|
| 687 |
|
| 688 |
/**
|
| 689 |
* Close the streams belonging to the given Process.
|
| 690 |
* @param process the <code>Process</code>.
|
| 691 |
*/
|
| 692 |
public static void closeStreams(Process process) {
|
| 693 |
FileUtils.close(process.getInputStream());
|
| 694 |
FileUtils.close(process.getOutputStream());
|
| 695 |
FileUtils.close(process.getErrorStream());
|
| 696 |
}
|
| 697 |
|
| 698 |
/**
|
| 699 |
* This method is VMS specific and used by getProcEnvironment().
|
| 700 |
*
|
| 701 |
* Parses VMS logicals from <code>in</code> and adds them to
|
| 702 |
* <code>environment</code>. <code>in</code> is expected to be the
|
| 703 |
* output of "SHOW LOGICAL". The method takes care of parsing the output
|
| 704 |
* correctly as well as making sure that a logical defined in multiple
|
| 705 |
* tables only gets added from the highest order table. Logicals with
|
| 706 |
* multiple equivalence names are mapped to a variable with multiple
|
| 707 |
* values separated by a comma (,).
|
| 708 |
*/
|
| 709 |
private static Vector addVMSLogicals(Vector environment, BufferedReader in)
|
| 710 |
throws IOException {
|
| 711 |
HashMap logicals = new HashMap();
|
| 712 |
String logName = null, logValue = null, newLogName;
|
| 713 |
String line = null;
|
| 714 |
// CheckStyle:MagicNumber OFF
|
| 715 |
while ((line = in.readLine()) != null) {
|
| 716 |
// parse the VMS logicals into required format ("VAR=VAL[,VAL2]")
|
| 717 |
if (line.startsWith("\t=")) {
|
| 718 |
// further equivalence name of previous logical
|
| 719 |
if (logName != null) {
|
| 720 |
logValue += "," + line.substring(4, line.length() - 1);
|
| 721 |
}
|
| 722 |
} else if (line.startsWith(" \"")) {
|
| 723 |
// new logical?
|
| 724 |
if (logName != null) {
|
| 725 |
logicals.put(logName, logValue);
|
| 726 |
}
|
| 727 |
int eqIndex = line.indexOf('=');
|
| 728 |
newLogName = line.substring(3, eqIndex - 2);
|
| 729 |
if (logicals.containsKey(newLogName)) {
|
| 730 |
// already got this logical from a higher order table
|
| 731 |
logName = null;
|
| 732 |
} else {
|
| 733 |
logName = newLogName;
|
| 734 |
logValue = line.substring(eqIndex + 3, line.length() - 1);
|
| 735 |
}
|
| 736 |
}
|
| 737 |
}
|
| 738 |
// CheckStyle:MagicNumber ON
|
| 739 |
// Since we "look ahead" before adding, there's one last env var.
|
| 740 |
if (logName != null) {
|
| 741 |
logicals.put(logName, logValue);
|
| 742 |
}
|
| 743 |
for (Iterator i = logicals.keySet().iterator(); i.hasNext();) {
|
| 744 |
String logical = (String) i.next();
|
| 745 |
environment.add(logical + "=" + logicals.get(logical));
|
| 746 |
}
|
| 747 |
return environment;
|
| 748 |
}
|
| 749 |
|
| 750 |
/**
|
| 751 |
* A command launcher for a particular JVM/OS platform. This class is
|
| 752 |
* a general purpose command launcher which can only launch commands in
|
| 753 |
* the current working directory.
|
| 754 |
*/
|
| 755 |
private static class CommandLauncher {
|
| 756 |
/**
|
| 757 |
* Launches the given command in a new process.
|
| 758 |
*
|
| 759 |
* @param project The project that the command is part of.
|
| 760 |
* @param cmd The command to execute.
|
| 761 |
* @param env The environment for the new process. If null,
|
| 762 |
* the environment of the current process is used.
|
| 763 |
* @return the created Process.
|
| 764 |
* @throws IOException if attempting to run a command in a
|
| 765 |
* specific directory.
|
| 766 |
*/
|
| 767 |
public Process exec(Project project, String[] cmd, String[] env)
|
| 768 |
throws IOException {
|
| 769 |
if (project != null) {
|
| 770 |
project.log("Execute:CommandLauncher: "
|
| 771 |
+ Commandline.describeCommand(cmd), Project.MSG_DEBUG);
|
| 772 |
}
|
| 773 |
return Runtime.getRuntime().exec(cmd, env);
|
| 774 |
}
|
| 775 |
|
| 776 |
/**
|
| 777 |
* Launches the given command in a new process, in the given working
|
| 778 |
* directory.
|
| 779 |
*
|
| 780 |
* @param project The project that the command is part of.
|
| 781 |
* @param cmd The command to execute.
|
| 782 |
* @param env The environment for the new process. If null,
|
| 783 |
* the environment of the current process is used.
|
| 784 |
* @param workingDir The directory to start the command in. If null,
|
| 785 |
* the current directory is used.
|
| 786 |
* @return the created Process.
|
| 787 |
* @throws IOException if trying to change directory.
|
| 788 |
*/
|
| 789 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 790 |
File workingDir) throws IOException {
|
| 791 |
if (workingDir == null) {
|
| 792 |
return exec(project, cmd, env);
|
| 793 |
}
|
| 794 |
throw new IOException("Cannot execute a process in different "
|
| 795 |
+ "directory under this JVM");
|
| 796 |
}
|
| 797 |
}
|
| 798 |
|
| 799 |
/**
|
| 800 |
* A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
|
| 801 |
* Runtime.exec() command.
|
| 802 |
*/
|
| 803 |
private static class Java13CommandLauncher extends CommandLauncher {
|
| 804 |
|
| 805 |
public Java13CommandLauncher() throws NoSuchMethodException {
|
| 806 |
// Used to verify if Java13 is available, is prerequisite in ant 1.8
|
| 807 |
}
|
| 808 |
|
| 809 |
/**
|
| 810 |
* Launches the given command in a new process, in the given working
|
| 811 |
* directory.
|
| 812 |
* @param project the Ant project.
|
| 813 |
* @param cmd the command line to execute as an array of strings.
|
| 814 |
* @param env the environment to set as an array of strings.
|
| 815 |
* @param workingDir the working directory where the command
|
| 816 |
* should run.
|
| 817 |
* @return the created Process.
|
| 818 |
* @throws IOException probably forwarded from Runtime#exec.
|
| 819 |
*/
|
| 820 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 821 |
File workingDir) throws IOException {
|
| 822 |
try {
|
| 823 |
if (project != null) {
|
| 824 |
project.log("Execute:Java13CommandLauncher: "
|
| 825 |
+ Commandline.describeCommand(cmd), Project.MSG_DEBUG);
|
| 826 |
}
|
| 827 |
return Runtime.getRuntime().exec(cmd, env, workingDir);
|
| 828 |
} catch (IOException ioex) {
|
| 829 |
throw ioex;
|
| 830 |
} catch (Exception exc) {
|
| 831 |
// IllegalAccess, IllegalArgument, ClassCast
|
| 832 |
throw new BuildException("Unable to execute command", exc);
|
| 833 |
}
|
| 834 |
}
|
| 835 |
}
|
| 836 |
|
| 837 |
/**
|
| 838 |
* A command launcher that proxies another command launcher.
|
| 839 |
*
|
| 840 |
* Sub-classes override exec(args, env, workdir).
|
| 841 |
*/
|
| 842 |
private static class CommandLauncherProxy extends CommandLauncher {
|
| 843 |
private CommandLauncher myLauncher;
|
| 844 |
|
| 845 |
CommandLauncherProxy(CommandLauncher launcher) {
|
| 846 |
myLauncher = launcher;
|
| 847 |
}
|
| 848 |
|
| 849 |
/**
|
| 850 |
* Launches the given command in a new process. Delegates this
|
| 851 |
* method to the proxied launcher.
|
| 852 |
* @param project the Ant project.
|
| 853 |
* @param cmd the command line to execute as an array of strings.
|
| 854 |
* @param env the environment to set as an array of strings.
|
| 855 |
* @return the created Process.
|
| 856 |
* @throws IOException forwarded from the exec method of the
|
| 857 |
* command launcher.
|
| 858 |
*/
|
| 859 |
public Process exec(Project project, String[] cmd, String[] env)
|
| 860 |
throws IOException {
|
| 861 |
return myLauncher.exec(project, cmd, env);
|
| 862 |
}
|
| 863 |
}
|
| 864 |
|
| 865 |
/**
|
| 866 |
* A command launcher for OS/2 that uses 'cmd.exe' when launching
|
| 867 |
* commands in directories other than the current working
|
| 868 |
* directory.
|
| 869 |
*
|
| 870 |
* <p>Unlike Windows NT and friends, OS/2's cd doesn't support the
|
| 871 |
* /d switch to change drives and directories in one go.</p>
|
| 872 |
*/
|
| 873 |
private static class OS2CommandLauncher extends CommandLauncherProxy {
|
| 874 |
OS2CommandLauncher(CommandLauncher launcher) {
|
| 875 |
super(launcher);
|
| 876 |
}
|
| 877 |
|
| 878 |
/**
|
| 879 |
* Launches the given command in a new process, in the given working
|
| 880 |
* directory.
|
| 881 |
* @param project the Ant project.
|
| 882 |
* @param cmd the command line to execute as an array of strings.
|
| 883 |
* @param env the environment to set as an array of strings.
|
| 884 |
* @param workingDir working directory where the command should run.
|
| 885 |
* @return the created Process.
|
| 886 |
* @throws IOException forwarded from the exec method of the
|
| 887 |
* command launcher.
|
| 888 |
*/
|
| 889 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 890 |
File workingDir) throws IOException {
|
| 891 |
File commandDir = workingDir;
|
| 892 |
if (workingDir == null) {
|
| 893 |
if (project != null) {
|
| 894 |
commandDir = project.getBaseDir();
|
| 895 |
} else {
|
| 896 |
return exec(project, cmd, env);
|
| 897 |
}
|
| 898 |
}
|
| 899 |
// Use cmd.exe to change to the specified drive and
|
| 900 |
// directory before running the command
|
| 901 |
final int preCmdLength = 7;
|
| 902 |
final String cmdDir = commandDir.getAbsolutePath();
|
| 903 |
String[] newcmd = new String[cmd.length + preCmdLength];
|
| 904 |
// CheckStyle:MagicNumber OFF - do not bother
|
| 905 |
newcmd[0] = "cmd";
|
| 906 |
newcmd[1] = "/c";
|
| 907 |
newcmd[2] = cmdDir.substring(0, 2);
|
| 908 |
newcmd[3] = "&&";
|
| 909 |
newcmd[4] = "cd";
|
| 910 |
newcmd[5] = cmdDir.substring(2);
|
| 911 |
newcmd[6] = "&&";
|
| 912 |
// CheckStyle:MagicNumber ON
|
| 913 |
System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
|
| 914 |
|
| 915 |
return exec(project, newcmd, env);
|
| 916 |
}
|
| 917 |
}
|
| 918 |
|
| 919 |
/**
|
| 920 |
* A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
|
| 921 |
* launching commands in directories other than the current working
|
| 922 |
* directory.
|
| 923 |
*/
|
| 924 |
private static class WinNTCommandLauncher extends CommandLauncherProxy {
|
| 925 |
WinNTCommandLauncher(CommandLauncher launcher) {
|
| 926 |
super(launcher);
|
| 927 |
}
|
| 928 |
|
| 929 |
/**
|
| 930 |
* Launches the given command in a new process, in the given working
|
| 931 |
* directory.
|
| 932 |
* @param project the Ant project.
|
| 933 |
* @param cmd the command line to execute as an array of strings.
|
| 934 |
* @param env the environment to set as an array of strings.
|
| 935 |
* @param workingDir working directory where the command should run.
|
| 936 |
* @return the created Process.
|
| 937 |
* @throws IOException forwarded from the exec method of the
|
| 938 |
* command launcher.
|
| 939 |
*/
|
| 940 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 941 |
File workingDir) throws IOException {
|
| 942 |
File commandDir = workingDir;
|
| 943 |
if (workingDir == null) {
|
| 944 |
if (project != null) {
|
| 945 |
commandDir = project.getBaseDir();
|
| 946 |
} else {
|
| 947 |
return exec(project, cmd, env);
|
| 948 |
}
|
| 949 |
}
|
| 950 |
// Use cmd.exe to change to the specified directory before running
|
| 951 |
// the command
|
| 952 |
final int preCmdLength = 6;
|
| 953 |
String[] newcmd = new String[cmd.length + preCmdLength];
|
| 954 |
// CheckStyle:MagicNumber OFF - do not bother
|
| 955 |
newcmd[0] = "cmd";
|
| 956 |
newcmd[1] = "/c";
|
| 957 |
newcmd[2] = "cd";
|
| 958 |
newcmd[3] = "/d";
|
| 959 |
newcmd[4] = commandDir.getAbsolutePath();
|
| 960 |
newcmd[5] = "&&";
|
| 961 |
// CheckStyle:MagicNumber ON
|
| 962 |
System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
|
| 963 |
|
| 964 |
return exec(project, newcmd, env);
|
| 965 |
}
|
| 966 |
}
|
| 967 |
|
| 968 |
/**
|
| 969 |
* A command launcher for Mac that uses a dodgy mechanism to change
|
| 970 |
* working directory before launching commands.
|
| 971 |
*/
|
| 972 |
private static class MacCommandLauncher extends CommandLauncherProxy {
|
| 973 |
MacCommandLauncher(CommandLauncher launcher) {
|
| 974 |
super(launcher);
|
| 975 |
}
|
| 976 |
|
| 977 |
/**
|
| 978 |
* Launches the given command in a new process, in the given working
|
| 979 |
* directory.
|
| 980 |
* @param project the Ant project.
|
| 981 |
* @param cmd the command line to execute as an array of strings.
|
| 982 |
* @param env the environment to set as an array of strings.
|
| 983 |
* @param workingDir working directory where the command should run.
|
| 984 |
* @return the created Process.
|
| 985 |
* @throws IOException forwarded from the exec method of the
|
| 986 |
* command launcher.
|
| 987 |
*/
|
| 988 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 989 |
File workingDir) throws IOException {
|
| 990 |
if (workingDir == null) {
|
| 991 |
return exec(project, cmd, env);
|
| 992 |
}
|
| 993 |
System.getProperties().put("user.dir", workingDir.getAbsolutePath());
|
| 994 |
try {
|
| 995 |
return exec(project, cmd, env);
|
| 996 |
} finally {
|
| 997 |
System.getProperties().put("user.dir", antWorkingDirectory);
|
| 998 |
}
|
| 999 |
}
|
| 1000 |
}
|
| 1001 |
|
| 1002 |
/**
|
| 1003 |
* A command launcher that uses an auxiliary script to launch commands
|
| 1004 |
* in directories other than the current working directory.
|
| 1005 |
*/
|
| 1006 |
private static class ScriptCommandLauncher extends CommandLauncherProxy {
|
| 1007 |
ScriptCommandLauncher(String script, CommandLauncher launcher) {
|
| 1008 |
super(launcher);
|
| 1009 |
myScript = script;
|
| 1010 |
}
|
| 1011 |
|
| 1012 |
/**
|
| 1013 |
* Launches the given command in a new process, in the given working
|
| 1014 |
* directory.
|
| 1015 |
* @param project the Ant project.
|
| 1016 |
* @param cmd the command line to execute as an array of strings.
|
| 1017 |
* @param env the environment to set as an array of strings.
|
| 1018 |
* @param workingDir working directory where the command should run.
|
| 1019 |
* @return the created Process.
|
| 1020 |
* @throws IOException forwarded from the exec method of the
|
| 1021 |
* command launcher.
|
| 1022 |
*/
|
| 1023 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 1024 |
File workingDir) throws IOException {
|
| 1025 |
if (project == null) {
|
| 1026 |
if (workingDir == null) {
|
| 1027 |
return exec(project, cmd, env);
|
| 1028 |
}
|
| 1029 |
throw new IOException("Cannot locate antRun script: "
|
| 1030 |
+ "No project provided");
|
| 1031 |
}
|
| 1032 |
// Locate the auxiliary script
|
| 1033 |
String antHome = project.getProperty(MagicNames.ANT_HOME);
|
| 1034 |
if (antHome == null) {
|
| 1035 |
throw new IOException("Cannot locate antRun script: "
|
| 1036 |
+ "Property '" + MagicNames.ANT_HOME + "' not found");
|
| 1037 |
}
|
| 1038 |
String antRun =
|
| 1039 |
FILE_UTILS.resolveFile(project.getBaseDir(),
|
| 1040 |
antHome + File.separator + myScript).toString();
|
| 1041 |
|
| 1042 |
// Build the command
|
| 1043 |
File commandDir = workingDir;
|
| 1044 |
if (workingDir == null) {
|
| 1045 |
commandDir = project.getBaseDir();
|
| 1046 |
}
|
| 1047 |
String[] newcmd = new String[cmd.length + 2];
|
| 1048 |
newcmd[0] = antRun;
|
| 1049 |
newcmd[1] = commandDir.getAbsolutePath();
|
| 1050 |
System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
|
| 1051 |
|
| 1052 |
return exec(project, newcmd, env);
|
| 1053 |
}
|
| 1054 |
|
| 1055 |
private String myScript;
|
| 1056 |
}
|
| 1057 |
|
| 1058 |
/**
|
| 1059 |
* A command launcher that uses an auxiliary perl script to launch commands
|
| 1060 |
* in directories other than the current working directory.
|
| 1061 |
*/
|
| 1062 |
private static class PerlScriptCommandLauncher
|
| 1063 |
extends CommandLauncherProxy {
|
| 1064 |
private String myScript;
|
| 1065 |
|
| 1066 |
PerlScriptCommandLauncher(String script, CommandLauncher launcher) {
|
| 1067 |
super(launcher);
|
| 1068 |
myScript = script;
|
| 1069 |
}
|
| 1070 |
|
| 1071 |
/**
|
| 1072 |
* Launches the given command in a new process, in the given working
|
| 1073 |
* directory.
|
| 1074 |
* @param project the Ant project.
|
| 1075 |
* @param cmd the command line to execute as an array of strings.
|
| 1076 |
* @param env the environment to set as an array of strings.
|
| 1077 |
* @param workingDir working directory where the command should run.
|
| 1078 |
* @return the created Process.
|
| 1079 |
* @throws IOException forwarded from the exec method of the
|
| 1080 |
* command launcher.
|
| 1081 |
*/
|
| 1082 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 1083 |
File workingDir) throws IOException {
|
| 1084 |
if (project == null) {
|
| 1085 |
if (workingDir == null) {
|
| 1086 |
return exec(project, cmd, env);
|
| 1087 |
}
|
| 1088 |
throw new IOException("Cannot locate antRun script: "
|
| 1089 |
+ "No project provided");
|
| 1090 |
}
|
| 1091 |
// Locate the auxiliary script
|
| 1092 |
String antHome = project.getProperty(MagicNames.ANT_HOME);
|
| 1093 |
if (antHome == null) {
|
| 1094 |
throw new IOException("Cannot locate antRun script: "
|
| 1095 |
+ "Property '" + MagicNames.ANT_HOME + "' not found");
|
| 1096 |
}
|
| 1097 |
String antRun =
|
| 1098 |
FILE_UTILS.resolveFile(project.getBaseDir(),
|
| 1099 |
antHome + File.separator + myScript).toString();
|
| 1100 |
|
| 1101 |
// Build the command
|
| 1102 |
File commandDir = workingDir;
|
| 1103 |
if (workingDir == null) {
|
| 1104 |
commandDir = project.getBaseDir();
|
| 1105 |
}
|
| 1106 |
// CheckStyle:MagicNumber OFF
|
| 1107 |
String[] newcmd = new String[cmd.length + 3];
|
| 1108 |
newcmd[0] = "perl";
|
| 1109 |
newcmd[1] = antRun;
|
| 1110 |
newcmd[2] = commandDir.getAbsolutePath();
|
| 1111 |
System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
|
| 1112 |
// CheckStyle:MagicNumber ON
|
| 1113 |
|
| 1114 |
return exec(project, newcmd, env);
|
| 1115 |
}
|
| 1116 |
}
|
| 1117 |
|
| 1118 |
/**
|
| 1119 |
* A command launcher for VMS that writes the command to a temporary DCL
|
| 1120 |
* script before launching commands. This is due to limitations of both
|
| 1121 |
* the DCL interpreter and the Java VM implementation.
|
| 1122 |
*/
|
| 1123 |
private static class VmsCommandLauncher extends Java13CommandLauncher {
|
| 1124 |
|
| 1125 |
public VmsCommandLauncher() throws NoSuchMethodException {
|
| 1126 |
super();
|
| 1127 |
}
|
| 1128 |
|
| 1129 |
/**
|
| 1130 |
* Launches the given command in a new process.
|
| 1131 |
* @param project the Ant project.
|
| 1132 |
* @param cmd the command line to execute as an array of strings.
|
| 1133 |
* @param env the environment to set as an array of strings.
|
| 1134 |
* @return the created Process.
|
| 1135 |
* @throws IOException forwarded from the exec method of the
|
| 1136 |
* command launcher.
|
| 1137 |
*/
|
| 1138 |
public Process exec(Project project, String[] cmd, String[] env)
|
| 1139 |
throws IOException {
|
| 1140 |
File cmdFile = createCommandFile(cmd, env);
|
| 1141 |
Process p
|
| 1142 |
= super.exec(project, new String[] {cmdFile.getPath()}, env);
|
| 1143 |
deleteAfter(cmdFile, p);
|
| 1144 |
return p;
|
| 1145 |
}
|
| 1146 |
|
| 1147 |
/**
|
| 1148 |
* Launches the given command in a new process, in the given working
|
| 1149 |
* directory. Note that under Java 1.4.0 and 1.4.1 on VMS this
|
| 1150 |
* method only works if <code>workingDir</code> is null or the logical
|
| 1151 |
* JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
|
| 1152 |
* @param project the Ant project.
|
| 1153 |
* @param cmd the command line to execute as an array of strings.
|
| 1154 |
* @param env the environment to set as an array of strings.
|
| 1155 |
* @param workingDir working directory where the command should run.
|
| 1156 |
* @return the created Process.
|
| 1157 |
* @throws IOException forwarded from the exec method of the
|
| 1158 |
* command launcher.
|
| 1159 |
*/
|
| 1160 |
public Process exec(Project project, String[] cmd, String[] env,
|
| 1161 |
File workingDir) throws IOException {
|
| 1162 |
File cmdFile = createCommandFile(cmd, env);
|
| 1163 |
Process p = super.exec(project, new String[] {cmdFile.getPath()},
|
| 1164 |
env, workingDir);
|
| 1165 |
deleteAfter(cmdFile, p);
|
| 1166 |
return p;
|
| 1167 |
}
|
| 1168 |
|
| 1169 |
/*
|
| 1170 |
* Writes the command into a temporary DCL script and returns the
|
| 1171 |
* corresponding File object. The script will be deleted on exit.
|
| 1172 |
* @param cmd the command line to execute as an array of strings.
|
| 1173 |
* @param env the environment to set as an array of strings.
|
| 1174 |
* @return the command File.
|
| 1175 |
* @throws IOException if errors are encountered creating the file.
|
| 1176 |
*/
|
| 1177 |
private File createCommandFile(String[] cmd, String[] env)
|
| 1178 |
throws IOException {
|
| 1179 |
File script = FILE_UTILS.createTempFile("ANT", ".COM", null, true, true);
|
| 1180 |
BufferedWriter out = null;
|
| 1181 |
try {
|
| 1182 |
out = new BufferedWriter(new FileWriter(script));
|
| 1183 |
|
| 1184 |
// add the environment as logicals to the DCL script
|
| 1185 |
if (env != null) {
|
| 1186 |
int eqIndex;
|
| 1187 |
for (int i = 0; i < env.length; i++) {
|
| 1188 |
eqIndex = env[i].indexOf('=');
|
| 1189 |
if (eqIndex != -1) {
|
| 1190 |
out.write("$ DEFINE/NOLOG ");
|
| 1191 |
out.write(env[i].substring(0, eqIndex));
|
| 1192 |
out.write(" \"");
|
| 1193 |
out.write(env[i].substring(eqIndex + 1));
|
| 1194 |
out.write('\"');
|
| 1195 |
out.newLine();
|
| 1196 |
}
|
| 1197 |
}
|
| 1198 |
}
|
| 1199 |
out.write("$ " + cmd[0]);
|
| 1200 |
for (int i = 1; i < cmd.length; i++) {
|
| 1201 |
out.write(" -");
|
| 1202 |
out.newLine();
|
| 1203 |
out.write(cmd[i]);
|
| 1204 |
}
|
| 1205 |
} finally {
|
| 1206 |
if (out != null) {
|
| 1207 |
out.close();
|
| 1208 |
}
|
| 1209 |
}
|
| 1210 |
return script;
|
| 1211 |
}
|
| 1212 |
|
| 1213 |
private void deleteAfter(final File f, final Process p) {
|
| 1214 |
new Thread() {
|
| 1215 |
public void run() {
|
| 1216 |
try {
|
| 1217 |
p.waitFor();
|
| 1218 |
} catch (InterruptedException e) {
|
| 1219 |
//ignore
|
| 1220 |
}
|
| 1221 |
FileUtils.delete(f);
|
| 1222 |
}
|
| 1223 |
}
|
| 1224 |
.start();
|
| 1225 |
}
|
| 1226 |
}
|
| 1227 |
}
|