You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Execute.java 29 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import org.apache.tools.ant.BuildException;
  56. import org.apache.tools.ant.Project;
  57. import org.apache.tools.ant.Task;
  58. import org.apache.tools.ant.types.Commandline;
  59. import org.apache.tools.ant.taskdefs.condition.Os;
  60. import java.io.File;
  61. import java.io.IOException;
  62. import java.io.BufferedReader;
  63. import java.io.StringReader;
  64. import java.io.ByteArrayOutputStream;
  65. import java.lang.reflect.InvocationTargetException;
  66. import java.lang.reflect.Method;
  67. import java.util.Vector;
  68. /**
  69. * Runs an external program.
  70. *
  71. * @author thomas.haas@softwired-inc.com
  72. *
  73. * @version $Revision$
  74. */
  75. public class Execute {
  76. /** Invalid exit code. **/
  77. public final static int INVALID = Integer.MAX_VALUE;
  78. private String[] cmdl = null;
  79. private String[] env = null;
  80. private int exitValue = INVALID;
  81. private ExecuteStreamHandler streamHandler;
  82. private ExecuteWatchdog watchdog;
  83. private File workingDirectory = null;
  84. private Project project = null;
  85. private boolean newEnvironment = false;
  86. /** Controls whether the VM is used to launch commands, where possible */
  87. private boolean useVMLauncher = true;
  88. private static String antWorkingDirectory = System.getProperty("user.dir");
  89. private static CommandLauncher vmLauncher = null;
  90. private static CommandLauncher shellLauncher = null;
  91. private static Vector procEnvironment = null;
  92. /** Used to destroy processes when the VM exits. */
  93. private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
  94. /**
  95. * Builds a command launcher for the OS and JVM we are running under
  96. */
  97. static {
  98. // Try using a JDK 1.3 launcher
  99. try {
  100. vmLauncher = new Java13CommandLauncher();
  101. }
  102. catch ( NoSuchMethodException exc ) {
  103. // Ignore and keep try
  104. }
  105. if ( Os.isFamily("mac") ) {
  106. // Mac
  107. shellLauncher = new MacCommandLauncher(new CommandLauncher());
  108. }
  109. else if ( Os.isFamily("os/2") ) {
  110. // OS/2 - use same mechanism as Windows 2000
  111. shellLauncher = new WinNTCommandLauncher(new CommandLauncher());
  112. }
  113. else if ( Os.isFamily("windows") ) {
  114. // Windows. Need to determine which JDK we're running in
  115. CommandLauncher baseLauncher;
  116. if ( System.getProperty("java.version").startsWith("1.1") ) {
  117. // JDK 1.1
  118. baseLauncher = new Java11CommandLauncher();
  119. }
  120. else {
  121. // JDK 1.2
  122. baseLauncher = new CommandLauncher();
  123. }
  124. if ( !Os.isFamily("win9x") ) {
  125. // Windows XP/2000/NT
  126. shellLauncher = new WinNTCommandLauncher(baseLauncher);
  127. }
  128. else {
  129. // Windows 98/95 - need to use an auxiliary script
  130. shellLauncher = new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
  131. }
  132. }
  133. else if ( Os.isFamily("netware") ) {
  134. // NetWare. Need to determine which JDK we're running in
  135. CommandLauncher baseLauncher;
  136. if ( System.getProperty("java.version").startsWith("1.1") ) {
  137. // JDK 1.1
  138. baseLauncher = new Java11CommandLauncher();
  139. }
  140. else {
  141. // JDK 1.2
  142. baseLauncher = new CommandLauncher();
  143. }
  144. shellLauncher = new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
  145. }
  146. else {
  147. // Generic
  148. shellLauncher = new ScriptCommandLauncher("bin/antRun", new CommandLauncher());
  149. }
  150. }
  151. /**
  152. * Find the list of environment variables for this process.
  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(out.toString()));
  171. String var = null;
  172. String line, lineSep = System.getProperty("line.separator");
  173. while ((line = in.readLine()) != null) {
  174. if (line.indexOf('=') == -1) {
  175. // Chunk part of previous env var (UNIX env vars can
  176. // contain embedded new lines).
  177. if (var == null) {
  178. var = lineSep + line;
  179. }
  180. else {
  181. var += lineSep + line;
  182. }
  183. }
  184. else {
  185. // New env var...append the previous one if we have it.
  186. if (var != null) {
  187. procEnvironment.addElement(var);
  188. }
  189. var = line;
  190. }
  191. }
  192. // Since we "look ahead" before adding, there's one last env var.
  193. if (var != null) {
  194. procEnvironment.addElement(var);
  195. }
  196. }
  197. catch (java.io.IOException exc) {
  198. exc.printStackTrace();
  199. // Just try to see how much we got
  200. }
  201. return procEnvironment;
  202. }
  203. private static String[] getProcEnvCommand() {
  204. if ( Os.isFamily("os/2") ) {
  205. // OS/2 - use same mechanism as Windows 2000
  206. // Not sure
  207. String[] cmd = {"cmd", "/c", "set" };
  208. return cmd;
  209. }
  210. else if ( Os.isFamily("windows") ) {
  211. // Determine if we're running under XP/2000/NT or 98/95
  212. if ( !Os.isFamily("win9x") ) {
  213. // Windows XP/2000/NT
  214. String[] cmd = {"cmd", "/c", "set" };
  215. return cmd;
  216. }
  217. else {
  218. // Windows 98/95
  219. String[] cmd = {"command.com", "/c", "set" };
  220. return cmd;
  221. }
  222. }
  223. else if ( Os.isFamily("unix") ) {
  224. // Generic UNIX
  225. // Alternatively one could use: /bin/sh -c env
  226. String[] cmd = {"/usr/bin/env"};
  227. return cmd;
  228. }
  229. else if ( Os.isFamily("netware") ) {
  230. String[] cmd = {"env"};
  231. return cmd;
  232. }
  233. else {
  234. // MAC OS 9 and previous
  235. // TODO: I have no idea how to get it, someone must fix it
  236. String[] cmd = null;
  237. return cmd;
  238. }
  239. }
  240. /**
  241. * Creates a new execute object using <code>PumpStreamHandler</code> for
  242. * stream handling.
  243. */
  244. public Execute() {
  245. this(new PumpStreamHandler(), null);
  246. }
  247. /**
  248. * Creates a new execute object.
  249. *
  250. * @param streamHandler the stream handler used to handle the input and
  251. * output streams of the subprocess.
  252. */
  253. public Execute(ExecuteStreamHandler streamHandler) {
  254. this(streamHandler, null);
  255. }
  256. /**
  257. * Creates a new execute object.
  258. *
  259. * @param streamHandler the stream handler used to handle the input and
  260. * output streams of the subprocess.
  261. * @param watchdog a watchdog for the subprocess or <code>null</code> to
  262. * to disable a timeout for the subprocess.
  263. */
  264. public Execute(ExecuteStreamHandler streamHandler, ExecuteWatchdog watchdog) {
  265. this.streamHandler = streamHandler;
  266. this.watchdog = watchdog;
  267. }
  268. /**
  269. * Returns the commandline used to create a subprocess.
  270. *
  271. * @return the commandline used to create a subprocess
  272. */
  273. public String[] getCommandline() {
  274. return cmdl;
  275. }
  276. /**
  277. * Sets the commandline of the subprocess to launch.
  278. *
  279. * @param commandline the commandline of the subprocess to launch
  280. */
  281. public void setCommandline(String[] commandline) {
  282. cmdl = commandline;
  283. }
  284. /**
  285. * Set whether to propagate the default environment or not.
  286. *
  287. * @param newenv whether to propagate the process environment.
  288. */
  289. public void setNewenvironment(boolean newenv) {
  290. newEnvironment = newenv;
  291. }
  292. /**
  293. * Returns the environment used to create a subprocess.
  294. *
  295. * @return the environment used to create a subprocess
  296. */
  297. public String[] getEnvironment() {
  298. if (env == null || newEnvironment) {
  299. return env;
  300. }
  301. return patchEnvironment();
  302. }
  303. /**
  304. * Sets the environment variables for the subprocess to launch.
  305. *
  306. * @param commandline array of Strings, each element of which has
  307. * an environment variable settings in format <em>key=value</em>
  308. */
  309. public void setEnvironment(String[] env) {
  310. this.env = env;
  311. }
  312. /**
  313. * Sets the working directory of the process to execute.
  314. *
  315. * <p>This is emulated using the antRun scripts unless the OS is
  316. * Windows NT in which case a cmd.exe is spawned,
  317. * or MRJ and setting user.dir works, or JDK 1.3 and there is
  318. * official support in java.lang.Runtime.
  319. *
  320. * @param wd the working directory of the process.
  321. */
  322. public void setWorkingDirectory(File wd) {
  323. if (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory)) {
  324. workingDirectory = null;
  325. } else {
  326. workingDirectory = wd;
  327. }
  328. }
  329. /**
  330. * Set the name of the antRun script using the project's value.
  331. *
  332. * @param project the current project.
  333. */
  334. public void setAntRun(Project project) throws BuildException {
  335. this.project = project;
  336. }
  337. /**
  338. * Launch this execution through the VM, where possible, rather than through
  339. * the OS's shell. In some cases and operating systems using the shell will
  340. * allow the shell to perform additional processing such as associating an
  341. * executable with a script, etc
  342. *
  343. * @param vmLauncher true if exec should launch through thge VM,
  344. * false if the shell should be used to launch the command.
  345. */
  346. public void setVMLauncher(boolean useVMLauncher) {
  347. this.useVMLauncher = useVMLauncher;
  348. }
  349. /**
  350. * Runs a process defined by the command line and returns its exit status.
  351. *
  352. * @return the exit status of the subprocess or <code>INVALID</code>
  353. * @exception java.io.IOExcpetion The exception is thrown, if launching
  354. * of the subprocess failed
  355. */
  356. public int execute() throws IOException {
  357. CommandLauncher launcher = vmLauncher != null ? vmLauncher : shellLauncher;
  358. if (!useVMLauncher) {
  359. launcher = shellLauncher;
  360. }
  361. final Process process = launcher.exec(project, getCommandline(), getEnvironment(), workingDirectory);
  362. try {
  363. streamHandler.setProcessInputStream(process.getOutputStream());
  364. streamHandler.setProcessOutputStream(process.getInputStream());
  365. streamHandler.setProcessErrorStream(process.getErrorStream());
  366. } catch (IOException e) {
  367. process.destroy();
  368. throw e;
  369. }
  370. streamHandler.start();
  371. // add the process to the list of those to destroy if the VM exits
  372. //
  373. processDestroyer.add(process);
  374. if (watchdog != null) {
  375. watchdog.start(process);
  376. }
  377. waitFor(process);
  378. // remove the process to the list of those to destroy if the VM exits
  379. //
  380. processDestroyer.remove(process);
  381. if (watchdog != null) {
  382. watchdog.stop();
  383. }
  384. streamHandler.stop();
  385. if (watchdog != null) {
  386. watchdog.checkException();
  387. }
  388. return getExitValue();
  389. }
  390. protected void waitFor(Process process) {
  391. try {
  392. process.waitFor();
  393. setExitValue(process.exitValue());
  394. } catch (InterruptedException e) {}
  395. }
  396. protected void setExitValue(int value) {
  397. exitValue = value;
  398. }
  399. /**
  400. * query the exit value of the process.
  401. * @return the exit value, 1 if the process was killed,
  402. * or Project.INVALID if no exit value has been received
  403. */
  404. public int getExitValue() {
  405. return exitValue;
  406. }
  407. /**
  408. * test for an untimely death of the process
  409. * @return true iff a watchdog had to kill the process
  410. * @since 1.5
  411. */
  412. public boolean killedProcess() {
  413. return watchdog!=null && watchdog.killedProcess();
  414. }
  415. /**
  416. * Patch the current environment with the new values from the user.
  417. * @return the patched environment
  418. */
  419. private String[] patchEnvironment() {
  420. Vector osEnv = (Vector) getProcEnvironment().clone();
  421. for (int i = 0; i < env.length; i++) {
  422. int pos = env[i].indexOf('=');
  423. // Get key including "="
  424. String key = env[i].substring(0, pos+1);
  425. int size = osEnv.size();
  426. for (int j = 0; j < size; j++) {
  427. if (((String)osEnv.elementAt(j)).startsWith(key)) {
  428. osEnv.removeElementAt(j);
  429. break;
  430. }
  431. }
  432. osEnv.addElement(env[i]);
  433. }
  434. String[] result = new String[osEnv.size()];
  435. osEnv.copyInto(result);
  436. return result;
  437. }
  438. /**
  439. * A utility method that runs an external command. Writes the output and
  440. * error streams of the command to the project log.
  441. *
  442. * @param task The task that the command is part of. Used for logging
  443. * @param cmdline The command to execute.
  444. *
  445. * @throws BuildException if the command does not return 0.
  446. */
  447. public static void runCommand(Task task, String[] cmdline) throws BuildException
  448. {
  449. try {
  450. task.log(Commandline.toString(cmdline), Project.MSG_VERBOSE);
  451. Execute exe = new Execute(new LogStreamHandler(task,
  452. Project.MSG_INFO,
  453. Project.MSG_ERR));
  454. exe.setAntRun(task.getProject());
  455. exe.setCommandline(cmdline);
  456. int retval = exe.execute();
  457. if ( retval != 0 ) {
  458. throw new BuildException(cmdline[0] + " failed with return code " + retval, task.getLocation());
  459. }
  460. }
  461. catch (java.io.IOException exc) {
  462. throw new BuildException("Could not launch " + cmdline[0] + ": " + exc, task.getLocation());
  463. }
  464. }
  465. /**
  466. * A command launcher for a particular JVM/OS platform. This class is
  467. * a general purpose command launcher which can only launch commands in
  468. * the current working directory.
  469. */
  470. private static class CommandLauncher
  471. {
  472. /**
  473. * Launches the given command in a new process.
  474. *
  475. * @param project The project that the command is part of
  476. * @param cmd The command to execute
  477. * @param env The environment for the new process. If null,
  478. * the environment of the current proccess is used.
  479. */
  480. public Process exec(Project project, String[] cmd, String[] env) throws IOException
  481. {
  482. if (project != null) {
  483. project.log("Execute:CommandLauncher: " +
  484. Commandline.toString(cmd), Project.MSG_DEBUG);
  485. }
  486. return Runtime.getRuntime().exec(cmd, env);
  487. }
  488. /**
  489. * Launches the given command in a new process, in the given working
  490. * directory.
  491. *
  492. * @param project The project that the command is part of
  493. * @param cmd The command to execute
  494. * @param env The environment for the new process. If null,
  495. * the environment of the current proccess is used.
  496. * @param workingDir The directory to start the command in. If null,
  497. * the current directory is used
  498. */
  499. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  500. {
  501. if ( workingDir == null ) {
  502. return exec(project, cmd, env);
  503. }
  504. throw new IOException("Cannot execute a process in different directory under this JVM");
  505. }
  506. }
  507. /**
  508. * A command launcher for JDK/JRE 1.1 under Windows. Fixes quoting problems
  509. * in Runtime.exec(). Can only launch commands in the current working
  510. * directory
  511. */
  512. private static class Java11CommandLauncher extends CommandLauncher
  513. {
  514. /**
  515. * Launches the given command in a new process. Needs to quote
  516. * arguments
  517. */
  518. public Process exec(Project project, String[] cmd, String[] env) throws IOException
  519. {
  520. // Need to quote arguments with spaces, and to escape quote characters
  521. String[] newcmd = new String[cmd.length];
  522. for ( int i = 0; i < cmd.length; i++ ) {
  523. newcmd[i] = Commandline.quoteArgument(cmd[i]);
  524. }
  525. if (project != null) {
  526. project.log("Execute:Java11CommandLauncher: " +
  527. Commandline.toString(newcmd), Project.MSG_DEBUG);
  528. }
  529. return Runtime.getRuntime().exec(newcmd, env);
  530. }
  531. }
  532. /**
  533. * A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
  534. * Runtime.exec() command
  535. */
  536. private static class Java13CommandLauncher extends CommandLauncher
  537. {
  538. public Java13CommandLauncher() throws NoSuchMethodException
  539. {
  540. // Locate method Runtime.exec(String[] cmdarray, String[] envp, File dir)
  541. _execWithCWD = Runtime.class.getMethod("exec", new Class[] {String[].class, String[].class, File.class});
  542. }
  543. /**
  544. * Launches the given command in a new process, in the given working
  545. * directory
  546. */
  547. public Process exec(Project project, String[] cmd, String[] env, File workingDir)
  548. throws IOException
  549. {
  550. try {
  551. if (project != null) {
  552. project.log("Execute:Java13CommandLauncher: " +
  553. Commandline.toString(cmd), Project.MSG_DEBUG);
  554. }
  555. Object[] arguments = { cmd, env, workingDir };
  556. return (Process)_execWithCWD.invoke(Runtime.getRuntime(), arguments);
  557. }
  558. catch (InvocationTargetException exc) {
  559. Throwable realexc = exc.getTargetException();
  560. if ( realexc instanceof ThreadDeath ) {
  561. throw (ThreadDeath)realexc;
  562. }
  563. else if ( realexc instanceof IOException ) {
  564. throw (IOException)realexc;
  565. }
  566. else {
  567. throw new BuildException("Unable to execute command", realexc);
  568. }
  569. }
  570. catch (Exception exc) {
  571. // IllegalAccess, IllegalArgument, ClassCast
  572. throw new BuildException("Unable to execute command", exc);
  573. }
  574. }
  575. private Method _execWithCWD;
  576. }
  577. /**
  578. * A command launcher that proxies another command launcher.
  579. *
  580. * Sub-classes override exec(args, env, workdir)
  581. */
  582. private static class CommandLauncherProxy extends CommandLauncher
  583. {
  584. CommandLauncherProxy(CommandLauncher launcher)
  585. {
  586. _launcher = launcher;
  587. }
  588. /**
  589. * Launches the given command in a new process. Delegates this
  590. * method to the proxied launcher
  591. */
  592. public Process exec(Project project, String[] cmd, String[] env) throws IOException
  593. {
  594. return _launcher.exec(project, cmd, env);
  595. }
  596. private CommandLauncher _launcher;
  597. }
  598. /**
  599. * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
  600. * launching commands in directories other than the current working
  601. * directory.
  602. */
  603. private static class WinNTCommandLauncher extends CommandLauncherProxy
  604. {
  605. WinNTCommandLauncher(CommandLauncher launcher)
  606. {
  607. super(launcher);
  608. }
  609. /**
  610. * Launches the given command in a new process, in the given working
  611. * directory.
  612. */
  613. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  614. {
  615. File commandDir = workingDir;
  616. if ( workingDir == null ) {
  617. if ( project != null ) {
  618. commandDir = project.getBaseDir();
  619. } else {
  620. return exec(project, cmd, env);
  621. }
  622. }
  623. // Use cmd.exe to change to the specified directory before running
  624. // the command
  625. final int preCmdLength = 6;
  626. String[] newcmd = new String[cmd.length + preCmdLength];
  627. newcmd[0] = "cmd";
  628. newcmd[1] = "/c";
  629. newcmd[2] = "cd";
  630. newcmd[3] = "/d";
  631. newcmd[4] = commandDir.getAbsolutePath();
  632. newcmd[5] = "&&";
  633. System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
  634. return exec(project, newcmd, env);
  635. }
  636. }
  637. /**
  638. * A command launcher for Mac that uses a dodgy mechanism to change
  639. * working directory before launching commands.
  640. */
  641. private static class MacCommandLauncher extends CommandLauncherProxy
  642. {
  643. MacCommandLauncher(CommandLauncher launcher)
  644. {
  645. super(launcher);
  646. }
  647. /**
  648. * Launches the given command in a new process, in the given working
  649. * directory
  650. */
  651. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  652. {
  653. if ( workingDir == null ) {
  654. return exec(project, cmd, env);
  655. }
  656. System.getProperties().put("user.dir", workingDir.getAbsolutePath());
  657. try {
  658. return exec(project, cmd, env);
  659. }
  660. finally {
  661. System.getProperties().put("user.dir", antWorkingDirectory);
  662. }
  663. }
  664. }
  665. /**
  666. * A command launcher that uses an auxiliary script to launch commands
  667. * in directories other than the current working directory.
  668. */
  669. private static class ScriptCommandLauncher extends CommandLauncherProxy
  670. {
  671. ScriptCommandLauncher(String script, CommandLauncher launcher)
  672. {
  673. super(launcher);
  674. _script = script;
  675. }
  676. /**
  677. * Launches the given command in a new process, in the given working
  678. * directory
  679. */
  680. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  681. {
  682. if ( project == null ) {
  683. if ( workingDir == null ) {
  684. return exec(project, cmd, env);
  685. }
  686. throw new IOException("Cannot locate antRun script: No project provided");
  687. }
  688. // Locate the auxiliary script
  689. String antHome = project.getProperty("ant.home");
  690. if ( antHome == null ) {
  691. throw new IOException("Cannot locate antRun script: Property 'ant.home' not found");
  692. }
  693. String antRun = project.resolveFile(antHome + File.separator + _script).toString();
  694. // Build the command
  695. File commandDir = workingDir;
  696. if ( workingDir == null && project != null ) {
  697. commandDir = project.getBaseDir();
  698. }
  699. String[] newcmd = new String[cmd.length + 2];
  700. newcmd[0] = antRun;
  701. newcmd[1] = commandDir.getAbsolutePath();
  702. System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
  703. return exec(project, newcmd, env);
  704. }
  705. private String _script;
  706. }
  707. /**
  708. * A command launcher that uses an auxiliary perl script to launch commands
  709. * in directories other than the current working directory.
  710. */
  711. private static class PerlScriptCommandLauncher extends CommandLauncherProxy
  712. {
  713. PerlScriptCommandLauncher(String script, CommandLauncher launcher)
  714. {
  715. super(launcher);
  716. _script = script;
  717. }
  718. /**
  719. * Launches the given command in a new process, in the given working
  720. * directory
  721. */
  722. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  723. {
  724. if ( project == null ) {
  725. if ( workingDir == null ) {
  726. return exec(project, cmd, env);
  727. }
  728. throw new IOException("Cannot locate antRun script: No project provided");
  729. }
  730. // Locate the auxiliary script
  731. String antHome = project.getProperty("ant.home");
  732. if ( antHome == null ) {
  733. throw new IOException("Cannot locate antRun script: Property 'ant.home' not found");
  734. }
  735. String antRun = project.resolveFile(antHome + File.separator + _script).toString();
  736. // Build the command
  737. File commandDir = workingDir;
  738. if ( workingDir == null && project != null ) {
  739. commandDir = project.getBaseDir();
  740. }
  741. String[] newcmd = new String[cmd.length + 3];
  742. newcmd[0] = "perl";
  743. newcmd[1] = antRun;
  744. newcmd[2] = commandDir.getAbsolutePath();
  745. System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
  746. return exec(project, newcmd, env);
  747. }
  748. private String _script;
  749. }
  750. }