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 30 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  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. * @author <a href="mailto:jtulley@novell.com">Jeff Tulley</a>
  73. *
  74. * @since Ant 1.2
  75. *
  76. * @version $Revision$
  77. */
  78. public class Execute {
  79. /** Invalid exit code. **/
  80. public final static int INVALID = Integer.MAX_VALUE;
  81. private String[] cmdl = null;
  82. private String[] env = null;
  83. private int exitValue = INVALID;
  84. private ExecuteStreamHandler streamHandler;
  85. private ExecuteWatchdog watchdog;
  86. private File workingDirectory = null;
  87. private Project project = null;
  88. private boolean newEnvironment = false;
  89. /** Controls whether the VM is used to launch commands, where possible */
  90. private boolean useVMLauncher = true;
  91. private static String antWorkingDirectory = System.getProperty("user.dir");
  92. private static CommandLauncher vmLauncher = null;
  93. private static CommandLauncher shellLauncher = null;
  94. private static Vector procEnvironment = null;
  95. /** Used to destroy processes when the VM exits. */
  96. private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
  97. /**
  98. * Builds a command launcher for the OS and JVM we are running under
  99. */
  100. static {
  101. // Try using a JDK 1.3 launcher
  102. try {
  103. vmLauncher = new Java13CommandLauncher();
  104. }
  105. catch (NoSuchMethodException exc) {
  106. // Ignore and keep trying
  107. }
  108. if (Os.isFamily("mac")) {
  109. // Mac
  110. shellLauncher = new MacCommandLauncher(new CommandLauncher());
  111. }
  112. else if (Os.isFamily("os/2")) {
  113. // OS/2 - use same mechanism as Windows 2000
  114. shellLauncher = new WinNTCommandLauncher(new CommandLauncher());
  115. }
  116. else if (Os.isFamily("windows")) {
  117. // Windows. Need to determine which JDK we're running in
  118. CommandLauncher baseLauncher;
  119. if (System.getProperty("java.version").startsWith("1.1")) {
  120. // JDK 1.1
  121. baseLauncher = new Java11CommandLauncher();
  122. }
  123. else {
  124. // JDK 1.2
  125. baseLauncher = new CommandLauncher();
  126. }
  127. if (!Os.isFamily("win9x")) {
  128. // Windows XP/2000/NT
  129. shellLauncher = new WinNTCommandLauncher(baseLauncher);
  130. }
  131. else {
  132. // Windows 98/95 - need to use an auxiliary script
  133. shellLauncher = new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
  134. }
  135. }
  136. else if (Os.isFamily("netware")) {
  137. // NetWare. Need to determine which JDK we're running in
  138. CommandLauncher baseLauncher;
  139. if (System.getProperty("java.version").startsWith("1.1")) {
  140. // JDK 1.1
  141. baseLauncher = new Java11CommandLauncher();
  142. }
  143. else {
  144. // JDK 1.2
  145. baseLauncher = new CommandLauncher();
  146. }
  147. shellLauncher = new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
  148. }
  149. else {
  150. // Generic
  151. shellLauncher = new ScriptCommandLauncher("bin/antRun", new CommandLauncher());
  152. }
  153. }
  154. /**
  155. * Find the list of environment variables for this process.
  156. */
  157. public static synchronized Vector getProcEnvironment() {
  158. if (procEnvironment != null) {
  159. return procEnvironment;
  160. }
  161. procEnvironment = new Vector();
  162. try {
  163. ByteArrayOutputStream out = new ByteArrayOutputStream();
  164. Execute exe = new Execute(new PumpStreamHandler(out));
  165. exe.setCommandline(getProcEnvCommand());
  166. // Make sure we do not recurse forever
  167. exe.setNewenvironment(true);
  168. int retval = exe.execute();
  169. if (retval != 0) {
  170. // Just try to use what we got
  171. }
  172. BufferedReader in =
  173. new BufferedReader(new StringReader(out.toString()));
  174. String var = null;
  175. String line, lineSep = System.getProperty("line.separator");
  176. while ((line = in.readLine()) != null) {
  177. if (line.indexOf('=') == -1) {
  178. // Chunk part of previous env var (UNIX env vars can
  179. // contain embedded new lines).
  180. if (var == null) {
  181. var = lineSep + line;
  182. }
  183. else {
  184. var += lineSep + line;
  185. }
  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. }
  200. catch (java.io.IOException exc) {
  201. exc.printStackTrace();
  202. // Just try to see how much we got
  203. }
  204. return procEnvironment;
  205. }
  206. private static String[] getProcEnvCommand() {
  207. if (Os.isFamily("os/2")) {
  208. // OS/2 - use same mechanism as Windows 2000
  209. // Not sure
  210. String[] cmd = {"cmd", "/c", "set" };
  211. return cmd;
  212. }
  213. else if (Os.isFamily("windows")) {
  214. // Determine if we're running under XP/2000/NT or 98/95
  215. if (!Os.isFamily("win9x")) {
  216. // Windows XP/2000/NT
  217. String[] cmd = {"cmd", "/c", "set" };
  218. return cmd;
  219. }
  220. else {
  221. // Windows 98/95
  222. String[] cmd = {"command.com", "/c", "set" };
  223. return cmd;
  224. }
  225. }
  226. else if (Os.isFamily("unix")) {
  227. // Generic UNIX
  228. // Alternatively one could use: /bin/sh -c env
  229. String[] cmd = {"/usr/bin/env"};
  230. return cmd;
  231. }
  232. else if (Os.isFamily("netware")) {
  233. String[] cmd = {"env"};
  234. return cmd;
  235. }
  236. else {
  237. // MAC OS 9 and previous
  238. // TODO: I have no idea how to get it, someone must fix it
  239. String[] cmd = null;
  240. return cmd;
  241. }
  242. }
  243. /**
  244. * Creates a new execute object using <code>PumpStreamHandler</code> for
  245. * stream handling.
  246. */
  247. public Execute() {
  248. this(new PumpStreamHandler(), null);
  249. }
  250. /**
  251. * Creates a new execute object.
  252. *
  253. * @param streamHandler the stream handler used to handle the input and
  254. * output streams of the subprocess.
  255. */
  256. public Execute(ExecuteStreamHandler streamHandler) {
  257. this(streamHandler, null);
  258. }
  259. /**
  260. * Creates a new execute object.
  261. *
  262. * @param streamHandler the stream handler used to handle the input and
  263. * output streams of the subprocess.
  264. * @param watchdog a watchdog for the subprocess or <code>null</code> to
  265. * to disable a timeout for the subprocess.
  266. */
  267. public Execute(ExecuteStreamHandler streamHandler, ExecuteWatchdog watchdog) {
  268. this.streamHandler = streamHandler;
  269. this.watchdog = watchdog;
  270. }
  271. /**
  272. * Returns the commandline used to create a subprocess.
  273. *
  274. * @return the commandline used to create a subprocess
  275. */
  276. public String[] getCommandline() {
  277. return cmdl;
  278. }
  279. /**
  280. * Sets the commandline of the subprocess to launch.
  281. *
  282. * @param commandline the commandline of the subprocess to launch
  283. */
  284. public void setCommandline(String[] commandline) {
  285. cmdl = commandline;
  286. }
  287. /**
  288. * Set whether to propagate the default environment or not.
  289. *
  290. * @param newenv whether to propagate the process environment.
  291. */
  292. public void setNewenvironment(boolean newenv) {
  293. newEnvironment = newenv;
  294. }
  295. /**
  296. * Returns the environment used to create a subprocess.
  297. *
  298. * @return the environment used to create a subprocess
  299. */
  300. public String[] getEnvironment() {
  301. if (env == null || newEnvironment) {
  302. return env;
  303. }
  304. return patchEnvironment();
  305. }
  306. /**
  307. * Sets the environment variables for the subprocess to launch.
  308. *
  309. * @param commandline array of Strings, each element of which has
  310. * an environment variable settings in format <em>key=value</em>
  311. */
  312. public void setEnvironment(String[] env) {
  313. this.env = env;
  314. }
  315. /**
  316. * Sets the working directory of the process to execute.
  317. *
  318. * <p>This is emulated using the antRun scripts unless the OS is
  319. * Windows NT in which case a cmd.exe is spawned,
  320. * or MRJ and setting user.dir works, or JDK 1.3 and there is
  321. * official support in java.lang.Runtime.
  322. *
  323. * @param wd the working directory of the process.
  324. */
  325. public void setWorkingDirectory(File wd) {
  326. if (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory)) {
  327. workingDirectory = null;
  328. } else {
  329. workingDirectory = wd;
  330. }
  331. }
  332. /**
  333. * Set the name of the antRun script using the project's value.
  334. *
  335. * @param project the current project.
  336. */
  337. public void setAntRun(Project project) throws BuildException {
  338. this.project = project;
  339. }
  340. /**
  341. * Launch this execution through the VM, where possible, rather than through
  342. * the OS's shell. In some cases and operating systems using the shell will
  343. * allow the shell to perform additional processing such as associating an
  344. * executable with a script, etc
  345. *
  346. * @param vmLauncher true if exec should launch through thge VM,
  347. * false if the shell should be used to launch the command.
  348. */
  349. public void setVMLauncher(boolean useVMLauncher) {
  350. this.useVMLauncher = useVMLauncher;
  351. }
  352. /**
  353. * Creates a process that runs a command.
  354. *
  355. * @param project the Project, only used for logging purposes, may be null.
  356. * @param command the command to run
  357. * @param env the environment for the command
  358. * @param the working directory for the command
  359. * @param useVM use the built-in exec command for JDK 1.3 if available.
  360. *
  361. * @since Ant 1.5
  362. */
  363. public static Process launch(Project project, String[] command,
  364. String[] env, File dir, boolean useVM)
  365. throws IOException {
  366. CommandLauncher launcher = vmLauncher != null ? vmLauncher : shellLauncher;
  367. if (!useVM) {
  368. launcher = shellLauncher;
  369. }
  370. return launcher.exec(project, command, env, dir);
  371. }
  372. /**
  373. * Runs a process defined by the command line and returns its exit status.
  374. *
  375. * @return the exit status of the subprocess or <code>INVALID</code>
  376. * @exception java.io.IOExcpetion The exception is thrown, if launching
  377. * of the subprocess failed
  378. */
  379. public int execute() throws IOException {
  380. final Process process = launch(project, getCommandline(),
  381. getEnvironment(), workingDirectory,
  382. useVMLauncher);
  383. try {
  384. streamHandler.setProcessInputStream(process.getOutputStream());
  385. streamHandler.setProcessOutputStream(process.getInputStream());
  386. streamHandler.setProcessErrorStream(process.getErrorStream());
  387. } catch (IOException e) {
  388. process.destroy();
  389. throw e;
  390. }
  391. streamHandler.start();
  392. // add the process to the list of those to destroy if the VM exits
  393. //
  394. processDestroyer.add(process);
  395. if (watchdog != null) {
  396. watchdog.start(process);
  397. }
  398. waitFor(process);
  399. // remove the process to the list of those to destroy if the VM exits
  400. //
  401. processDestroyer.remove(process);
  402. if (watchdog != null) {
  403. watchdog.stop();
  404. }
  405. streamHandler.stop();
  406. if (watchdog != null) {
  407. watchdog.checkException();
  408. }
  409. return getExitValue();
  410. }
  411. protected void waitFor(Process process) {
  412. try {
  413. process.waitFor();
  414. setExitValue(process.exitValue());
  415. } catch (InterruptedException e) {}
  416. }
  417. protected void setExitValue(int value) {
  418. exitValue = value;
  419. }
  420. /**
  421. * query the exit value of the process.
  422. * @return the exit value, 1 if the process was killed,
  423. * or Project.INVALID if no exit value has been received
  424. */
  425. public int getExitValue() {
  426. return exitValue;
  427. }
  428. /**
  429. * test for an untimely death of the process
  430. * @return true iff a watchdog had to kill the process
  431. * @since Ant 1.5
  432. */
  433. public boolean killedProcess() {
  434. return watchdog != null && watchdog.killedProcess();
  435. }
  436. /**
  437. * Patch the current environment with the new values from the user.
  438. * @return the patched environment
  439. */
  440. private String[] patchEnvironment() {
  441. Vector osEnv = (Vector) getProcEnvironment().clone();
  442. for (int i = 0; i < env.length; i++) {
  443. int pos = env[i].indexOf('=');
  444. // Get key including "="
  445. String key = env[i].substring(0, pos + 1);
  446. int size = osEnv.size();
  447. for (int j = 0; j < size; j++) {
  448. if (((String) osEnv.elementAt(j)).startsWith(key)) {
  449. osEnv.removeElementAt(j);
  450. break;
  451. }
  452. }
  453. osEnv.addElement(env[i]);
  454. }
  455. String[] result = new String[osEnv.size()];
  456. osEnv.copyInto(result);
  457. return result;
  458. }
  459. /**
  460. * A utility method that runs an external command. Writes the output and
  461. * error streams of the command to the project log.
  462. *
  463. * @param task The task that the command is part of. Used for logging
  464. * @param cmdline The command to execute.
  465. *
  466. * @throws BuildException if the command does not return 0.
  467. */
  468. public static void runCommand(Task task, String[] cmdline) throws BuildException
  469. {
  470. try {
  471. task.log(Commandline.toString(cmdline), Project.MSG_VERBOSE);
  472. Execute exe = new Execute(new LogStreamHandler(task,
  473. Project.MSG_INFO,
  474. Project.MSG_ERR));
  475. exe.setAntRun(task.getProject());
  476. exe.setCommandline(cmdline);
  477. int retval = exe.execute();
  478. if (retval != 0) {
  479. throw new BuildException(cmdline[0]
  480. + " failed with return code " + retval, task.getLocation());
  481. }
  482. }
  483. catch (java.io.IOException exc) {
  484. throw new BuildException("Could not launch " + cmdline[0] + ": "
  485. + exc, task.getLocation());
  486. }
  487. }
  488. /**
  489. * A command launcher for a particular JVM/OS platform. This class is
  490. * a general purpose command launcher which can only launch commands in
  491. * the current working directory.
  492. */
  493. private static class CommandLauncher
  494. {
  495. /**
  496. * Launches the given command in a new process.
  497. *
  498. * @param project The project that the command is part of
  499. * @param cmd The command to execute
  500. * @param env The environment for the new process. If null,
  501. * the environment of the current proccess is used.
  502. */
  503. public Process exec(Project project, String[] cmd, String[] env)
  504. throws IOException {
  505. if (project != null) {
  506. project.log("Execute:CommandLauncher: " +
  507. Commandline.toString(cmd), Project.MSG_DEBUG);
  508. }
  509. return Runtime.getRuntime().exec(cmd, env);
  510. }
  511. /**
  512. * Launches the given command in a new process, in the given working
  513. * directory.
  514. *
  515. * @param project The project that the command is part of
  516. * @param cmd The command to execute
  517. * @param env The environment for the new process. If null,
  518. * the environment of the current proccess is used.
  519. * @param workingDir The directory to start the command in. If null,
  520. * the current directory is used
  521. */
  522. public Process exec(Project project, String[] cmd, String[] env,
  523. File workingDir) throws IOException {
  524. if (workingDir == null) {
  525. return exec(project, cmd, env);
  526. }
  527. throw new IOException("Cannot execute a process in different "
  528. + "directory under this JVM");
  529. }
  530. }
  531. /**
  532. * A command launcher for JDK/JRE 1.1 under Windows. Fixes quoting problems
  533. * in Runtime.exec(). Can only launch commands in the current working
  534. * directory
  535. */
  536. private static class Java11CommandLauncher extends CommandLauncher {
  537. /**
  538. * Launches the given command in a new process. Needs to quote
  539. * arguments
  540. */
  541. public Process exec(Project project, String[] cmd, String[] env)
  542. throws IOException {
  543. // Need to quote arguments with spaces, and to escape
  544. // quote characters
  545. String[] newcmd = new String[cmd.length];
  546. for (int i = 0; i < cmd.length; i++) {
  547. newcmd[i] = Commandline.quoteArgument(cmd[i]);
  548. }
  549. if (project != null) {
  550. project.log("Execute:Java11CommandLauncher: " +
  551. Commandline.toString(newcmd), Project.MSG_DEBUG);
  552. }
  553. return Runtime.getRuntime().exec(newcmd, env);
  554. }
  555. }
  556. /**
  557. * A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
  558. * Runtime.exec() command
  559. */
  560. private static class Java13CommandLauncher extends CommandLauncher
  561. {
  562. public Java13CommandLauncher() throws NoSuchMethodException
  563. {
  564. // Locate method Runtime.exec(String[] cmdarray,
  565. // String[] envp, File dir)
  566. _execWithCWD = Runtime.class.getMethod("exec",
  567. new Class[] {String[].class, String[].class, File.class});
  568. }
  569. /**
  570. * Launches the given command in a new process, in the given working
  571. * directory
  572. */
  573. public Process exec(Project project, String[] cmd, String[] env, File workingDir)
  574. throws IOException
  575. {
  576. try {
  577. if (project != null) {
  578. project.log("Execute:Java13CommandLauncher: " +
  579. Commandline.toString(cmd), Project.MSG_DEBUG);
  580. }
  581. Object[] arguments = { cmd, env, workingDir };
  582. return (Process) _execWithCWD.invoke(Runtime.getRuntime(),
  583. arguments);
  584. }
  585. catch (InvocationTargetException exc) {
  586. Throwable realexc = exc.getTargetException();
  587. if (realexc instanceof ThreadDeath) {
  588. throw (ThreadDeath) realexc;
  589. }
  590. else if (realexc instanceof IOException) {
  591. throw (IOException) realexc;
  592. }
  593. else {
  594. throw new BuildException("Unable to execute command", realexc);
  595. }
  596. }
  597. catch (Exception exc) {
  598. // IllegalAccess, IllegalArgument, ClassCast
  599. throw new BuildException("Unable to execute command", exc);
  600. }
  601. }
  602. private Method _execWithCWD;
  603. }
  604. /**
  605. * A command launcher that proxies another command launcher.
  606. *
  607. * Sub-classes override exec(args, env, workdir)
  608. */
  609. private static class CommandLauncherProxy extends CommandLauncher
  610. {
  611. CommandLauncherProxy(CommandLauncher launcher)
  612. {
  613. _launcher = launcher;
  614. }
  615. /**
  616. * Launches the given command in a new process. Delegates this
  617. * method to the proxied launcher
  618. */
  619. public Process exec(Project project, String[] cmd, String[] env) throws IOException
  620. {
  621. return _launcher.exec(project, cmd, env);
  622. }
  623. private CommandLauncher _launcher;
  624. }
  625. /**
  626. * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
  627. * launching commands in directories other than the current working
  628. * directory.
  629. */
  630. private static class WinNTCommandLauncher extends CommandLauncherProxy
  631. {
  632. WinNTCommandLauncher(CommandLauncher launcher)
  633. {
  634. super(launcher);
  635. }
  636. /**
  637. * Launches the given command in a new process, in the given working
  638. * directory.
  639. */
  640. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  641. {
  642. File commandDir = workingDir;
  643. if (workingDir == null) {
  644. if (project != null) {
  645. commandDir = project.getBaseDir();
  646. } else {
  647. return exec(project, cmd, env);
  648. }
  649. }
  650. // Use cmd.exe to change to the specified directory before running
  651. // the command
  652. final int preCmdLength = 6;
  653. String[] newcmd = new String[cmd.length + preCmdLength];
  654. newcmd[0] = "cmd";
  655. newcmd[1] = "/c";
  656. newcmd[2] = "cd";
  657. newcmd[3] = "/d";
  658. newcmd[4] = commandDir.getAbsolutePath();
  659. newcmd[5] = "&&";
  660. System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
  661. return exec(project, newcmd, env);
  662. }
  663. }
  664. /**
  665. * A command launcher for Mac that uses a dodgy mechanism to change
  666. * working directory before launching commands.
  667. */
  668. private static class MacCommandLauncher extends CommandLauncherProxy
  669. {
  670. MacCommandLauncher(CommandLauncher launcher)
  671. {
  672. super(launcher);
  673. }
  674. /**
  675. * Launches the given command in a new process, in the given working
  676. * directory
  677. */
  678. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  679. {
  680. if (workingDir == null) {
  681. return exec(project, cmd, env);
  682. }
  683. System.getProperties().put("user.dir", workingDir.getAbsolutePath());
  684. try {
  685. return exec(project, cmd, env);
  686. }
  687. finally {
  688. System.getProperties().put("user.dir", antWorkingDirectory);
  689. }
  690. }
  691. }
  692. /**
  693. * A command launcher that uses an auxiliary script to launch commands
  694. * in directories other than the current working directory.
  695. */
  696. private static class ScriptCommandLauncher extends CommandLauncherProxy
  697. {
  698. ScriptCommandLauncher(String script, CommandLauncher launcher)
  699. {
  700. super(launcher);
  701. _script = script;
  702. }
  703. /**
  704. * Launches the given command in a new process, in the given working
  705. * directory
  706. */
  707. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  708. {
  709. if (project == null) {
  710. if (workingDir == null) {
  711. return exec(project, cmd, env);
  712. }
  713. throw new IOException("Cannot locate antRun script: No project provided");
  714. }
  715. // Locate the auxiliary script
  716. String antHome = project.getProperty("ant.home");
  717. if (antHome == null) {
  718. throw new IOException("Cannot locate antRun script: Property 'ant.home' not found");
  719. }
  720. String antRun = project.resolveFile(antHome + File.separator + _script).toString();
  721. // Build the command
  722. File commandDir = workingDir;
  723. if (workingDir == null && project != null) {
  724. commandDir = project.getBaseDir();
  725. }
  726. String[] newcmd = new String[cmd.length + 2];
  727. newcmd[0] = antRun;
  728. newcmd[1] = commandDir.getAbsolutePath();
  729. System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
  730. return exec(project, newcmd, env);
  731. }
  732. private String _script;
  733. }
  734. /**
  735. * A command launcher that uses an auxiliary perl script to launch commands
  736. * in directories other than the current working directory.
  737. */
  738. private static class PerlScriptCommandLauncher extends CommandLauncherProxy
  739. {
  740. PerlScriptCommandLauncher(String script, CommandLauncher launcher)
  741. {
  742. super(launcher);
  743. _script = script;
  744. }
  745. /**
  746. * Launches the given command in a new process, in the given working
  747. * directory
  748. */
  749. public Process exec(Project project, String[] cmd, String[] env, File workingDir) throws IOException
  750. {
  751. if (project == null) {
  752. if (workingDir == null) {
  753. return exec(project, cmd, env);
  754. }
  755. throw new IOException("Cannot locate antRun script: No project provided");
  756. }
  757. // Locate the auxiliary script
  758. String antHome = project.getProperty("ant.home");
  759. if (antHome == null) {
  760. throw new IOException("Cannot locate antRun script: Property 'ant.home' not found");
  761. }
  762. String antRun = project.resolveFile(antHome + File.separator + _script).toString();
  763. // Build the command
  764. File commandDir = workingDir;
  765. if (workingDir == null && project != null) {
  766. commandDir = project.getBaseDir();
  767. }
  768. String[] newcmd = new String[cmd.length + 3];
  769. newcmd[0] = "perl";
  770. newcmd[1] = antRun;
  771. newcmd[2] = commandDir.getAbsolutePath();
  772. System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
  773. return exec(project, newcmd, env);
  774. }
  775. private String _script;
  776. }
  777. }