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

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