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

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