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

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