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.

Ant.java 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2003 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 "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 java.io.File;
  56. import java.io.FileOutputStream;
  57. import java.io.IOException;
  58. import java.io.PrintStream;
  59. import java.lang.reflect.Method;
  60. import java.util.Enumeration;
  61. import java.util.Hashtable;
  62. import java.util.Vector;
  63. import java.util.Set;
  64. import java.util.HashSet;
  65. import org.apache.tools.ant.BuildException;
  66. import org.apache.tools.ant.BuildListener;
  67. import org.apache.tools.ant.DefaultLogger;
  68. import org.apache.tools.ant.Project;
  69. import org.apache.tools.ant.ProjectComponent;
  70. import org.apache.tools.ant.ProjectHelper;
  71. import org.apache.tools.ant.Target;
  72. import org.apache.tools.ant.Task;
  73. import org.apache.tools.ant.types.PropertySet;
  74. import org.apache.tools.ant.util.FileUtils;
  75. /**
  76. * Build a sub-project.
  77. *
  78. * <pre>
  79. * &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
  80. * &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
  81. * &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
  82. * &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
  83. * &lt;/ant&gt;</SPAN>
  84. * &lt;/target&gt;</SPAN>
  85. *
  86. * &lt;target name=&quot;bar&quot; depends=&quot;init&quot;&gt;
  87. * &lt;echo message=&quot;prop is ${property1} ${foo}&quot; /&gt;
  88. * &lt;/target&gt;
  89. * </pre>
  90. *
  91. *
  92. * @author Costin Manolache
  93. *
  94. * @since Ant 1.1
  95. *
  96. * @ant.task category="control"
  97. */
  98. public class Ant extends Task {
  99. /** the basedir where is executed the build file */
  100. private File dir = null;
  101. /**
  102. * the build.xml file (can be absolute) in this case dir will be
  103. * ignored
  104. */
  105. private String antFile = null;
  106. /** the target to call if any */
  107. private String target = null;
  108. /** the output */
  109. private String output = null;
  110. /** should we inherit properties from the parent ? */
  111. private boolean inheritAll = true;
  112. /** should we inherit references from the parent ? */
  113. private boolean inheritRefs = false;
  114. /** the properties to pass to the new project */
  115. private Vector properties = new Vector();
  116. /** the references to pass to the new project */
  117. private Vector references = new Vector();
  118. /** the temporary project created to run the build file */
  119. private Project newProject;
  120. /** The stream to which output is to be written. */
  121. private PrintStream out = null;
  122. /** the sets of properties to pass to the new project */
  123. private Vector propertySets = new Vector();
  124. /**
  125. * If true, pass all properties to the new Ant project.
  126. * Defaults to true.
  127. * @param value if true pass all properties to the new Ant project.
  128. */
  129. public void setInheritAll(boolean value) {
  130. inheritAll = value;
  131. }
  132. /**
  133. * If true, pass all references to the new Ant project.
  134. * Defaults to false.
  135. * @param value if true, pass all references to the new Ant project
  136. */
  137. public void setInheritRefs(boolean value) {
  138. inheritRefs = value;
  139. }
  140. /**
  141. * Creates a Project instance for the project to call.
  142. */
  143. public void init() {
  144. newProject = new Project();
  145. newProject.setDefaultInputStream(getProject().getDefaultInputStream());
  146. newProject.setJavaVersionProperty();
  147. }
  148. /**
  149. * Called in execute or createProperty if newProject is null.
  150. *
  151. * <p>This can happen if the same instance of this task is run
  152. * twice as newProject is set to null at the end of execute (to
  153. * save memory and help the GC).</p>
  154. * <p>calls init() again</p>
  155. *
  156. */
  157. private void reinit() {
  158. init();
  159. }
  160. /**
  161. * Attaches the build listeners of the current project to the new
  162. * project, configures a possible logfile, transfers task and
  163. * data-type definitions, transfers properties (either all or just
  164. * the ones specified as user properties to the current project,
  165. * depending on inheritall), transfers the input handler.
  166. */
  167. private void initializeProject() {
  168. newProject.setInputHandler(getProject().getInputHandler());
  169. Vector listeners = getProject().getBuildListeners();
  170. final int count = listeners.size();
  171. for (int i = 0; i < count; i++) {
  172. newProject.addBuildListener((BuildListener) listeners.elementAt(i));
  173. }
  174. if (output != null) {
  175. File outfile = null;
  176. if (dir != null) {
  177. outfile = FileUtils.newFileUtils().resolveFile(dir, output);
  178. } else {
  179. outfile = getProject().resolveFile(output);
  180. }
  181. try {
  182. out = new PrintStream(new FileOutputStream(outfile));
  183. DefaultLogger logger = new DefaultLogger();
  184. logger.setMessageOutputLevel(Project.MSG_INFO);
  185. logger.setOutputPrintStream(out);
  186. logger.setErrorPrintStream(out);
  187. newProject.addBuildListener(logger);
  188. } catch (IOException ex) {
  189. log("Ant: Can't set output to " + output);
  190. }
  191. }
  192. getProject().initSubProject(newProject);
  193. // set user-defined properties
  194. getProject().copyUserProperties(newProject);
  195. if (!inheritAll) {
  196. // set Java built-in properties separately,
  197. // b/c we won't inherit them.
  198. newProject.setSystemProperties();
  199. } else {
  200. // set all properties from calling project
  201. addAlmostAll(getProject().getProperties());
  202. }
  203. Enumeration e = propertySets.elements();
  204. while (e.hasMoreElements()) {
  205. PropertySet ps = (PropertySet) e.nextElement();
  206. addAlmostAll(ps.getProperties());
  207. }
  208. }
  209. /**
  210. * Pass output sent to System.out to the new project.
  211. *
  212. * @param output a line of output
  213. * @since Ant 1.5
  214. */
  215. public void handleOutput(String output) {
  216. if (newProject != null) {
  217. newProject.demuxOutput(output, false);
  218. } else {
  219. super.handleOutput(output);
  220. }
  221. }
  222. /**
  223. * Process input into the ant task
  224. *
  225. * @param buffer the buffer into which data is to be read.
  226. * @param offset the offset into the buffer at which data is stored.
  227. * @param length the amount of data to read
  228. *
  229. * @return the number of bytes read
  230. *
  231. * @exception IOException if the data cannot be read
  232. *
  233. * @see Task#handleInput(byte[], int, int)
  234. *
  235. * @since Ant 1.6
  236. */
  237. public int handleInput(byte[] buffer, int offset, int length)
  238. throws IOException {
  239. if (newProject != null) {
  240. return newProject.demuxInput(buffer, offset, length);
  241. } else {
  242. return super.handleInput(buffer, offset, length);
  243. }
  244. }
  245. /**
  246. * Pass output sent to System.out to the new project.
  247. *
  248. * @param output The output to log. Should not be <code>null</code>.
  249. *
  250. * @since Ant 1.5.2
  251. */
  252. public void handleFlush(String output) {
  253. if (newProject != null) {
  254. newProject.demuxFlush(output, false);
  255. } else {
  256. super.handleFlush(output);
  257. }
  258. }
  259. /**
  260. * Pass output sent to System.err to the new project.
  261. *
  262. * @param output The error output to log. Should not be <code>null</code>.
  263. *
  264. * @since Ant 1.5
  265. */
  266. public void handleErrorOutput(String output) {
  267. if (newProject != null) {
  268. newProject.demuxOutput(output, true);
  269. } else {
  270. super.handleErrorOutput(output);
  271. }
  272. }
  273. /**
  274. * Pass output sent to System.err to the new project.
  275. *
  276. * @param output The error output to log. Should not be <code>null</code>.
  277. *
  278. * @since Ant 1.5.2
  279. */
  280. public void handleErrorFlush(String output) {
  281. if (newProject != null) {
  282. newProject.demuxFlush(output, true);
  283. } else {
  284. super.handleErrorFlush(output);
  285. }
  286. }
  287. /**
  288. * Do the execution.
  289. * @throws BuildException if a target tries to call itself
  290. * probably also if a BuildException is thrown by the new project
  291. */
  292. public void execute() throws BuildException {
  293. File savedDir = dir;
  294. String savedAntFile = antFile;
  295. String savedTarget = target;
  296. try {
  297. if (newProject == null) {
  298. reinit();
  299. }
  300. if ((dir == null) && (inheritAll)) {
  301. dir = getProject().getBaseDir();
  302. }
  303. initializeProject();
  304. if (dir != null) {
  305. newProject.setBaseDir(dir);
  306. if (savedDir != null) {
  307. // has been set explicitly
  308. newProject.setInheritedProperty("basedir" ,
  309. dir.getAbsolutePath());
  310. }
  311. } else {
  312. dir = getProject().getBaseDir();
  313. }
  314. overrideProperties();
  315. if (antFile == null) {
  316. antFile = "build.xml";
  317. }
  318. File file = FileUtils.newFileUtils().resolveFile(dir, antFile);
  319. antFile = file.getAbsolutePath();
  320. log("calling target " + (target != null ? target : "[default]")
  321. + " in build file " + antFile, Project.MSG_VERBOSE);
  322. newProject.setUserProperty("ant.file" , antFile);
  323. // Are we trying to call the target in which we are defined (or
  324. // the build file if this is a top level task)?
  325. if (newProject.getProperty("ant.file")
  326. .equals(getProject().getProperty("ant.file"))
  327. && getOwningTarget() != null) {
  328. if (getOwningTarget().getName().equals("")) {
  329. if (getTaskName().equals("antcall")) {
  330. throw new BuildException("antcall must not be used at"
  331. + " the top level.");
  332. } else {
  333. throw new BuildException(getTaskName() + " task at the"
  334. + " top level must not invoke"
  335. + " its own build file.");
  336. }
  337. }
  338. }
  339. ProjectHelper.configureProject(newProject, new File(antFile));
  340. if (target == null) {
  341. target = newProject.getDefaultTarget();
  342. }
  343. if (newProject.getProperty("ant.file")
  344. .equals(getProject().getProperty("ant.file"))
  345. && getOwningTarget() != null) {
  346. String owningTargetName = getOwningTarget().getName();
  347. if (owningTargetName.equals(target)) {
  348. throw new BuildException(getTaskName() + " task calling "
  349. + "its own parent target.");
  350. } else {
  351. Target other =
  352. (Target) getProject().getTargets().get(target);
  353. if (other != null && other.dependsOn(owningTargetName)) {
  354. throw new BuildException(getTaskName()
  355. + " task calling a target"
  356. + " that depends on"
  357. + " its parent target \'"
  358. + owningTargetName
  359. + "\'.");
  360. }
  361. }
  362. }
  363. addReferences();
  364. if (target != null && !"".equals(target)) {
  365. try {
  366. log("Entering " + antFile + "...", Project.MSG_VERBOSE);
  367. newProject.executeTarget(target);
  368. } finally {
  369. log("Exiting " + antFile + ".", Project.MSG_VERBOSE);
  370. }
  371. }
  372. } finally {
  373. // help the gc
  374. newProject = null;
  375. Enumeration e = properties.elements();
  376. while (e.hasMoreElements()) {
  377. Property p = (Property) e.nextElement();
  378. p.setProject(null);
  379. }
  380. if (output != null && out != null) {
  381. try {
  382. out.close();
  383. } catch (final Exception ex) {
  384. //ignore
  385. }
  386. }
  387. dir = savedDir;
  388. antFile = savedAntFile;
  389. target = savedTarget;
  390. }
  391. }
  392. /**
  393. * Override the properties in the new project with the one
  394. * explicitly defined as nested elements here.
  395. * @throws BuildException under unknown circumstances
  396. */
  397. private void overrideProperties() throws BuildException {
  398. // remove duplicate properties - last property wins
  399. // Needed for backward compatibility
  400. Set set = new HashSet();
  401. for (int i = properties.size() - 1; i >= 0; --i) {
  402. Property p = (Property) properties.get(i);
  403. if (set.contains(p.getName())) {
  404. properties.remove(i);
  405. } else {
  406. set.add(p.getName());
  407. }
  408. }
  409. Enumeration e = properties.elements();
  410. while (e.hasMoreElements()) {
  411. Property p = (Property) e.nextElement();
  412. p.setProject(newProject);
  413. p.execute();
  414. }
  415. getProject().copyInheritedProperties(newProject);
  416. }
  417. /**
  418. * Add the references explicitly defined as nested elements to the
  419. * new project. Also copy over all references that don't override
  420. * existing references in the new project if inheritrefs has been
  421. * requested.
  422. * @throws BuildException if a reference does not have a refid
  423. */
  424. private void addReferences() throws BuildException {
  425. Hashtable thisReferences
  426. = (Hashtable) getProject().getReferences().clone();
  427. Hashtable newReferences = newProject.getReferences();
  428. Enumeration e;
  429. if (references.size() > 0) {
  430. for (e = references.elements(); e.hasMoreElements();) {
  431. Reference ref = (Reference) e.nextElement();
  432. String refid = ref.getRefId();
  433. if (refid == null) {
  434. throw new BuildException("the refid attribute is required"
  435. + " for reference elements");
  436. }
  437. if (!thisReferences.containsKey(refid)) {
  438. log("Parent project doesn't contain any reference '"
  439. + refid + "'",
  440. Project.MSG_WARN);
  441. continue;
  442. }
  443. thisReferences.remove(refid);
  444. String toRefid = ref.getToRefid();
  445. if (toRefid == null) {
  446. toRefid = refid;
  447. }
  448. copyReference(refid, toRefid);
  449. }
  450. }
  451. // Now add all references that are not defined in the
  452. // subproject, if inheritRefs is true
  453. if (inheritRefs) {
  454. for (e = thisReferences.keys(); e.hasMoreElements();) {
  455. String key = (String) e.nextElement();
  456. if (newReferences.containsKey(key)) {
  457. continue;
  458. }
  459. copyReference(key, key);
  460. }
  461. }
  462. }
  463. /**
  464. * Try to clone and reconfigure the object referenced by oldkey in
  465. * the parent project and add it to the new project with the key
  466. * newkey.
  467. *
  468. * <p>If we cannot clone it, copy the referenced object itself and
  469. * keep our fingers crossed.</p>
  470. */
  471. private void copyReference(String oldKey, String newKey) {
  472. Object orig = getProject().getReference(oldKey);
  473. if (orig == null) {
  474. log("No object referenced by " + oldKey + ". Can't copy to "
  475. + newKey,
  476. Project.MSG_WARN);
  477. return;
  478. }
  479. Class c = orig.getClass();
  480. Object copy = orig;
  481. try {
  482. Method cloneM = c.getMethod("clone", new Class[0]);
  483. if (cloneM != null) {
  484. copy = cloneM.invoke(orig, new Object[0]);
  485. log("Adding clone of reference " + oldKey, Project.MSG_DEBUG);
  486. }
  487. } catch (Exception e) {
  488. // not Clonable
  489. }
  490. if (copy instanceof ProjectComponent) {
  491. ((ProjectComponent) copy).setProject(newProject);
  492. } else {
  493. try {
  494. Method setProjectM =
  495. c.getMethod("setProject", new Class[] {Project.class});
  496. if (setProjectM != null) {
  497. setProjectM.invoke(copy, new Object[] {newProject});
  498. }
  499. } catch (NoSuchMethodException e) {
  500. // ignore this if the class being referenced does not have
  501. // a set project method.
  502. } catch (Exception e2) {
  503. String msg = "Error setting new project instance for "
  504. + "reference with id " + oldKey;
  505. throw new BuildException(msg, e2, getLocation());
  506. }
  507. }
  508. newProject.addReference(newKey, copy);
  509. }
  510. /**
  511. * Copies all properties from the given table to the new project -
  512. * omitting those that have already been set in the new project as
  513. * well as properties named basedir or ant.file.
  514. * @param props properties to copy to the new project
  515. * @since Ant 1.6
  516. */
  517. private void addAlmostAll(Hashtable props) {
  518. Enumeration e = props.keys();
  519. while (e.hasMoreElements()) {
  520. String key = e.nextElement().toString();
  521. if ("basedir".equals(key) || "ant.file".equals(key)) {
  522. // basedir and ant.file get special treatment in execute()
  523. continue;
  524. }
  525. String value = props.get(key).toString();
  526. // don't re-set user properties, avoid the warning message
  527. if (newProject.getProperty(key) == null) {
  528. // no user property
  529. newProject.setNewProperty(key, value);
  530. }
  531. }
  532. }
  533. /**
  534. * The directory to use as a base directory for the new Ant project.
  535. * Defaults to the current project's basedir, unless inheritall
  536. * has been set to false, in which case it doesn't have a default
  537. * value. This will override the basedir setting of the called project.
  538. * @param d new directory
  539. */
  540. public void setDir(File d) {
  541. this.dir = d;
  542. }
  543. /**
  544. * The build file to use.
  545. * Defaults to "build.xml". This file is expected to be a filename relative
  546. * to the dir attribute given.
  547. * @param s build file to use
  548. */
  549. public void setAntfile(String s) {
  550. // @note: it is a string and not a file to handle relative/absolute
  551. // otherwise a relative file will be resolved based on the current
  552. // basedir.
  553. this.antFile = s;
  554. }
  555. /**
  556. * The target of the new Ant project to execute.
  557. * Defaults to the new project's default target.
  558. * @param s target to invoke
  559. */
  560. public void setTarget(String s) {
  561. if (s.equals("")) {
  562. throw new BuildException("target attribute must not be empty");
  563. }
  564. this.target = s;
  565. }
  566. /**
  567. * Filename to write the output to.
  568. * This is relative to the value of the dir attribute
  569. * if it has been set or to the base directory of the
  570. * current project otherwise.
  571. * @param s file to which the output should go to
  572. */
  573. public void setOutput(String s) {
  574. this.output = s;
  575. }
  576. /**
  577. * Property to pass to the new project.
  578. * The property is passed as a 'user property'
  579. * @return new property created
  580. */
  581. public Property createProperty() {
  582. if (newProject == null) {
  583. reinit();
  584. }
  585. Property p = new Property(true, getProject());
  586. p.setProject(newProject);
  587. p.setTaskName("property");
  588. properties.addElement(p);
  589. return p;
  590. }
  591. /**
  592. * Reference element identifying a data type to carry
  593. * over to the new project.
  594. * @param r reference to add
  595. */
  596. public void addReference(Reference r) {
  597. references.addElement(r);
  598. }
  599. /**
  600. * Set of properties to pass to the new project.
  601. *
  602. * @param ps property set to add
  603. * @since Ant 1.6
  604. */
  605. public void addPropertyset(PropertySet ps) {
  606. propertySets.addElement(ps);
  607. }
  608. /**
  609. * Helper class that implements the nested &lt;reference&gt;
  610. * element of &lt;ant&gt; and &lt;antcall&gt;.
  611. */
  612. public static class Reference
  613. extends org.apache.tools.ant.types.Reference {
  614. /** Creates a reference to be configured by Ant */
  615. public Reference() {
  616. super();
  617. }
  618. private String targetid = null;
  619. /**
  620. * Set the id that this reference to be stored under in the
  621. * new project.
  622. *
  623. * @param targetid the id under which this reference will be passed to
  624. * the new project */
  625. public void setToRefid(String targetid) {
  626. this.targetid = targetid;
  627. }
  628. /**
  629. * Get the id under which this reference will be stored in the new
  630. * project
  631. *
  632. * @return the id of the reference in the new project.
  633. */
  634. public String getToRefid() {
  635. return targetid;
  636. }
  637. }
  638. }